Passing StringBuilder to DLL function expecting ch

2019-02-24 21:45发布

I'm trying to interact with a DLL library created in Delphi. In C++, I made this call perfectly fine:

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

Where channelName is type defined as typedef int (CALLBACK* ChannelName)(int,int,char*);

Now I'm trying to do the same thing in C#. I've searched and found that StringBuilder is commonly used as a char pointer for DLL functions. Here is how I declared my function:

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

And here is how I'm trying to call it:

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

This is just making the console print out jibberish, for example:

☺ §
☺ î☺8f9
☺ î☺8f9
☺ î☺8f9
☺ î☺8f9
☺ î☺8f9
☺ î☺8f9
☺ î☺8f9
☺ î☺8f9
☺ î☺8f9
☺ î☺8f9
☺ î☺8f9

I remember running into a similar problem in C++ which is why I memset the memory that I malloc to 0. I tried to find an equivalent in C#, but maybe it's an issue with the StringBuilder instead? In case my question isn't very clear, I just want to be able to pass a string into my function, have the function fill it up, and then print it out. Strings are immutable in C# and no good pointer options exist which is why I'm trying StringBuilder.

1条回答
SAY GOODBYE
2楼-- · 2019-02-24 22:22

In .NET, strings (and StringBuilders) are 16-bit Unicode characters. My guess is that you native function deals in 8-bit ASCII characters. You need to tell the Marshaller how to convert the characters when marshalling them. Change your DllImport attribute like so:

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

Updated

Also you should specify the [Out] attribute on the StringBuilder so that the Marshaller only marshals on the way out as you are passing nothing on the way in.

Updated Again

The [In,Out] attribute is redundant (that's the default), however putting it there makes it explicit that you know you desire both In and Out copying.

[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;
}

Updated Again

It looks like the (poorly named) 'y' parameter is the length of the char * buffer passed in and my guess is that it returns the number of characters written into the buffer. If that is the case, I would wrap this invocation in a more natural C# way:

[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);
}
查看更多
登录 后发表回答