通过 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机制