介面的限制: COM要求客戶和伺服器高度的分離,這已經由介面實現了,但是現在問題是,介面方法只提供了有限的幾種資料類型. 如果介面是基於IDispatch的,我們的選擇更加有限.請記住這些限制, C++對象只在下面幾種情況下可以傳遞: 1. 客戶和服務都是VC編譯的; 2. 他們必須有共同的對象的定義,比如相同的標頭檔; 3. 通過傳遞C++對象簡化應用的設計; 4. 在分布式環境中,需要注意你的COM必須具備遠程啟用, 本地/遠程透明性, 安全性登方面的特性. 下面是一個例子: 1. 產生一個ATL DLL伺服器 2. 添加一個繼承於CObject的類. 3. 在類的標頭檔中加上DECLARE_SERIAL 4. 在類的CPP檔案中加上IMPLEMENT_SERIAL 5. 重載Serialize方法 // 你的CSimpleObj類看起來這樣子的 class CSimpleObj : public CObject{ DECLARE_SERIAL( CSimpleObj )public: // constructor and destructor CSimpleObj(); virtual ~CSimpleObj(); // 設定內部的字串 void SetString( CString csData ); // 用於序列化你的資料 virtual void Serialize(CArchive& ar); // 顯示資料 void Show();private: CString m_strData;//這裡定義一個字串對象 };// Write this object to an archivevoid CSimpleObj::Serialize(CArchive& ar){ CObject::Serialize( ar ); if (ar.IsLoading()) { // extract data from archive ar >> m_strData; } else { // store data into archive ar << m_strData; }}// Method to display data in this objectvoid CSimpleObj::Show(){ AfxMessageBox(m_strData);}// save a string in data membervoid CSimpleObj::SetString(CString csData){ m_strData = csData;} 6. 下一步我們通過CArchive存取對象,這裡用了另外一個類CBlob. class CBlob{public: CBlob() {}; virtual ~CBlob() {}; // 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:};// Extract data from a CObject and use it to create a SAFEARRAY.SAFEARRAY* CBlob::Load( CObject *pObj){ CMemFile memfile; // memory file // define the flag that tells the archive whether it should // load or store long lMode = CArchive::store | CArchive::bNoFlushOnDelete; // create the archive using the memory file CArchive ar(&memfile, lMode ); // m_pDocument is not used ar.m_pDocument = NULL; // serialize the object into the archive ar.WriteObject(pObj); // close the archive -- the data is now stored in memfile ar.Close(); // get the length (in bytes) of the memory file long llen = memfile.GetLength(); // detach the buffer and close the file unsigned char *pMemData = memfile.Detach(); // set up safearray SAFEARRAY *psa; // create a safe array to store the stream data psa = SafeArrayCreateVector( VT_UI1, 0, llen ); // pointers to byte arrays unsigned char *pData = NULL; // get a pointer to the safe array. Locks the array. SafeArrayAccessData( psa, (void**)&pData ); // copy the memory file into the safearray memcpy( pData, pMemData, llen ); // clean up buffer delete pMemData; // unlock access to safearray SafeArrayUnaccessData(psa); // return a pointer to a SAFEARRAY allocated here return psa;}// Re-create an object from a SAFEARRAYBOOL CBlob::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;}這裡用了SAFEARRAY ,是比較適合我們的目的的,它能夠容納複雜的多維陣列,這裡我們只用了一個很簡單的數組. 但是對於SAFEARRAY 有一個問題,MIDL 不認識這種類型,最簡單的辦法是用VARIANT 類型. 下面幾步: 1. 產生一個COM介面, 2. 產生一個SAFEARRAY對象 3. 在IDL檔案裡 定義2個方法: [helpstring("method SetArray")] HRESULT SetArray([in]SAFEARRAY unsigned char) pData); [helpstring("method GetArray")] HRESULT GetArray([out/*,retval*/]SAFEARRAY(unsigned char) *pData); 4. 產生一個基於MFC的用戶端進行測試 IDL檔案看起來這樣的: interface IBolbData : IUnknown { [helpstring("method SetArray")] HRESULT SetArray([in]SAFEARRAY (unsigned char) pData); [helpstring("method GetArray")] HRESULT GetArray([out/*,retval*/] SAFEARRAY(unsigned char) *pData); }; // Sets object. STDMETHODIMP CBolbData::SetArray(SAFEARRAY *pData) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) // create a dummy pointer of CSimpleObj CSimpleObj *dummy=NULL; // create blob obect to expand/deserialize CBlob blob; // Init dummy object using safe array through this function blob.Expand( (CObject*&)dummy, pData ); dummy->Show(); // Call show function to test the object. delete dummy; // Delete the pointer.
return S_OK; } // Creates Object and sends to client. STDMETHODIMP CBolbData::GetArray(SAFEARRAY **pData) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) // create object to send to server CSimpleObj *pMyOb = new CSimpleObj(); // set the string data pMyOb->SetString( "A SAFEARRAY from the server!" ); // create blob to serialize object CBlob blob; // load the object into the blob *pData = blob.Load( pMyOb ); // delete the pMyOb pointer delete pMyOb; return S_OK; } 最後,我們做一個帶有2個按鈕的對話方塊應用程式, 2個按鈕的回應程式法是: void CClientDlg::OnOK() { // create COM smart pointer from CLSID string try { IBolbDataPtr pI( "Server.BolbData.1" ); SAFEARRAY *psa ; // Get the safearray from the server pI->GetArray( &psa ); // create a pointer to an object CSimpleObj *dummy=NULL; // blob object to expand CBlob blob; // use the blob to expand the safearray into an object blob.Expand( (CObject *&)dummy, psa ); // call a method on the object to test it dummy->Show(); // delete the object delete dummy; } // Handle any COM exceptions from smart pointers catch (_com_error e) { // display the message string from the error AfxMessageBox( e.ErrorMessage() ); } } void CClientDlg::OnLoad() { try { // create smart pointer from CLSID string IBolbDataPtr pI( "Server.BolbData.1" ); SAFEARRAY *psa ; // create object to send to server CSimpleObj *pMyOb = new CSimpleObj(); // set the string data pMyOb->SetString( "The client sent a SAFEARRAY!" ); // create blob to serialize object CBlob blob; // load the object into the blob psa = blob.Load( pMyOb ); // delete the object delete pMyOb; pI->SetArray( psa ); } // Handle any COM exceptions from smart pointers catch (_com_error e) { // display the message string from the error AfxMessageBox( e.ErrorMessage() ); } } 這個例子裡涵蓋的內容比較多,包括了如何使用序列化,如何使用安全陣列,如何通過介面傳遞C++對象. 作者原貼在這裡: http://www.codeguru.com/Cpp/COM-Tech/atl/atl/article.php/c3587/ |