Hi CodeGuru,

I need to serialize the MFC CRuntimeClass Object between an MFC application and a ATL COM object.

Any comments or suggestions are welcome.
Thanks,
Chris

Sorry for the long email.

I have some legacy MFC applications that use CFormView derived views. I would like to put the classes and views into a ATL COM object so they may be shared between host applications. The host application would switch between views. To do this I need to get a pointer to the CRuntimeClass data struct.

Using a MFC DLL:

Code:
hDLL = GetModuleHandle("MasTest.dll");
hEXE = AfxGetResourceHandle();
AfxSetResourceHandle((HINSTANCE) hDLL);

CRuntimeClass *pRuntimeClass;
pRuntimeClass = new CRuntimeClass(); 
pRuntimeClass = RUNTIME_CLASS(CMasTestView); 

// m_nMasTestView = m_wndHorSplitter.AddView(1, 0, RUNTIME_CLASS(CMasTestView), pContext);
m_nMasTestView = m_wndHorSplitter.AddView(1, 0, pRuntimeClass, pContext);
AfxSetResourceHandle(hEXE);
delete pRuntimeClass
When I put the CFormView derived view in a ATL COM object, I will need to serialize the CRuntimeClass Object.

I'm using the framework detailed in the article "Passing C++ Object in ATL DLL" by Uttam Kumar.
http://www.codeguru.com/cpp/com-tech...cle.php/c3587/

COM interfaces only support a limited number of data types. The framework basically allows the user to serialize simple (CString) and complex (CPtrArray) objects through a COM interface using the serialization support of CObject and SAFEARRAY.

Below is a sample of how a CString object is received from the Server.
Code:
try
{
    CString     strTemp; 
    SAFEARRAY   *psa ;


    IBolbDataPtr pI("Server.BolbData.1");

    // Get the safearray from the server
    pI->GetArray(&psa);

    // create a pointer to an object
    CSimpleObj *pSimpleObj=NULL;

    // blob object to expand
    CBlob blob;

    // use the blob to expand the safearray into an object
    blob.Expand((CObject *&)pSimpleObj, psa);
    
    // call a method on the object to test it
    strTemp = pSimpleObj->GetString();

    // delete the object
    delete pSimpleObj;
}
catch (_com_error e)
{
    AfxMessageBox( e.ErrorMessage() );
}
Below are the selected code segments that I have implemented to serialize the CRuntimeClass object. Note that I have created a class called CMasMfcRuntime that contains the CRuntimeClass data structures. The implementation fails when I attempt to serialize m_pfnCreateObject and m_pfnGetBaseClass.

Looking at AFX.H (where CRuntimeClass is defined) ... There seems to be a Store() and Load() function defined in the CRuntimeClass struct (See below). Could I use these functions to support serialization of the CRuntimeClass object?

Code:
struct CRuntimeClass
{
// Attributes
    LPCSTR m_lpszClassName;
    int m_nObjectSize;
    UINT m_wSchema; // schema number of the loaded class
    CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
#ifdef _AFXDLL
    CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
    CRuntimeClass* m_pBaseClass;
#endif

// Operations
    CObject* CreateObject();
    BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;

// Implementation
    void Store(CArchive& ar) const;
    static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);

    // CRuntimeClass objects linked together in simple list
    CRuntimeClass* m_pNextClass;       // linked list of registered classes
};
Below is some of the selected code:

Code:
/////////////////////////////////////////////////////////////////////
Inside the Host Application. 

CMasMfcRuntime *m_pMasMfcRuntime = 0x00; 

// create smart pointer from CLSID string
IDataPerformancePtr pDataPerformance("MasServer.DataPerformance.1");

// create object to send to server
CMasCpContext *pCMasCpContext = new CMasCpContext();

// Calling GetMasMfcRuntimeObject() will return the serialized 
// CRuntimeClass struct from the server.  
pDataPerformance->GetMasMfcRuntimeObject(&psa);

// Use the pMasCpContext object to expand the 
// safearray into an object
oMasBlob.Expand((CObject *&)pMasCpContext, psa);

// Get the CRuntimeClass data struct from the pMasCpContext obejct. 
m_pMasMfcRuntime = pMasCpContext->GetMasMfcRuntime(); 

// delete the object
delete pMasCpContext;




/////////////////////////////////////////////////////////////////////
ATL COM Object
CMasMfcRuntime Interface

