How to pass string parameters between C++ and C#?

Deadly 提交于 2020-01-23 17:55:07

问题


I have imported a C++ library in my C# project. I can get the answer of a C++ method that has no parameters. What I want to do is to send some string parameters to C++ and get the result of it.

In C++:

public String SomeMethod(String param1, String param2)
{
     //Some code
}

In C#:

[DllImport("Library Path")]
public static extern string SomeMethod(string param1, string param2);

How can I do this?


回答1:


For strings that are input parameters passed from C# to C++, you could just use LPCWSTR (i.e. const wchar_t*) at the interface on the C++ side, and marshal using the UnmanagedType.LPWstr option on the C# side.

But you must pay attention when you want to return strings from C++ to C#.
In fact, the string must somehow be allocated in C++, then the string pointer must be passed to C#. C# and the CLR must be able to properly release this string when it's no needed anymore, and this point can present issues. For example: C#/CLR should be able to release the string memory using the same allocator used by the C++ code to allocate the string.

This problem of using the same memory allocator for strings created in C++ and consumed in C# can be solved using BSTRs.
A BSTR is a COM string, that is allocated using the COM allocator, and can be freed using the same COM allocator. So, both the C++ and the C# side can share the same memory allocator for the string.

To return a BSTR, you can use the MarshalAs.UnmanagedTypeBStr option.

More details on string marshaling in C# can be found in this MSDN page.

In C++:

//
// Note: Export using a pure C interface from C++ DLL.
// (You can use C++ *inside* the DLL boundaries.)
// __stdcall is a widely used calling convention for 
// C DLL functions (e.g. lots of Win32 APIs use it).
//
extern "C" BSTR __stdcall GetSomeString(void)
{
    //
    // Note: BSTRs are allocated using SysAllocString()
    // (...and freed using SysFreeString()).
    //
    // You could also use a convenient ATL RAII wrapper
    // (instead of using raw BSTRs inside C++ code), 
    // like CComBSTR.
    //
    return ::SysAllocString(L"Hello from C++!");
}

In C#:

[DllImport(@"YourDll.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeString();

If you choose to have BSTRs as the returned string, for coherence of the C++ function interface code, you may also want to use BSTRs as the input strings (even if that is not strictly necessary).


Another option you have is to request the caller to provide a properly sized buffer that the native C++ function will fill with the returned string. This is somewhat similar to what several Win32 APIs (like e.g. GetWindowText()) do.
As an example, here you can find the corresponding P/Invoke for GetWindwoText():

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

However, I think that returning a BSTR is nicer from a C# calling perspective (in fact, e.g. on the C# side you are not required to use a StringBuilder for the output string, etc.).


Last but not least, another option that you might be interested in is to build a tiny C++/CLI bridging layer to expose your native C++ code to C#, and marshal strings between native and managed using C++/CLI (and e.g. the System::String class).



来源:https://stackoverflow.com/questions/28061637/how-to-pass-string-parameters-between-c-and-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!