从 .Net 调用本机函数时参数值混乱
Posted
技术标签:
【中文标题】从 .Net 调用本机函数时参数值混乱【英文标题】:Parameter values messed up when calling native functions from .Net 【发布时间】:2020-10-09 15:38:34 【问题描述】:现在我有一个 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中,它可以正常工作。
这是正常行为吗?我究竟做错了什么?我怎样才能避免这种情况?
如果需要,我很乐意提供有关解决方案设置的更多详细信息。
【问题讨论】:
【参考方案1】:DllImport
的默认调用约定是 stdcall
,但 C/C++ 中的默认调用约定通常是 cdecl
。您描述的症状很容易由两种语言之间的调用约定不匹配引起,因此请确保两组代码就使用哪种调用约定达成一致。
此外,一般来说,您不能安全地跨 DLL 边界传递 C++ std::string
(或任何其他基于类的字符串类型),而且绝对不能跨语言传递。跨越语言边界时,仅使用可移植的 POD 类型非常重要。大多数语言都与 C 兼容,因此请仅使用 C 可以使用的类型(因此,在这种情况下,使用 char*
/wchar_t*
表示字符串)。
还必须考虑内存管理问题。如何分配内存,谁负责释放内存以及如何释放内存等。在这种情况下,如果 C/C++ DLL 返回一个 char*
字符串,并且 C# 自动将该数据编组为本机 string
,那么 C# 将尝试使用CoTaskMemFree()
释放char*
字符串,因此DLL需要使用CoTaskMemAlloc()
分配char*
字符串,例如:
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# 调用以释放 @987654339 @字符串正确,例如:
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);
【讨论】:
那么请mark the answer as accepted。以上是关于从 .Net 调用本机函数时参数值混乱的主要内容,如果未能解决你的问题,请参考以下文章