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

时间:2018-03-20 08:52:05

标签: c# com ipc

我有一个COM对象的引用,并希望将该引用传递给另一个进程。 所以,我需要一些方法来序列化有关此引用的信息,这将允许我在另一个进程中再次恢复它。

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

我对COM不太了解;从我的理解,可能有Monikers的某些类型的对象(例如,COM引用,如Excel.Workbook可能会通过他们的.FullName属性恢复,似乎被用作名字对象),但到目前为止,我还没有发现任何类型的COM对象都可以使用这样的东西

1 个答案:

答案 0 :(得分:1)

这是一种方式:

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

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