C#调用C++DLL,将指针传递给指针参数
C# call C++ DLL passing pointer-to-pointer argument
你们能帮我解决以下问题吗?我有一个C++函数dll,它将被另一个C#应用程序调用。我需要的功能之一如下:
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++函数使用了一个指向DataStruct指针的指针,而我刚刚在.中传递了CS_DataStruct的引用
我可以知道如何将指针对指针传递给C++函数吗?如果不可能,有什么变通办法吗?(C++API是固定的,因此将API更改为指针是不可能的)
编辑:DataStruct的内存将由c++函数分配。在此之前,我不知道数据数组应该有多大。(感谢下面的评论)
我使用了以下测试实现:
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
,并且应该不会出现内存泄漏。我希望它能有所帮助。
不需要使用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);
如果您确实调用了本机代码,请确保您的结构在内存中对齐。CLR不保证对齐,除非您推动它。
尝试
[StructLayout(LayoutKind.Explicit)]
struct DataStruct
{
string data;
int len;
};
更多信息:http://www.developerfusion.com/article/84519/mastering-structs-in-c/
- 为函数定义符号不明确的指针参数
- 构造函数 (C++) 中的 char 指针参数存在问题
- 按引用调用与按指针参数调用的差异 前递增和后递增
- 为什么具有指针参数的成员函数需要指向指针的指针?
- 通过引用函数传递指针参数是什么意思?
- 从double到double*的指针参数
- void 函数中的指针参数返回不一致的值
- 无法向上转换指针到指针参数
- 将数组动态分配到具有指针参数的函数中
- 模板函数指针参数与构造函数参数
- 使用通用函数指针参数化函数模板的简洁方法
- 如何为C++字符串分配空指针参数?
- C++带有指针参数的函数
- 指针参数的默认值
- 将常量添加到函数模板指针参数
- 如何传递unique_ptr<T>代替原始*输出*指针参数?
- 不带星号的函数指针参数
- 功能指针参数参数转换为const
- 将指针参数传递给双指针参数
- 如何调试指针参数是否通过函数修改