首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C#程序无法从C++ COM程序取回字节数组

C#程序无法从C++ COM程序取回字节数组
EN

Stack Overflow用户
提问于 2021-10-21 19:02:56
回答 1查看 81关注 0票数 1

我似乎无法从COM C++程序中获取C#程序中填充的字节数组。C#程序包含对C++ DLL的引用,并通过以下方式实例化:

代码语言:javascript
运行
复制
_wiCore = new WebInspectorCoreLib.WICore();

实际呼叫

代码语言:javascript
运行
复制
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:

代码语言:javascript
运行
复制
[id(5)] HRESULT GetFlawImage([in] ULONG flawID, [in] USHORT station, [in, out] ULONG *pImageSize, [out] BYTE *pImageBytes);

这将正确返回图像大小,但不会返回数组中的任何内容。我还尝试了签名(pImageBytes的额外间接级别):

代码语言:javascript
运行
复制
[id(5)] HRESULT GetFlawImage([in] ULONG flawID, [in] USHORT station, [in, out] ULONG *pImageSize, [out] BYTE **pImageBytes);

在C#中,传入一个IntPtr,但它返回的内存地址包含图像字节的地址,而不是图像字节。

你觉得我做错了什么吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-10-21 20:32:40

从C++传回数组有多种方法。

例如,您可以像尝试那样使用原始字节数组。它可以工作,但在.NET中不是很实用,因为它不是.NET喜欢的COM automation type

所以,假设我们有这个.idl:

代码语言:javascript
运行
复制
interface IBlah : IUnknown
{
    HRESULT GetBytes([out] int *count, [out] unsigned char **bytes);
}

下面是一个本机实现示例:

代码语言:javascript
运行
复制
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):

代码语言:javascript
运行
复制
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:

代码语言:javascript
运行
复制
HRESULT GetBytesAsArray([out] SAFEARRAY(BYTE)* array);

此示例本机实现(有点复杂,因为COM自动化不适用于C/C++,而适用于VB/VBA/脚本对象...):

代码语言:javascript
运行
复制
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#代码要简单得多:

代码语言:javascript
运行
复制
var obj = (IBlah)Activator.CreateInstance(myType);
obj.GetBytesAsArray(out var bytesArray);
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69667470

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档