现在我有一个c ++ DLL项目,用于.Net中较低级功能的包装。
C ++代码:
string testA(uint a) {
cout << a;
return "egrt";
}
bool testB(uint a) {
cout << a;
return false;
}
C ++标头:
#pragma once
#ifdef API_EXPORTS
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif
typedef unsigned int uint;
extern "C" API string testA(uint a);
extern "C" API bool testB(uint a);
C#代码:
[DllImport(...)] extern static string testA(uint a);
[DllImport(...)] extern static bool testB(uint a);
static void Main() {
testA(10);
testB(13);
}
由于某种原因,testA中的参数不是我给出的数字,但是在testB中,它可以正常工作。
那是正常行为吗?我究竟做错了什么?我该如何避免呢?
如果需要,我很乐意提供有关解决方案设置的更多详细信息。
答案 0 :(得分:3)
DllImport
的默认调用约定为stdcall
,但C / C ++中的默认调用约定通常为cdecl
。您描述的症状很容易由两种语言之间的调用约定不匹配引起,因此请确保两组代码都同意使用哪种调用约定。
此外,您通常不能跨DLL边界(绝对不是在语言之间)安全地传递C ++ std::string
(或任何其他基于类的字符串类型)。跨越语言界限时,仅使用便携式POD类型非常重要。大多数语言都与C兼容,因此请仅使用C可以使用的类型(因此,在这种情况下,对字符串使用char*
/ wchar_t*
)。
还有一些内存管理问题也必须考虑在内。内存的分配方式,负责释放内存的人员以及内存的分配方式等。在这种情况下,如果C / C ++ DLL返回char*
字符串,并且C#自动将数据编组为本地string
,则C#将尝试使用char*
释放CoTaskMemFree()
字符串,因此DLL需要使用char*
分配CoTaskMemAlloc()
字符串,例如:
char* __stdcall testA(uint32_t a) {
cout << a;
const char *str = "egrt";
char *result = (char*) CoTaskMemAlloc(strlen(str)+1);
if (result)
strcpy(result, str);
return result;
}
bool __stdcall testB(uint32_t a) {
cout << a;
return false;
}
#pragma once
#include <cstdint>
#ifdef API_EXPORTS
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif
extern "C" API char* __stdcall testA(uint32_t a);
extern "C" API bool __stdcall testB(uint32_t a);
[DllImport(..., CharSet=CharSet.Ansi)]
extern static string testA(uint a);
[DllImport(...)]
extern static bool testB(uint a);
static void Main() {
testA(10);
testB(13);
}
如果这不是DLL的选择,则必须将char*
字符串整理为IntPtr
,并且DLL将必须导出一个附加函数供C#调用。正确释放char*
字符串,例如:
char* __stdcall testA(uint32_t a) {
cout << a;
const char *str = "egrt";
char *result = ...; // allocated some other way...
if (result)
strcpy(result, str);
return result;
}
void __stdcall freeA(void *p) {
cout << p;
// deallocate p as needed...
}
bool __stdcall testB(uint32_t a) {
cout << a;
return false;
}
#pragma once
#include <cstdint>
#ifdef API_EXPORTS
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif
extern "C" API char* __stdcall testA(uint32_t a);
extern "C" API void __stdcall freeA(void *p);
extern "C" API bool __stdcall testB(uint32_t a);
[DllImport(...)]
extern static IntPtr testA(uint a);
[DllImport(...)]
extern static void freeA(IntPtr p);
[DllImport(...)]
extern static bool testB(uint a);
static void Main() {
IntPtr p = testA(10);
// string s = Marshal.PtrToStringAnsi(p);
freeA(p);
testB(13);
}