I have a reference to a COM object, and would like to pass that reference on to another process.
So, I'd need some way to serialize information about this reference that would allow me to restore it again in the other process.
Is there any way to do this for COM objects of any arbitrary type?
I'm don't know too much about COM; from what I understand, there may be Monikers for some types of objects (for example, COM references such as Excel.Workbook might be restored via their .FullName property which seems to be used as the moniker), but so far I haven't found if something like this is possible for any kind of COM object
Here's one way:
- In the server process, use
CoMarshalInterface
to marshal the
interface to a stream. Convert bytes written to stream to a base-64
string (or any other encoding). Pass the encoded string to your
client.
- In the client process, receive the string from your server. Decode
the string into bytes. Wrap those bytes in a stream. Unmarshal from
the stream.
Server side:
#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;
}
Client side:
#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);
}