程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 向ATL DLL中傳遞C++對象

向ATL DLL中傳遞C++對象

編輯:關於VC++

簡介

幾個星期以前,我拼命的尋找一個能夠通過COM接口傳遞C++對象的例子,但是,沒有找到.這就是我發表這篇文章的原因。

向ATL的DLL中傳遞一個C++對象參數並不是非常之難,但是,當然也會有點難度,也很有趣。

在開始一個工程以前,首先你得確信客戶機和服務器組件都是適應C++的程序,其次,你必須知道怎樣設置你的客戶機和服務器。

接口的局限性

COM技術要求客戶機和服務器高度的分離,這是通過接口實現的,但是問題出在:接口的方法中只提供了有限個參數數據類型,如果這個接口是基於IDispatch的,參數類型的可選范圍就更加受到限制了,由於這些局限性,C++對象只有在滿足以下條件時才能夠傳遞:

客戶機和服務器都是由VC++編寫。

它們必須共享對象的定義(比如 頭文件)。

傳遞應用程序設計的簡單的對象。

你的應用程序可能需要運行在一個分布式環境下。你希望COM的遠程活動,本地/遠程活動是透明的,安全的。

我建議,在開始工作之前,先順序的看一下各個標題,現在,我列出實例,並作以下事情:

創建一個ATL DLL服務器,

添加一個MFC類,從CObject類派生,

在類的頭部使用 DECLARE_SERIAL 宏,

在類的中間使用 IMPLEMENT_SERI 宏,

覆蓋Serialize() 方法,// 你的 CSimpleObj 類應該像這樣:
class CSimpleObj : public CObject
{
     DECLARE_SERIAL( CSimpleObj )
public:
   // 構造函數和析構函數
     CSimpleObj();
     virtual ~CSimpleObj();
   // 設置內部字符串數據
     void SetString( CString csData );
   // 用來向存檔文件串行輸入數據(序列化)
     virtual void Serialize(CArchive& ar);
   // 現實字符串數據
     void Show();
private:
     CString m_strData;// 內部字符串數據
};
// 把這個數據對象寫入到文檔中
void CSimpleObj::Serialize(CArchive& ar)
{
     CObject::Serialize( ar );
   if (ar.IsLoading())
   {
     // 從檔案文件提取數據
       ar >> m_strData;
   }
   else
   {
     // 把數據存入檔案文件
       ar << m_strData;
   }
}
// 顯示對象數據的方法
void CSimpleObj::Show()
{
     AfxMessageBox(m_strData);
}
//把字符串數據保存到一個變量中
void CSimpleObj::SetString(CString csData)
{
     m_strData = csData;
}

現在,下一步就是用一個CArchive對象來進行序列化和反序列化(載入和存儲對象),我用了一個叫CBlob的新類來實現的

class CBlob
{
public:
     CBlob() {};
     virtual ~CBlob() {};
   // 從一個 CObject對象中提取數據並載入到一個 SAFEARRAY對象中.
     SAFEARRAY* Load( CObject *pObj );
   // 重新創建一個SAFEARRAY對象
     BOOL Expand( CObject * &pObj, SAFEARRAY *pVar );
private:
};
// 從一個 CObject對象中提取數據並用它構建一個 SAFEARRAY對象.
SAFEARRAY* CBlob::Load( CObject *pObj)
{
     CMemFile memfile; // 內存文件
   // 定義一個用來標記檔案文件是讀取還是存儲的標志
     long lMode = CArchive::store | CArchive::bNoFlushOndelete;
   // 用內存文件創建檔案文件
     CArchive ar(&memfile, lMode );
   // m_pDocument 不使用
     ar.m_pDocument = NULL;
   // 序列化對象到檔案文件中
     ar.WriteObject(pObj);
   // 關閉檔案文件--現在,數據在內存文件中
     ar.Close();
   // 取得內存文件的長度(以字節為單位)
     long llen = memfile.GetLength();
   // 釋放緩沖區 關閉文件
     unsigned char *pMemData = memfile.Detach();
   // 設定safearray
     SAFEARRAY *psa;
   // 創建safearray對象存取流數據
     psa = SafeArrayCreateVector( VT_UI1, 0, llen );
   // 指向字節數組的指針
     unsigned char *pData = NULL;
   // 取得一個 safe array的指針. 鎖定數組.
     SafeArrayAccessData( psa, (void**)&pData );
   // 拷貝內存文件到 safearray
     memcpy( pData, pMemData, llen );
   // 清理緩沖區
     delete pMemData;
   // 鎖定對 safearray的訪問
     SafeArrayUnaccessData(psa);
   // 返回一個在這分配的SAFEARRAY的指針
     return psa;
}
// 重新創建一個SAFEARRAY對象
BOOL CBlob::Expand(CObject * &rpObj, SAFEARRAY *psa)
{
     CMemFile memfile; // 反序列化的內存文件
     long lLength; // 字節數
     char *pBuffer; // 緩沖區指針
   // 鎖定數組數據的訪問
     SafeArrayAccessData( psa, (void**)&pBuffer );
   // 取得數組中元素個數. 是字節數
     lLength = psa->rgsabound->cElements;
   // 連接緩沖區到內存文件
     memfile.Attach((unsigned char*)pBuffer, lLength);
   // 從緩沖區頭部開始
     memfile.SeekToBegin();
   // 創建一個連接到內存文件上的檔案文件
     CArchive ar(&memfile, CArchive::load | CArchive::bNoFlushOndelete);
   // 不使用文檔指針
     ar.m_pDocument = NULL;
   // 填充對象 取得指針
     rpObj = ar.ReadObject(0);
   // 關閉檔案文件
     ar.Close();
   // 注意: 當SAFEARRAY被毀壞時 pBuffer 被釋放
   // 釋放緩沖區 關閉文件
     pBuffer = (char*) memfile.Detach();
   // 釋放safearray 緩沖區
     SafeArrayUnaccessData( psa );
     return TRUE;
}

