如何将 Byte* 从 C++ 转换为 C# 中的 Byte[]

How to convert a Byte* from C++ to Byte[] in C#

我有一个 C++ 库,其中包含一个返回 Byte* 的库:

typedef unsigned char Byte;
Byte* RotateImage90(Byte* data, int w, int h);

我在 C# (Xamarin) 的程序中使用这个库:

[DllImport("libCpp", EntryPoint = "RotateImage90")]
public static extern IntPtr rotate90(byte[] data, int w, int h);

Byte[] test(Byte[] data, int w, int h)
{
    IntPtr ptr = rotate90(data, w, h);
    Byte[] img = ????;// <= function missing
    return img;
}

效果很好,但我不知道如何将指针转换为字节数组。有人知道这样做的功能吗?

函数接口的一个问题是函数动态分配给 return 旋转图像字节数组的内存必须使用 same 内存分配器分配C# 端(或任何客户端代码)将用于释放内存。

换句话说,分配内存的模块和释放内存的模块必须使用相同的分配器。

当我需要在本机代码和 C# 代码之间传递一些数组数据时,我成功地使用了 Safe Arrays。在C++端,可以使用ATL的CComSafeArraysimplify the safe array programming;另一方面,C# 和 CLR 非常了解安全数组,因此很容易在 C# 中获取数组数据并在托管代码中使用它。

您可以使用这样的函数在 C++ 中生成安全的字节数组(在您的情况下,安全数组将存储旋转后的图像数据):

extern "C" HRESULT __stdcall ProduceSafeArrayOfBytes(/* [out] */ SAFEARRAY** ppsa)
{
    HRESULT hr = S_OK;

    try
    { 
        // Create the safe array to be returned to the caller
        CComSafeArray<BYTE> sa( /* Element count */);

        // Fill the safe array data.
        // You can use a simple sa[i] syntax,
        // where 'i' is a 0-based index
        ...

        // Return the safe array to the caller (transfer ownership)
        *ppsa = sa.Detach();
    }
    // Convert exceptions to HRESULT return codes
    catch (const CAtlException& e)
    {
        hr = e;
    }
    catch (const std::exception& )
    { 
        hr = E_FAIL;
    }

    return hr;
}

在 C# 端,您可以使用此 PInvoke 签名:

[DllImport("NativeDll.dll", PreserveSig = false)]
public static extern void ProduceSafeArrayOfBytes(
    [Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
    out byte[] result
);

VT_UI1 枚举字段告诉 .NET 编组器安全数组包含字节。

您可以使用如下简单的代码在 C# 中获取数组数据:

byte[] data;
ProduceSafeArrayOfBytes(out data);

如您所见,在您的 C# 代码中,您处理的是一个简单的 byte[] 数组;所有正确的数据编组(包括释放内存)都会自动发生。

您可以修改上述框架代码,添加其他功能参数,例如您的图像宽度和高度。


作为替代方案,另一种选择是使用 C++/CLI 开发一个微型桥接层,以将原始 C 风格数组转换为 .NET 托管数组。

无论如何,关于使用通用内存分配器分配和释放数组内存的 DLL 函数接口的说明仍然有效。


作为第三种选择,如果您可以修改您的 DLL 函数接口,您可以要求调用者分配一个数组并将其传递给 DLL 函数。该函数会将结果数据写入此 调用者分配的数组

这将简化内存管理,因为您为 DLL 函数提供了一个 已经 由调用者分配的内存块。调用者将负责分配和释放该内存。

因此,您的 DLL 函数将如下所示:

extern "C" void __cdecl RotateImage90(Byte* result, Byte* data, int width, int height);

结果数组由调用者分配,调用者也负责释放它。 该函数只是将其输出写入此调用者分配的数组中。

PInvoke 看起来像这样:

[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RotateImage90(byte[] result, byte[] data, int width, int height);