将返回数组从C ++封送到C#

问题描述 投票:0回答:2

我的c ++应用程序中有一个数组,我想使用它的指针从中创建一个C#数组委托,而不进行复制。因此,我可以使其从C#到C ++正常工作,C ++可以访问C#中定义的同一数组,但不能使其反向工作。

C#代码:

   [DllImport("CppDll.dll",CallingConvention=CallingConvention.Cdecl)]
   [return: MarshalAs(UnmanagedType.LPArray)]
   public static extern int[] GetCppArray();

C ++代码:

    int test_data[5] = { 12,60,55,49,26 };

    extern "C" __declspec(dllexport) int* GetCppArray()
    {
        return test_data;
    }

在C#中使用:

int[] cpparray = NativeLib.GetCppArray();

我收到此错误:

System.Runtime.InteropServices.MarshalDirectiveException:'无法封送'返回值':无效的托管/非托管类型组合。'

我知道我可以使用内存编写器通过数组指针地址直接写入C ++内存。如果在参数中使用相同的MarshalAs(UnmanagedType.LPArray)并将C#数组传递给c ++,则它起作用,但是为什么它在相反的操作中不起作用?

注意:我的数据很大,我真的不能在这里使用任何副本。

c# c++ pinvoke dllimport
2个回答
0
投票

您应该首先完整阅读本文:https://docs.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-for-arrays#unmanaged-arrays

我也找到了这个相关的质量检查:How to create and initialize SAFEARRAY of doubles in C++ to pass to C#

您的GetCppArray函数仅返回一个指针-它不返回自描述的“安全”数组,而.NET中的数组包含长度和等级(维度)信息,因此,您肯定需要将C ++代码修改为正确执行此操作。

第一个选择是将数组作为COM样式安全数组返回,这是通过the SAFEARRAY( typename ) macro完成的-它必须作为参数而不是返回值传递。

[在C ++中使用COM安全数组的主要方法有两种:使用Win32函数,例如SAFEARRAY( typename )-很难正确使用,或者使用SafeArrayCreate

(免责声明:我是通过查看API引用编写此代码的,我尚未对其进行测试-我什至不知道它是否会编译)。

the ATL CComSafeArray

然后需要更新C#代码以声明它返回CComSafeArray

// C++ code for SafeArrayCreate:

#include <comdef.h>
int test_data[5] = { 12, 60, 55, 49, 26 };

extern "C" __declspec(dllexport) HRESULT GetCppArray( [out] SAFEARRAY( int )** arr )
{
    SAFEARRAYBOUND bounds;
    bounds.lLbound   = 0;
    bounds.cElements = sizeof(test_data);

    *arr = SafeArrayCreate(
        VT_I4,  // element type
        1,      // dimensions
        &bounds 
    );
    if( !arr ) {
        // SafeArrayCreate failed.
        return E_UNEXPECTED;
    }

    int* arrPtr;
    HRESULT hr = SafeArrayAccessData( *arr, &arrPtr );
    if( !SUCCEEDED( hr ) ) {
         hr = SafeArrayDestroy( arr );
         // After SafeArrayDestory, if `hr != S_OK` then something is really wrong.
         return E_UNEXPECTED;
    }

    for( size_t i = 0; i < sizeof(test_data); i++ ) {
        *arrPtr[i] = test_data[i];
    }

    hr = SafeArrayUnaccessData( *arrPtr );
    if( !SUCCEEDED( hr ) ) {
         hr = SafeArrayDestroy( arr );
         return E_UNEXPECTED;
    }

    return S_OK;
}

0
投票

这是一个有效的示例,但不使用元帅。我还修改了SafeArray方法的定义。

C ++代码

标题

// HRESULT is best represented as a UInt32 instead of Int32.

[DllImport( "CppDll.dll", CallingConvention = CallingConvention.Cdecl )]
public static extern UInt32 GetCppArray(
    [MarshalAs( UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_I4 )] out Int32[] arr
);

。cpp文件

GetCppArray

C#代码

#ifdef LIBRARY_EXPORTS
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif

extern "C" API void GetCppArray(int** values, int* size);

对于您的int testData[5] = { 1, 2, 3, 4, 5 }; void GetCppArray(int** values, int* size) { *values = &testData[0]; *size = 5; } 项目,应检查class Program { [DllImport("Library.dll")] private static extern unsafe void GetCppArray(out int* values, out int size); static void Main(string[] args) { unsafe { int* values; int size; GetCppArray(out values, out size); for (int i = 0; i < size; i++) { Console.WriteLine(values[i].ToString()); } } } } (转到项目属性->构建->常规部分)。

如果在原始代码中动态分配了用于值向量的内存,则可以定义一个新方法C#来分配内存,也可以使用Allow unsafe code类。 C++是一个示例。

© www.soinside.com 2019 - 2024. All rights reserved.