使用 P/Invoke 调用带有 char* 参数的 C DLL 函数
我读过其他类似的问题,但它们没有解决这个特定的问题。我有一个带有 touppercase 函数的旧 C 库(作为示例)。这需要一个 char* 并返回一个 char*。然而,返回的指针是指向同一个字符串的指针(别问我不是我写的)。
该函数如下所示:
__declspec(dllexport)
char * __cdecl touppercase(char *ps_source)
{
char *ps_buffer = NULL;
assert (ps_source != NULL);
ps_buffer = ps_source;
while (*ps_buffer != '\0')
{
*ps_buffer = toupper(*ps_buffer);
ps_buffer++;
}
*ps_buffer = '\0';
return (ps_source);
}
声明此函数的 C# 代码如下所示:
[DllImport("mydll.dll", EntryPoint = "touppercase",
CharSet = CharSet.Ansi, ExactSpelling = true,
CallingConvention = CallingConvention.Cdecl)]
private static extern System.IntPtr touppercase(string postData);
在我的应用程序中对此的调用如下所示
string sTest2 = Marshal.PtrToStringAnsi(to_uppercase(sTest));
但是 sTest2 最终只是一个随机字符串。
我向同一个 dll 添加了一个具有相同参数的测试函数,但这会在本地分配内存并复制字符串。这很好用。为什么原来的版本现在可以用了?
注意:更新 dll 库本身不是一个选项。
I've read other similar questions on this but they don't solve this particular problem. I have an old C-library with a touppercase function (as an example). This takes a char* and returns a char*. However, the pointer returned is a pointer to the same string (don't ask me I didn't write it).
The function looks like this:
__declspec(dllexport)
char * __cdecl touppercase(char *ps_source)
{
char *ps_buffer = NULL;
assert (ps_source != NULL);
ps_buffer = ps_source;
while (*ps_buffer != '\0')
{
*ps_buffer = toupper(*ps_buffer);
ps_buffer++;
}
*ps_buffer = '\0';
return (ps_source);
}
The C# code to declare this looks like:
[DllImport("mydll.dll", EntryPoint = "touppercase",
CharSet = CharSet.Ansi, ExactSpelling = true,
CallingConvention = CallingConvention.Cdecl)]
private static extern System.IntPtr touppercase(string postData);
The call to this in my app looks like
string sTest2 = Marshal.PtrToStringAnsi(to_uppercase(sTest));
However sTest2 just ends up being a random string.
I added a test function to the same dll with the same parameters but this allocates memory locally and copies the string. This works fine. Why does the original version now work?
Note: Updating the dll libraries themselves isn't an option.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
该函数正在修改引用,因此您应该使用字符串构建器:
将其称为这样的:
对于返回指针的解释 ->本·沃伊特
The function is modifying the reference so you should use a stringbuilder:
Call it something like this:
For the explanation of the return pointer -> Ben Voigt
正如您所观察到的,返回类型是指向同一字符串的指针。提供给函数的字符串是 .NET 用于 Unicode->ANSI 转换的临时缓冲区,在 P/Invoke 返回之前释放。所以它是一个必须忽略的野指针(最好只使用 C# void 返回类型)。
我还建议使用
StringBuilder
作为参数而不是String
,这让 .NET 知道该函数将更改传递的参数。StringBuilder
的内容将由该函数修改,这就是您在没有返回值的情况下获取结果的方式。As you correctly observed, the return type is a pointer to the same string. And the string provided to the function is a temporary buffer used by .NET for Unicode->ANSI conversion, deallocated before P/Invoke returns. So it's a wild pointer which you must ignore (best to just use C# void return type).
I would also recommend using
StringBuilder
for the parameter instead ofString
, this lets .NET know that the function will change the parameter passed. The content of theStringBuilder
will be modified by the function, that's how you get your results without a return value.