首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

ATL DLL 跨邊界傳遞 C++ 物件:序列化 + SAFEARRAY 方案完整實作

     編輯:關於VC++

COM 介面嚴格限制參數類型 (僅支援 Automation 相容類型),導致無法直接跨 DLL 邊界傳遞自訂 C++ 類別物件。本文示範一種經典方案:利用 MFC 序列化機制將物件扁平化為位元組陣列,封裝於 SAFEARRAY 中跨介面傳遞,對端再反序列化還原物件。

核心前提條件

  • Client 與 Server 均為 VC++ 開發。
  • 雙方共享類別定義 (Header 檔)。
  • 物件需繼承 CObject 並實作 DECLARE_SERIAL / IMPLEMENT_SERIAL 及 Serialize(CArchive&)。

1. 可序列化的業務物件範例

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;
};

IMPLEMENT_SERIAL(CSimpleObj, CObject, 1)

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; }

2. 序列化輔助類:CBlob (物件 ↔ SAFEARRAY)

封裝 CMemFile + CArchive 完成記憶體序列化,並與 SAFEARRAY 互轉:

class CBlob
{
public:
    // 物件 → SAFEARRAY (序列化)
    SAFEARRAY* Load(CObject* pObj)
    {
        CMemFile memFile;
        CArchive ar(&memFile, CArchive::store | CArchive::bNoFlushOnDelete);
        ar.m_pDocument = NULL;
        ar.WriteObject(pObj);
        ar.Close();

        DWORD len = memFile.GetLength();
        BYTE* pData = memFile.Detach();

        SAFEARRAY* psa = SafeArrayCreateVector(VT_UI1, 0, len);
        BYTE* pSafeData;
        SafeArrayAccessData(psa, (void**)&pSafeData);
        memcpy(pSafeData, pData, len);
        delete[] pData;
        SafeArrayUnaccessData(psa);
        return psa;
    }

    // SAFEARRAY → 物件 (反序列化)
    BOOL Expand(CObject*& rpObj, SAFEARRAY* psa)
    {
        CMemFile memFile;
        BYTE* pData;
        SafeArrayAccessData(psa, (void**)&pData);
        LONG len = psa->rgsabound[0].cElements;
        memFile.Attach(pData, len);
        memFile.SeekToBegin();

        CArchive ar(&memFile, CArchive::load | CArchive::bNoFlushOnDelete);
        ar.m_pDocument = NULL;
        rpObj = ar.ReadObject(RUNTIME_CLASS(CSimpleObj)); // 需指定 RUNTIME_CLASS
        ar.Close();

        pData = (BYTE*)memFile.Detach(); // 避免雙重釋放
        SafeArrayUnaccessData(psa);
        return TRUE;
    }
};

3. COM 介面定義 (IDL)

interface IBlobData : IUnknown
{
    [helpstring("method SetArray")] HRESULT SetArray([in] SAFEARRAY(unsigned char) pData);
    [helpstring("method GetArray")] HRESULT GetArray([out] SAFEARRAY(unsigned char)* pData);
};

注意:MIDL 不直接支援 SAFEARRAY(unsigned char) 語法,實際 .idl 中需寫為 SAFEARRAY(BYTE) 或使用 VARIANT 封裝 (下文提及)。

4. ATL 伺服器端實作

STDMETHODIMP CBlobData::SetArray(SAFEARRAY* pData)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    CSimpleObj* pObj = NULL;
    CBlob blob;
    blob.Expand(pObj, pData); // 還原物件
    if (pObj) { pObj->Show(); delete pObj; }
    return S_OK;
}

STDMETHODIMP CBlobData::GetArray(SAFEARRAY** ppData)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    CSimpleObj* pObj = new CSimpleObj();
    pObj->SetString(_T("來自 Server 的 SAFEARRAY!"));
    CBlob blob;
    *ppData = blob.Load(pObj); // 序列化為 SAFEARRAY
    delete pObj;
    return S_OK;
}

5. MFC Client 呼叫範例 (智慧指標)

void CClientDlg::OnGetFromServer() // 取得物件
{
    try {
        IBlobDataPtr pSrv(_T("Server.BlobData.1"));
        SAFEARRAY* psa = NULL;
        pSrv->GetArray(&psa);
        CSimpleObj* pObj = NULL;
        CBlob blob;
        blob.Expand(pObj, psa);
        if (pObj) { pObj->Show(); delete pObj; }
    } catch (_com_error& e) { AfxMessageBox(e.ErrorMessage()); }
}

void CClientDlg::OnSendToServer() // 送出物件
{
    try {
        IBlobDataPtr pSrv(_T("Server.BlobData.1"));
        CSimpleObj* pObj = new CSimpleObj();
        pObj->SetString(_T("Client 送出的 SAFEARRAY!"));
        CBlob blob;
        SAFEARRAY* psa = blob.Load(pObj);
        delete pObj;
        pSrv->SetArray(psa);
    } catch (_com_error& e) { AfxMessageBox(e.ErrorMessage()); }
}

總結與延伸

  • 此法繞過 COM 類型系統限制,實現同質 C++ 環境下的物件傳遞。
  • 缺點:需共享 Header、不支援異構語言、SAFEARRAY 在 MIDL 需特殊處理。
  • 改進方向:改用 VARIANT 封裝 SAFEARRAY (支援 VT_ARRAY | VT_UI1),提升 IDL 相容性;或採用自定義封送器。

完整專案碼可下載參考:原文配套源碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved