正如您在收到的各种回复中指出的那样,存在几个缺陷。总而言之,这是我的建议。
在本机端,声明如下内容(假设您使用 C++ 进行编码):
extern "C"
__declspec(dllexport)
const char* __cdecl Connect(const char* lpPostData)
{
static char buffer[1000];
...
return buffer;
}
extern "C" 向编译器明确表示,它不应在导出函数名称时破坏函数名称,而应像对待 C 函数一样对待它。
__ceclspec(dllexport) 使函数在 DLL 中作为导出入口点可见。然后,您可以从外部项目中引用它。
__cdecl 确保您使用 C 方式在堆栈上传递参数。如果调用者不采用相同的参数传递方案,您可能会搞砸事情。
始终使用char:对char(ANSI 文本)或wchar_t(16 位Unicode 文本)感到厌烦。永远不要像您在示例中所做的那样返回指向局部变量的指针。一旦函数返回,该内存位置将不再有效并且可能包含垃圾。此外,如果用户正在修改存储在那里的数据,可能会导致程序崩溃,因为这会破坏调用堆栈。
在托管 C# 方面,这是我的建议:
[DllImport("test.dll", EntryPoint="Connect",
CharSet=CharSet.Ansi, ExactSpelling=true,
CallingConvention=CallingConvention.Cdecl)]
private static extern System.IntPtr Connect(string postData);
它将绑定到您的 Connect 入口点,使用 ANSI 字符串约定(每个字符 8 位,这是 char 所期望的)并确保调用者期待 __cdecl 调用约定。
您必须通过调用将IntPtr 编组为string 对象:
string value = Marshal.PtrToStringAnsi (Connect (postData));
注意:在我的原始帖子中,我建议将 Connect 声明为返回 string,但作为评论者 Mattias 更正,这不是正确的做法。请参阅CLR Inside Out 上的这篇文章,了解 CLR 如何处理 retvals 类型的 string。感谢您向我指出这一点(也感谢romkyns)。
您还可以考虑将字符串从buffer 复制到本机代码中通过CoTaskMemAlloc 分配的一块内存中,并返回一个指向它的指针。然后,您可以简单地将 DllImport 函数声明为返回 string,并且不需要在托管世界中进行任何进一步的编组。