我的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 ++,则它起作用,但是为什么它在相反的操作中不起作用?
注意:我的数据很大,我真的不能在这里使用任何副本。
您应该首先完整阅读本文: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;
}
这是一个有效的示例,但不使用元帅。我还修改了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++
是一个示例。