问题
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 BSTR
s.
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 BSTR
s as the returned string, for coherence of the C++ function interface code, you may also want to use BSTR
s 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