English 中文(简体)
Calling unmanaged function from C#: should I pass StringBuilder or use unsafe code?
原标题:

I ve got a C# program that needs to pass a char buffer to an unmanaged function. I ve found two ways that seem to work reliably, but I m not sure which I should choose.

Here s the unmanaged function s signature.

extern "C" __declspec(dllexport) int getNextResponse(char *buffer);

The first option is to define the buffer as a StringBuilder, as follows.

//at class level...
[DllImport("mydll.dll")]
static extern int getNextResponse(StringBuilder buffer);

//in main method body...
StringBuilder sb = new StringBuilder(" ", 65536);
int rc = getNextResponse(sb);

This is simple, and it works, and I think I basically understand why it works because the StringBuilder has a buffer behind the scenes, so (I assume) the interop layer is just marshalling the StringBuilder to a char *.

The other option is using unsafe code.

//at class level...
[DllImport("mydll.dll")]
static extern int getNextResponse(byte* buffer);

//separate method...
private static unsafe int runGetNextResponse(byte[] buffer)
{
    fixed (byte* p = buffer)
    {
        int rc = getNextResponse(p);
        return rc;
    }            
}

//in main method body...
byte[] b = new byte[65536];
int rc = runGetNextResponse(b);

The second approach is more code, but it s also more explicit about what s going on.

Are these two approaches doing basically the same thing? Is there any reason to choose one over the other?

最佳回答

I d strongly prefer using the StringBuilder version.

There s not going to be a huge difference between the two, and using unsafe code is not nearly as clean.

In my opinion, since there is a way to solve the problem using a core library class, using unsafe code without a clear (and needed) benefit is a premature optimization.

问题回答

While using a StringBuilder is preferred there s one caveat. Imagine for example that in your getNextResponse method you store the pointer to some static variable and use it in another method:

char* globalPointer;

int getNextResponse(char *buffer) {
    globalPointer = buffer;
    return 0;
}

void someOtherMethod() {
    printf("%s
", globalPointer);
}

Now let s look at the managed side:

var sb = new StringBuilder();
sb.Append("Hello World");
int result = getNextResponse(sb);
Console.WriteLine(result);
someOtherMethod(); // kaboom: The GC could have already destroyed the string builder.

The unsafe method guarantees you that the memory location won t be moved around:

byte[] buffer = Encoding.UTF8.GetBytes("Hello World");
fixed (byte* p = buffer)
{
    int result = getNextResponse(p);
    Console.WriteLine(result);
    someOtherMethod(); // works fine as the buffer address is pinned down in memory
}

In this case the unsafe version will work better.

While I can t weigh in definitively, I can share my own experiences. I have used the StringBuilder method exclusively and have had no problems with it. I like the simpler code of it and the avoidance of unsafe.

It depends on the cost of marshalling. If you do a lot of marshalling or the data being marshalled is large, you may want to reuse the buffer instead of creating/destroying the string builder buffer every time.

It depends on what the data in the buffer actually is. If it is character data use the StringBuilder approach. If it is binary data use a byte array instead, but don t use the unsafe method. While the exaggerated fear of unsafe that is getting common is a little silly and there is no reason not to use it when warranted, in this case it is unnecessary. Use:

//at class level...
[DllImport("mydll.dll")]
static extern int getNextResponse([In, Out] byte[] buffer);

//in main method body...
byte[] buffer = new byte[65536];
int rc = getNextResponse(buffer);




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签