我有一个COM对象的引用,并希望将该引用传递给另一个进程。 所以,我需要一些方法来序列化有关此引用的信息,这将允许我在另一个进程中再次恢复它。
有没有办法为任意类型的COM对象执行此操作?
我对COM不太了解;从我的理解,可能有Monikers的某些类型的对象(例如,COM引用,如Excel.Workbook可能会通过他们的.FullName属性恢复,似乎被用作名字对象),但到目前为止,我还没有发现任何类型的COM对象都可以使用这样的东西
答案 0 :(得分: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);
}