2

I was given C code which I have compiled to a DLL. I then created a wrapper in VC++ that calls functions in this code. Everything up to this point works fine. I'm having trouble passing a C# stringbuilder (char*) to the C++ code from my C# code that uses this wrapper.

So far I have a wrapper class written in VC++ that has this function call:

void Wrapper::ReadStream(char* buffer, int* size)
{
    int req_sz;
    read_stream(opts, req_sz, req, buffer); //This calls the C DLL, this part works.
} 

I'm having a problem in my C# code while trying to call the C++ wrapper function ReadStream. After looking around for similar problems I found that in order to pass a String from C# to C++ (which will be looking for a char*) a stringbuilder should be passed, so I have tried:

C# Code:

...
Wrapper wrap = new Wrapper()
...

int bufferSize = 48;
StringBuilder buffer = new StringBuilder(bufferSize);
wrap.ReadStream(buffer, ref bufferSize);

But the code will not compile because C# sees the function definition as wanting an sbyte*. I've read that this is because C# chars are unicode (2 bytes), whereas C++ chars are single byte characters, but the suggested solution is to pass a StringBuilder.

I've seen a lot of solutions where the C# code passes a stringbuilder directly to the DLL, but cannot find one that will pass a stringbuilder to a C++ wrapper class.

sdm350
  • 397
  • 1
  • 6
  • 17

2 Answers2

1

You can pass the C# unicode string to std::wstring, then in c++ code you can convert the std::wstring to std::string and pass down to C-API.

Or you can use Encoding ASCII GetBytes and get the Byte[] in the C# code, pass the array to c++, convert it to a null terminated string and pass it to C-API. I will probably go for the second option.

Community
  • 1
  • 1
Alessandro Teruzzi
  • 3,918
  • 1
  • 27
  • 41
1

You can definitely pass a StringBuilder to the C++ side, just change the Wrapper::ReadStream signature to:

void Wrapper::ReadStream(LPWSTR buffer, int* size)

BTW, since you say that you have the C code, I'd suggest modifying it so that you can call it directly from C# (since C is a subset of C++, C code is often also C++ code, and you can add C++ features to it like classes). That way you'd avoid wrappers, which are always a pain.

While you're at it, I'd also suggest modifying it to use Unicode strings instead of ANSI strings (unless there's something that requires ANSI strings).

And finally, I suggest using COM to have the C# side and the C++ side talk to each other. Then the ReadStream method would be in an IDL interface like:

[object, uuid(2C5B0D36-0B34-4FEE-A0B8-B10F7A019A6D)]
interface ISomeObject : IUnknown
{
    HRESULT ReadStream([out, retval] BSTR* pbstrBuffer);
}

BSTR is COM's string type (it's Unicode). The C# side would see the following interface:

interface ISomeObject
{
    string ReadStream();
}

There's no need for a "size" parameter, because you're returning a new string, not filling in a passed-in buffer (which has the problem that the buffer may be too small to hold the result).

If you haven't used COM before, it can have a steep learning curve, but it's worth it if you're planning on doing much interop like this.

user1610015
  • 6,561
  • 2
  • 15
  • 18
  • Is there a certain library I need to import to use LPWSTR? – sdm350 Oct 24 '12 at 17:03
  • @Azkar LPWSTR is just a synonym for `const wchar_t*` (and it's defined in ``). – user1610015 Oct 24 '12 at 17:57
  • Gotcha. Thank you for your answer, I actually ended up going a different direction though... I'm not actually looking to fill the buffer with unicode characters, the data that I'm receiving over the network is just signed chars (one byte). I ended up send the C++ an sbyte buffer pointer which works for what I want to do. I considered the COM advice but just simply don't have the time to change that right now, and we have a couple of other wrappers that work this way. – sdm350 Oct 24 '12 at 18:04