在這裡 ,我使用SAFEARRAY是因為它對我們來說是最好的選擇,它可以包含一些復雜的多維數組,但是,這個例子我們只使用了非常簡單的數組,SAFEARRAY數據,有一個問題:MIDL認不出這個數據類型,在下一篇文章中我將講述最簡單的方法:使用 VARIANT數據類型。

下一步如下:

創建一個COM接口,

創建一個SAFEARRAY對象,

在IDL文件中定義:[helpstring("method SetArray")]
HRESULT SetArray([in]SAFEARRAY (unsigned char) pData);[helpstring("method GetArray")]
HRESULT GetArray([out/*,retval*/]SAFEARRAY(unsigned char) *pData);

創建一個基於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);
};
// 設定對象
STDMETHODIMP CBolbData::SetArray(SAFEARRAY *pData)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState())
    // 創建CSimpleObj的亞元指針
      CSimpleObj *dummy=NULL;
  // 創建 blob 對象 用來填充、反序列化
    CBlob blob;
  // 使用 safearray 創建亞元對象
    blob.Expand( (CObject*&)dummy, pData );
    dummy->Show(); // 調用顯示函數測試對象
    delete dummy; //刪除指針
    return S_OK;
}
// 創建對象 並發送給客戶機.
STDMETHODIMP CBolbData::GetArray(SAFEARRAY **pData)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState())
    // 創建對象並發送給服務器
      CSimpleObj *pMyOb = new CSimpleObj();
  //設定字符串數據
    pMyOb->SetString( "A SAFEARRAY from the server!" );
  // 創建blob來序列化對象
    CBlob blob;
  // 將對象載入blob
    *pData = blob.Load( pMyOb );
  // 刪除pMyOb指針
    delete pMyOb;
    return S_OK;
}

最後,寫一個有兩個按鈕的基於對話框的 MFC 應用程序 並添加如下代碼:

void CClientDlg::OnOK()
{
  // 從CLSID串創建COM智能指針
  try
  {
      IBolbDataPtr pI( "Server.BolbData.1" );
      SAFEARRAY *psa ;
    // 從服務器取得 safearray
      pI->GetArray( &psa );
    // 創建指針
      CSimpleObj *dummy=NULL;
    // blob 對象
      CBlob blob;
    //使用blob 擴展 safearray 到一個對象裡
      blob.Expand( (CObject *&)dummy, psa );
    //通過調用一個對象的方法來測試它
      dummy->Show();
    // 刪除對象
      delete dummy;
  }
  // 通過智能指針處理任意 COM 異常
  catch (_com_error e)
  {
    // 顯示錯誤信息
      AfxMessageBox( e.ErrorMessage() );
  }
}
void CClientDlg::OnLoad()
{
  try
  {
    // 從CLSID 串創建智能指針
      IBolbDataPtr pI( "Server.BolbData.1" );
      SAFEARRAY *psa ;
    // 創建送給服務器的對象
      CSimpleObj *pMyOb = new CSimpleObj();
    // 設置字符串數據
      pMyOb->SetString( "The client sent a SAFEARRAY!" );
    // 創建 blob 用來序列化對象
      CBlob blob;
    // 將對象載入到 blob
      psa = blob.Load( pMyOb );
    //刪除對象
      delete pMyOb;
      pI->SetArray( psa );
  }
  catch (_com_error e)
  {
    // 顯示錯誤信息
      AfxMessageBox( e.ErrorMessage() );
  }
}

總結

這篇文章包含了很多的主題:例如 怎樣使用序列化,怎樣使用 SAFEARRAY,和怎樣通過接口傳遞C++對象。我要感謝William Rubin,他的文章對我幫助很大,我曾經計劃把這個主題解釋的更詳細,但由於時間不足我無法完成,然而我會不斷的更新這篇文章,在這期間,請不用客氣的跟我聯系。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved