通过 IPC 跨进程传递对 COM 对象的引用?

Posted

技术标签:

【中文标题】通过 IPC 跨进程传递对 COM 对象的引用?【英文标题】:Passing a reference to a COM object accross processes via IPC? 【发布时间】:2018-03-20 08:52:05 【问题描述】:

我有一个对 COM 对象的引用,并且想将该引用传递给另一个进程。 因此,我需要一些方法来序列化有关此引用的信息,以便我在其他进程中再次恢复它。

有没有办法对任意类型的 COM 对象执行此操作?

我对 COM 了解不多;据我了解,某些类型的对象可能有 Monikers(例如,COM 引用,如 Excel.Workbook 可能通过他们的 .FullName 属性恢复,这似乎被用作名字对象),但到目前为止我还没有发现对于任何类型的 COM 对象是否都可能发生这样的事情

【问题讨论】:

Moniker 就像 url,你可以在里面传递任何东西,只要你的代码理解它的含义。否则,通常传递引用可能很复杂,特别是如果您是 COM 新手,请检查:blogs.msdn.microsoft.com/oldnewthing/20151020-00/?p=91321 【参考方案1】:

这是一种方法:

    在服务器进程中,使用CoMarshalInterface来封送 流的接口。将写入流的字节转换为 base-64 字符串(或任何其他编码)。将编码的字符串传递给您的 客户。 在客户端进程中,从您的服务器接收字符串。解码 将字符串转换为字节。将这些字节包装在一个流中。解组自 流。

服务器端:

#include <windows.h>
#include <comdef.h>
#include <shlwapi.h>
#include <vector>
// link: crypt32.lib (for CryptBinaryToStringW)
// link: shlwapi.lib (for SHCreateMemStream)

/// <summary>
/// Gets a token that can be used to unmarshal an interface in another process.
/// </summary>
HRESULT GetInterfaceToken(LPUNKNOWN pUnk, REFIID riid, LPBSTR pbstrOut)

    // validate output parameters
    if (pbstrOut == nullptr)
        return E_POINTER;

    // set default values for output parameters
    *pbstrOut = nullptr;

    // validate input parameters
    if (pUnk == nullptr)
        return E_INVALIDARG;

    // create a stream
    IStreamPtr stream;
    stream.Attach(SHCreateMemStream(nullptr, 0));
    if (!stream)
        return E_FAIL;

    // marshal interface into stream
    auto hr = CoMarshalInterface(stream, riid, pUnk, MSHCTX_LOCAL, nullptr, MSHLFLAGS_NORMAL);
    if (FAILED(hr))
        return hr;

    // get stream length
    ULONG stream_length;
    
        STATSTG stat;
        hr = stream->Stat(&stat, STATFLAG_NONAME);
        if (FAILED(hr))
            return hr;
        stream_length = static_cast<ULONG>(stat.cbSize.QuadPart);
    

    // read data from stream
    std::vector<BYTE> raw_data;
    
        hr = stream->Seek( 0 , STREAM_SEEK_SET, nullptr);
        if (FAILED(hr))
            return hr;
        raw_data.resize(stream_length);
        ULONG bytes_read;
        hr = stream->Read(&raw_data.front(), stream_length, &bytes_read);
        if (FAILED(hr))
            return hr;
        if (bytes_read != stream_length)
            return E_FAIL;
    

    // encode bytes as base-64 string
    std::vector<WCHAR> encoded_bytes;
    
        DWORD encoded_length_with_null = 0;
        if (!CryptBinaryToStringW(&raw_data.front(), stream_length, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, nullptr, &encoded_length_with_null))
            return E_FAIL;
        encoded_bytes.resize(encoded_length_with_null);
        auto encoded_length = encoded_length_with_null;
        if (!CryptBinaryToStringW(&raw_data.front(), stream_length, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, &encoded_bytes.front(), &encoded_length))
            return E_FAIL;
        if (encoded_length != encoded_length_with_null - 1)
            return E_FAIL;
    

    // create result
    const auto result = SysAllocStringLen(&encoded_bytes.front(), encoded_bytes.size() - 1);
    if (result == nullptr)
        return E_OUTOFMEMORY;

    // set output parameters
    *pbstrOut = result;

    // success
    return S_OK;

客户端:

#include <windows.h>
#include <comdef.h>
#include <shlwapi.h>
#include <vector>
// link: crypt32.lib (for CryptStringToBinaryW)
// link: shlwapi.lib (for SHCreateMemStream)

/// <summary>
/// Unmarshals an interface from a token.
/// </summary>
HRESULT UnmarshalInterfaceFromToken(BSTR token, REFIID riid, LPVOID* ppv)

    // validate output parameters
    if (ppv == nullptr)
        return E_POINTER;

    // set default values for output parameters
    *ppv = nullptr;

    // validate input parameters
    if (token == nullptr)
        return E_INVALIDARG;

    // decode base-64 string as bytes
    std::vector<BYTE> decoded_bytes;
    
        DWORD decoded_length = 0;
        if (!CryptStringToBinaryW(token, 0, CRYPT_STRING_BASE64, nullptr, &decoded_length, nullptr, nullptr))
            return E_FAIL;
        decoded_bytes.resize(decoded_length);
        if (!CryptStringToBinaryW(token, 0, CRYPT_STRING_BASE64, &decoded_bytes.front(), &decoded_length, nullptr, nullptr))
            return E_FAIL;
        if (decoded_length != decoded_bytes.size())
            return E_FAIL;
    

    // wrap the bytes in an IStream
    IStreamPtr stream;
    stream.Attach(SHCreateMemStream(&decoded_bytes.front(), decoded_bytes.size()));
    if (!stream)
        return E_FAIL;

    // unmarshal interface from stream
    return CoUnmarshalInterface(stream, riid, ppv);

【讨论】:

谢谢! CoMarshalInterface 正是我所希望的,在我的案例中已经实现了它并且似乎工作得很好!

以上是关于通过 IPC 跨进程传递对 COM 对象的引用?的主要内容,如果未能解决你的问题,请参考以下文章

Android Service IPC通信之Messenger机制

Swoole基于IPC通信的跨进程连接池

OpenHarmony IPC通讯详解

AIDL跨进程通信

如何在不传递引用的情况下在 Python 中使用 SyncManager 跨进程共享列表

如何在 Python 中进行跨进程跨脚本同步