C#调用C ++ DLL传递指针到指针的参数

你能帮我解决一下这个问题吗? 我有一个C ++函数dll,它将由另一个C#应用程序调用。 我需要的function之一如下:

struct DataStruct { unsigned char* data; int len; }; DLLAPI int API_ReadFile(const wchar_t* filename, DataStruct** outData); 

我在C#中编写了以下代码:

 class CS_DataStruct { public byte[] data; public int len; } [DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] private static extern int API_ReadFile([MarshalAs(UnmanagedType.LPWStr)]string filename, ref CS_DataStruct data); 

不幸的是,上面的代码不起作用…我想这是因为C ++ func采用DataStruct的指针指向,而我只是传递了CS_DataStruct的引用。

我可以知道如何将指针传递给C ++函数? 如果不可能,有任何解决方法吗? (C ++ API是固定的,因此无法将API更改为指针)

编辑: DataStruct的内存将由c ++函数分配。 在此之前,我不知道数据arrays应该有多大。 (感谢下面的评论)

我使用了以下测试实现:

 int API_ReadFile(const wchar_t* filename, DataStruct** outData) { *outData = new DataStruct(); (*outData)->data = (unsigned char*)_strdup("hello"); (*outData)->len = 5; return 0; } void API_Free(DataStruct** pp) { free((*pp)->data); delete *pp; *pp = NULL; } 

访问这些函数的C#代码如下:

 [StructLayout(LayoutKind.Sequential)] struct DataStruct { public IntPtr data; public int len; }; [DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] unsafe private static extern int API_ReadFile([MarshalAs(UnmanagedType.LPWStr)]string filename, DataStruct** outData); [DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl)] unsafe private static extern void API_Free(DataStruct** handle); unsafe static int ReadFile(string filename, out byte[] buffer) { DataStruct* outData; int result = API_ReadFile(filename, &outData); buffer = new byte[outData->len]; Marshal.Copy((IntPtr)outData->data, buffer, 0, outData->len); API_Free(&outData); return result; } static void Main(string[] args) { byte[] buffer; ReadFile("test.txt", out buffer); foreach (byte ch in buffer) { Console.Write("{0} ", ch); } Console.Write("\n"); } 

现在数据安全地传输到buffer ,应该没有内存泄漏。 我希望它会有所帮助。

如果您确实调用了本机代码,请确保您的结构在内存中已对齐。 除非你按下它,否则CLR不保证对齐。

尝试

 [StructLayout(LayoutKind.Explicit)] struct DataStruct { string data; int len; }; 

更多信息: http : //www.developerfusion.com/article/84519/mastering-structs-in-c/

没有必要使用unsafe从DLL传递指向数组的指针。 这是一个例子(参见’results’参数)。 关键是使用ref属性。 它还显示了如何传递其他几种类型的数据。

如C ++ / C中所定义:

 #ifdef __cplusplus extern "C" { #endif #ifdef BUILDING_DLL #define DLLCALL __declspec(dllexport) #else #define DLLCALL __declspec(dllimport) #endif static const int DataLength = 10; static const int StrLen = 16; static const int MaxResults = 30; enum Status { on = 0, off = 1 }; struct Result { char name[StrLen]; //!< Up to StrLen-1 char null-terminated name float location; Status status; }; /** * Analyze Data * @param data [in] array of doubles * @param dataLength [in] number of floats in data * @param weight [in] * @param status [in] enum with data status * @param results [out] array of MaxResults (pre-allocated) DLLResult structs. * Up to MaxResults results will be returned. * @param nResults [out] the actual number of results being returned. */ void DLLCALL __stdcall analyzeData( const double *data, int dataLength, float weight, Status status, Result **results, int *nResults); #ifdef __cplusplus } #endif 

在C#中使用:

 private const int DataLength = 10; private const int StrLen = 16; private const int MaxThreatPeaks = 30; public enum Status { on = 0, off = 1 }; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct Result { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = StrLen)] public string name; //!< Up to StrLen-1 char null-terminated name public float location; public Status status; } [DllImport("dllname.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "analyzeData@32")] // "@32" is only used in the 32-bit version. public static extern void analyzeData( double[] data, int dataLength, float weight, Status status, [MarshalAs(UnmanagedType.LPArray, SizeConst = MaxResults)] ref Result[] results, out int nResults ); 

如果没有extern "C"部分,C ++编译器将以编译器相关的方式破坏导出名称。 我注意到EntryPoint / Exported函数名称与64位DLL中的函数名称完全匹配,但在编译为32位DLL时附加了“@ 32”(数字可能会有所不同)。 运行dumpbin /exports dllname.dll以确定导出的名称。 在某些情况下,您可能还需要使用DLLImport参数ExactSpelling = true 。 请注意,此函数声明为__stdcall 。 如果未指定, __cdecl ,您需要CallingConvention.Cdecl

以下是它在C#中的使用方式:

 Status status = Status.on; double[] data = { -0.034, -0.05, -0.039, -0.034, -0.057, -0.084, -0.105, -0.146, -0.174, -0.167}; Result[] results = new Result[MaxResults]; int nResults = -1; // just to see that it changes (input value is ignored) analyzeData(data, DataLength, 1.0f, status, ref results, out nResults);