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:
When I put the CFormView derived view in a ATL COM object, I will need to serialize the CRuntimeClass Object.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
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.
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.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() ); }
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?
Below is some of the selected code: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 };
The End.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; }




Reply With Quote