将StringBuilder传递给期望char指针的DLL函数

tre*_*r-e 3 c# c++ pointers dllimport

我正在尝试与在Delphi中创建的DLL库进行交互.在C++中,我使这个调用非常好:

for(int y = 1; y <= 12; y++)
{
    char * chanName = (char *) malloc(21); 
    memset(chanName,0,21);
    channelName(y,20,chanName);
    ...
}
Run Code Online (Sandbox Code Playgroud)

channelName类型定义为何处typedef int (CALLBACK* ChannelName)(int,int,char*);

现在我想在C#中做同样的事情.我搜索并发现它StringBuilder通常用作DLL函数的char指针.这是我如何声明我的功能:

[DllImport("myDLL.dll")]
public static extern int ChannelName(int x, int y, StringBuilder z);
Run Code Online (Sandbox Code Playgroud)

以下是我试图称之为:

for (int x = 0; x < 12; x++)
{
    StringBuilder b = new StringBuilder(100);
    DLLInterface.ChannelName(x+1, b.Length, b);
    Console.WriteLine(b.ToString());
}
Run Code Online (Sandbox Code Playgroud)

这只是让控制台打印出乱码,例如:

? §
? î?8f9
? î?8f9
? î?8f9
? î?8f9
? î?8f9
? î?8f9
? î?8f9
? î?8f9
? î?8f9
? î?8f9
? î?8f9
Run Code Online (Sandbox Code Playgroud)

我记得在C++中遇到类似的问题,这就是为什么我memset的内存是我malloc为0.我试图在C#中找到一个等价物,但也许这是一个问题StringBuilder而不是?如果我的问题不是很清楚,我只想将一个字符串传递给我的函数,让函数填满,然后打印出来.字符串在C#中是不可变的,并且没有好的指针选项存在,这就是我正在尝试的原因StringBuilder.

Ter*_*ver 6

在.NET中,字符串(和StringBuilders)是16位Unicode字符.我的猜测是你的本地函数处理8位ASCII字符.您需要告诉Marshaller如何在编组时转换字符.像这样更改您的DllImport属性:

[DllImport("myDLL.dll", CharSet=CharSet.Ansi)]
public static extern int ChannelName(int x, int y, [Out] StringBuilder z);
Run Code Online (Sandbox Code Playgroud)

更新

你还应该在StringBuilder上指定[Out]属性,这样Marshaller只会在出路时进行编组,因为你在途中没有传递任何东西.

再次更新

[In,Out]属性是多余的(这是默认值),但是将其放在那里会明确表示您需要In和Out复制.

[DllImport("myDLL.dll")]
private static extern int ChannelName(int x, int y, [In,Out] byte[] z);
public static int ChannelName(int x, int y, out string result)
{
    byte[] z = new byte[100];
    int ret = ChannelName(x, y, z);
    result = Encoding.ASCII.GetString(z);
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

再次更新

看起来(命名不好)'y'参数是传入的char*缓冲区的长度,我的猜测是它返回写入缓冲区的字符数.如果是这种情况,我会以更自然的C#方式包装此调用:

[DllImport("myDLL.dll")]
private static extern int ChannelName(int x, int y, [In, Out] byte[] z);

public static string ChannelName(int x)
{
    byte[] z = new byte[100];
    int length = ChannelName(x, z.Length, z);
    return Encoding.ASCII.GetString(z, 0, length);
}
Run Code Online (Sandbox Code Playgroud)