通过 IPC 跨进程传递对 COM 对象的引用?
Passing a reference to a COM object accross processes via IPC?
我有一个 COM 对象的引用,并想将该引用传递给另一个进程。
因此,我需要一些方法来序列化有关此引用的信息,以便我可以在其他进程中再次恢复它。
有没有办法对任意类型的 COM 对象执行此操作?
我对 COM 了解不多;据我了解,某些类型的对象可能有名字对象(例如,Excel.Workbook 等 COM 引用可能会通过它们的 .FullName 属性 恢复,这似乎被用作名字对象),但是到目前为止,我还没有发现对于任何类型的 COM 对象是否可以实现类似的功能
这是一种方法:
- 在服务器进程中,使用
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);
}
我有一个 COM 对象的引用,并想将该引用传递给另一个进程。 因此,我需要一些方法来序列化有关此引用的信息,以便我可以在其他进程中再次恢复它。
有没有办法对任意类型的 COM 对象执行此操作?
我对 COM 了解不多;据我了解,某些类型的对象可能有名字对象(例如,Excel.Workbook 等 COM 引用可能会通过它们的 .FullName 属性 恢复,这似乎被用作名字对象),但是到目前为止,我还没有发现对于任何类型的 COM 对象是否可以实现类似的功能
这是一种方法:
- 在服务器进程中,使用
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);
}