2010-07-06 53 views
1

我有一个生成值的C++ plus方法。我从一个C#应用程序调用这个方法。
C++的方法是这样的:char *在托管代码中?

extern "C" REGISTRATION_API char * generate(char dIn[],char dOut[]) 

generate方法返回字符数组(sOut[]=returnvalue; return sOut;

现在我从我的C#应用​​程序调用该方法:

[DllImport("mydll.dll")] 
static extern string generate(string sIn, string sOut); 

如你所见,c#中的返回类型是string。发生的事情是,c#中的返回值不正确,并且已损坏。 (该值是generate方法中是正确的,但每当我把它从C#中提取它,我得到一些错误的值。)

那是正确的,我在C#方法有string返回值,而在C++中,它是一个char*? 请分享您的意见,这是紧急的,谢谢。

+1

你'char *'实际上代表一个字符串(ANSI/Unicode?)还是一个字节序列?如果是字符串,请使用StringBuilder,否则请在'static extern'声明中使用byte []。 – dtb 2010-07-06 15:42:50

回答

1

无法从本地C++应用程序安全地调用此方法。它返回一个指向栈帧数组的指针。对另一个函数的任何调用都会覆盖该堆栈帧并损坏该数组。

由于在获取此函数的返回值后没有函数调用,因此现在可能在C++程序中偶然发生作用。当P/Invoke编组人坐在中间时,这种运气不再可用。

您将不得不重新设计C++函数。你应该给它的参数允许调用者传递它自己的缓冲区来填充结果。例如:

extern "C" void __stdcall generate(const char* input, char* output, int outputSize) 

调用此在C#代码通过传递一个StringBuilder的输出参数,正确初始化以足够的容量来接收的字符串。 outputSize参数确保C++函数可以避免写入缓冲区末尾并破坏垃圾回收堆。不要忽视它。

3

不幸的是,char*可能有些模棱两可。 (从字面上看,它是一个指向单个字符的指针,在那里你可能或者可能不能通过像数组那样解引用指针来获得其他字符。)通常,它是一个指向单个字节的空终止字符串的指针ANSI编码(微软称之为LPStr)。默认情况下,P/Invoke编组System.String的实例作为BStr类型,它类似于Unicode Pascal字符串(开头的长度)。您需要使用MarshalAs attribute明确指定您希望托管代码与非托管代码之间的字符串编组方式。

最有可能的,你要这样声明:

[DllImport("mydll.dll")] 
[return: MarshalAs(UnmanagedType.LPStr)] 
static extern string generate(
    [MarshalAs(UnmanagedType.LPStr)] string sIn, 
    [MarshalAs(UnmanagedType.LPStr)] out string sOut); 

然而,你的函数签名是困惑:这是返回字符串,或者将它设置一个输出变量?您可能需要调整P/Invoke声明以适应您的需求。

2

那是正确的,我在C#方法有 字符串返回值,而在C++中它是 一个char *?

这要看真的。

如果C++方法为字符串分配内存并期望调用者释放它,那么你有问题......大问题。你可以尝试调用Marshal.FreeHGlobal,但是没有保证C++方法实际上在全局堆中分配内存。大多数API都可以将指针送回其中一种方法来释放内存。

char *也可能指向不需要释放的内存。在这种情况下,使用string应该没问题。

您可以让C#声明返回IntPtr然后调用其中一个Marhsal.PtrToStringXXX方法。