2011-05-27 113 views
5

我已经阅读了其他类似的问题,但他们没有解决这个问题。我有一个带touppercase函数的旧C库(作为例子)。这需要一个char *并返回一个char *。但是,返回的指针是一个指向同一个字符串的指针(不要问我,我没有写它)。使用char *参数调用C DLL函数使用P/Invoke

功能如下:

__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库本身不是一个选项。

+0

调用此函数后sTest会发生什么?它会改变吗? – 2011-05-27 14:19:01

+0

你为什么要宣布调用约定。默认情况下C#使用调用约定而不是标准约定(C/C++)。你已经改变了C库中的约定。如果C代码没有声明为默认值之外的其他值,则只需更改约定。 – 2011-05-27 14:41:29

+1

@ramhound:明确指定导出函数的调用约定是最安全的。您不希望导出受'/ Gr'或'/ Gz'的影响。 – 2011-05-27 16:02:12

回答

10

的功能修改参考,所以你应该使用StringBuilder:

[DllImport("dll.dll", EntryPoint="touppercase", 
CharSet = CharSet.Ansi, ExactSpelling = true, 
CallingConvention = CallingConvention.Cdecl)] 
private static extern System.IntPtr touppercase(StringBuilder postData); 

说它是这样的:

StringBuilder sTest = new StringBuilder("abs"); 
    touppercase(sTest); 
    string result = sTest.ToString(); 

对于返回指针的解释 - >本福格特

+0

工作奇迹。非常感谢。 – Jonnster 2011-05-27 15:12:53

3

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

我也建议使用StringBuilder作为参数而不是String,这让.NET知道函数会改变传递的参数。 StringBuilder的内容将被该函数修改,这就是您如何得到没有返回值的结果。

+0

非常感谢。 – Jonnster 2011-05-27 15:13:23