Serialization of the CRuntimeClass struct ...
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.