问题
Assume this C function:
void do_something(const char* str)
It stores the string somewhere for later reference.
Furthermore, I have this signature in C# to call this function:
[DllImport("NativeLib")]
static extern void do_something(string str);
Now, what do I need to do when passing a string to this method:
- Do I need to pin the string (with
GCHandle.Alloc()
) (or is the marshaller creating a copy)? - If I do need to pin it, do I pass the "original" string (i.e. the string I passed to
GCHandle.Alloc()
)? Or do I need to pass the return value ofGCHandle.AddrOfPinnedObject()
? - Is
string
the correct data type (fordo_something
) in this case? Or should I useIntPtr
instead?
回答1:
Storing pointers through an interop boundary is a Really Bad Idea, do everything you can to modify the C code to make a copy of the string instead.
Pinning the string as suggested in the upvoted answer isn't going to work, the pinvoke marshaller must convert the string to char* and releases that converted string after making the native call, invalidating the pointer. You must marshal the string yourself. Declare the argument as IntPtr and use Marshal.StringToHGlobalAnsi() to create the pointer value.
Or use Marshal.StringToCoTaskMemAnsi(), it allocates from the COM heap. Which is your real problem, there is no great scenario to release the string. The C code cannot release the string because it doesn't know how it was allocated. Your C# code cannot release the string because it doesn't know when the C code is done with the pointer. This is why copying the string is so important. You can live with the memory leak if you make this call only a few times.
回答2:
Given that the unmanaged code will be storing a pointer rather than copying the string, I recommend that you use GCHandle.Alloc() to manually create a copy of the string and pass it as an IntPtr.
You will then be able to manage the lifetime of the passed block of memory, only releasing it (via the GCHandle) when you are done with it.
来源:https://stackoverflow.com/questions/10977231/what-to-pass-for-pinned-string-in-p-invoke