C# 使用P/Invoke调用带有char*参数的C DLL函数

C# 使用P/Invoke调用带有char*参数的C DLL函数,c#,dll,pinvoke,C#,Dll,Pinvoke,我读过其他类似的问题,但它们并不能解决这个问题。我有一个带有touppercase函数的旧C库(作为示例)。这将获取一个char*并返回一个char*。但是,返回的指针是指向同一字符串的指针(不要问我我没有写它) 函数如下所示: __declspec(dllexport) char * __cdecl touppercase(char *ps_source) { char *ps_buffer = NULL; assert (ps_source != NULL); p

我读过其他类似的问题,但它们并不能解决这个问题。我有一个带有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);
}
    StringBuilder sTest = new StringBuilder("abs");
    touppercase(sTest);
    string result = sTest.ToString();
声明这一点的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库本身不是一个选项。

正如您正确观察到的,返回类型是指向同一字符串的指针。提供给函数的字符串是.NET用于Unicode->ANSI转换的临时缓冲区,在P/Invoke返回之前释放。所以它是一个必须忽略的野生指针(最好只使用C#void返回类型)


我还建议对参数使用
StringBuilder
,而不是
String
,这样可以让.NET知道函数将更改传递的参数。函数将修改
StringBuilder
的内容,这就是在没有返回值的情况下获得结果的方式。

函数正在修改引用,因此您应该使用StringBuilder:

[DllImport("dll.dll", EntryPoint="touppercase",
CharSet = CharSet.Ansi, ExactSpelling = true,
CallingConvention = CallingConvention.Cdecl)]
private static extern System.IntPtr touppercase(StringBuilder postData);
可以这样称呼它:

__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);
}
    StringBuilder sTest = new StringBuilder("abs");
    touppercase(sTest);
    string result = sTest.ToString();

对于返回指针->Ben Voigt

的解释,调用此函数后sTest会发生什么?它改变了吗?你为什么要声明呼叫约定。默认情况下,C#使用调用约定而不是标准约定(C/C++)。您已经在C库中更改了约定。只有在C代码未声明为默认值以外的值时,您才需要更改约定。@ramhound:显式指定导出函数的调用约定是最安全的。您不希望导出受到
/Gr
/Gz
的影响。