class CMasMfcRuntime : public CObject
{
public:

    // Standard Constructor and Destructor
    CMasMfcRuntime();
    ~CMasMfcRuntime();

    void    Serialize(CArchive& ar);

    // Attributes
    LPCSTR m_lpszClassName;
    int m_nObjectSize;
    UINT m_wSchema; // schema number of the loaded class
    CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class

#ifdef _AFXDLL
    CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
    CRuntimeClass* m_pBaseClass;
#endif

// Operations
//  CObject* CreateObject();
//  BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;

// Implementation
//  void Store(CArchive& ar) const;
//  static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);

    // CRuntimeClass objects linked together in simple list
    CRuntimeClass* m_pNextClass;       // linked list of registered classes
};


CMasMfcRuntime Implementation

void CMasMfcRuntime::Serialize(CArchive& ar)
{
    CObject::Serialize( ar );
    CString csTemp; 

    if (ar.IsLoading())
    {
        // extract data from archive
        //ar >> csTemp;
        //ar >> m_nObjectSize;
    }
    else
    {
        csTemp = m_lpszClassName;
        
        // store data into archive
        ar << csTemp;
        ar << m_nObjectSize;
        //ar << m_pfnCreateObject;  // Fails
        //ar << m_pfnGetBaseClass;  // Fails
        //ar << m_pNextClass;
    }
}




/////////////////////////////////////////////////////////////////////
ATL COM Object
CMasCpContext Interface

class CMasCpContext : public CObject
{
    DECLARE_SERIAL( CMasCpContext )
public:
    // constructor and destructor
    CMasCpContext();
    virtual ~CMasCpContext();

    // Used to serialize data into an archive
    virtual void Serialize(CArchive& ar);

    CMasMfcRuntime   *GetMasMfcRuntime(void) {return m_pMasMfcRuntime;}
    void            SetMasMfcRuntime(CRuntimeClass *pData); 

protected:
    // attributes

    CMasMfcRuntime *m_pMasMfcRuntime; 

};

CMasCpContext Implementation

void CMasCpContext::Serialize(CArchive& ar)
{
    int nStatus = 0; 
    int nPtrArrayIndex = 0; 

    CObject::Serialize(ar);

    if (ar.IsLoading()) 
    {
        if (m_pMasMfcRuntime == 0x00) 
        { 
            m_pMasMfcRuntime = new CMasMfcRuntime();
        }            

        // Serialize it
        m_pMasMfcRuntime->Serialize(ar);
    } 
    else
    { 
        // no nothing here 
        if (m_pMasMfcRuntime)
        { 
            m_pMasMfcRuntime->Serialize(ar);
        }
    }
}




/////////////////////////////////////////////////////////////////////
ATL COM Object
CMasBlob Interface

class CMasBlob
{
public:
    CMasBlob() {};
    virtual ~CMasBlob() {};

    // Extract data from a CObject and load it into a SAFEARRAY.
    SAFEARRAY* Load( CObject *pObj );
    
    // Re-create an object from a SAFEARRAY
    BOOL Expand( CObject * &pObj, SAFEARRAY *pVar );
    
private:
};


CMasBlob Implementation

BOOL CMasBlob::Expand(CObject * &rpObj, SAFEARRAY *psa)
{
    CMemFile memfile;       // memory file for de-serailze
    long lLength;           // number of bytes
    char *pBuffer;          // buffer pointer

    // lock access to array data
    SafeArrayAccessData( psa, (void**)&pBuffer  );

    // Get number of elements in array. This is the number of bytes
    lLength = psa->rgsabound->cElements;

    // Attach the buffer to the memory file
    memfile.Attach((unsigned char*)pBuffer, lLength);

    // Start at beginning of buffer
    memfile.SeekToBegin();

    // Create an archive with the attached memory file
    CArchive ar(&memfile, CArchive::load | CArchive::bNoFlushOnDelete);

    // Document pointer is not used
    ar.m_pDocument = NULL;

    // Inflate the object and get the pointer
    rpObj = ar.ReadObject(0);

    // close the archive
    ar.Close();

    // Note: pBuffer is freed when the SAFEARRAY is destroyed
    // Detach the buffer and close the file
    pBuffer = (char*) memfile.Detach();

    // release the safearray buffer
    SafeArrayUnaccessData( psa );

    return TRUE;
}
The End.