我似乎无法从COM C++程序中获取C#程序中填充的字节数组。C#程序包含对C++ DLL的引用,并通过以下方式实例化:
_wiCore = new WebInspectorCoreLib.WICore();
实际呼叫
uint imageSize = *image byte count*; // set to size of image being retrieved
var arr = new byte[imageSize];
_wiCore.GetFlawImage(1, 0, ref imageSize, out arr);
C++ IDL:
[id(5)] HRESULT GetFlawImage([in] ULONG flawID, [in] USHORT station, [in, out] ULONG *pImageSize, [out] BYTE *pImageBytes);
这将正确返回图像大小,但不会返回数组中的任何内容。我还尝试了签名(pImageBytes的额外间接级别):
[id(5)] HRESULT GetFlawImage([in] ULONG flawID, [in] USHORT station, [in, out] ULONG *pImageSize, [out] BYTE **pImageBytes);
在C#中,传入一个IntPtr,但它返回的内存地址包含图像字节的地址,而不是图像字节。
你觉得我做错了什么吗?
发布于 2021-10-21 20:32:40
从C++传回数组有多种方法。
例如,您可以像尝试那样使用原始字节数组。它可以工作,但在.NET中不是很实用,因为它不是.NET喜欢的COM automation type。
所以,假设我们有这个.idl:
interface IBlah : IUnknown
{
HRESULT GetBytes([out] int *count, [out] unsigned char **bytes);
}
下面是一个本机实现示例:
STDMETHODIMP CBlah::GetBytes(int* count, unsigned char** bytes)
{
if (!count || !bytes)
return E_INVALIDARG;
*count = numBytes;
*bytes = (unsigned char*)CoTaskMemAlloc(*count);
if (!*bytes)
return E_OUTOFMEMORY;
for (unsigned char i = 0; i < *count; i++)
{
(*bytes)[i] = i;
}
return S_OK;
}
以及一个样例C#调用代码(请注意,当.NET类型库导入程序不是COM自动化类型时,它不知道指针以外的任何内容,所以它只是盲目地将参数定义为IntPtr):
var obj = (IBlah)Activator.CreateInstance(myType);
// we must allocate a pointer (to a byte array pointer)
var p = Marshal.AllocCoTaskMem(IntPtr.Size);
try
{
obj.GetBytes(out var count, p);
var bytesPtr = Marshal.ReadIntPtr(p);
try
{
var bytes = new byte[count];
Marshal.Copy(bytesPtr, bytes, 0, bytes.Length);
// here bytes is filled
}
finally
{
// here, we *must* use the same allocator than used in native code
Marshal.FreeCoTaskMem(bytesPtr);
}
}
finally
{
Marshal.FreeCoTaskMem(p);
}
注意:这在进程外的情况下不会起作用,因为.idl不完整,无法支持它,等等。
也可以使用COM自动化类型,如SAFEARRAY (或包装变体)。这也允许您将其与其他语言(如VB/VBA、脚本引擎等)一起使用。
所以,我们可以有这样的.idl:
HRESULT GetBytesAsArray([out] SAFEARRAY(BYTE)* array);
此示例本机实现(有点复杂,因为COM自动化不适用于C/C++,而适用于VB/VBA/脚本对象...):
STDMETHODIMP CBlah::GetBytesAsArray(SAFEARRAY** array)
{
if (!array)
return E_INVALIDARG;
// create a 1-dim array of UI1 (byte)
*array = SafeArrayCreateVector(VT_UI1, 0, numBytes);
if (!*array)
return E_OUTOFMEMORY;
unsigned char* bytes;
HRESULT hr = SafeArrayAccessData(*array, (void**)&bytes); // check errors
if (FAILED(hr))
{
SafeArrayDestroy(*array);
return hr;
}
for (unsigned char i = 0; i < numBytes; i++)
{
bytes[i] = i;
}
SafeArrayUnaccessData(*array);
return S_OK;
}
正如预期的那样,示例C#代码要简单得多:
var obj = (IBlah)Activator.CreateInstance(myType);
obj.GetBytesAsArray(out var bytesArray);
https://stackoverflow.com/questions/69667470
复制相似问题