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

Passing a reference to a COM object accross processes via IPC?

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

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

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

这是一种方法:

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

服务器端:

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