I am using Pinvoke for Interoperability between Native(C++) code and Managed(C#) code. What i want to achieve is get some text from native code into my managed code. For this i try lot lot of things,e.g passing string/stringbuilder by ref, using [IN] and [OUT], Marshaling to LPSTR, returning string from function etc. but nothing works in my case. Any help with some small code would be highly appreciated.
5 Answers
I'd do it with a BSTR since it means you don't have to call into native twice per string, once to get the length and then once to get the contents.
With a BSTR the marshaller will take care of deallocating the BSTR with the right memory manager so you can safely pass it out of your C++ code.
C++
#include <comutil.h>
BSTR GetSomeText()
{
return ::SysAllocString(L"Greetings from the native world!");
}
C#
[DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();
There is one minor drawback of the BSTR, namely that it carries a UTF-16 payload but your source data may well be char*.
To overcome this you can wrap up the conversion from char* to BSTR like this:
BSTR ANSItoBSTR(const char* input)
{
BSTR result = NULL;
int lenA = lstrlenA(input);
int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0);
if (lenW > 0)
{
result = ::SysAllocStringLen(0, lenW);
::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW);
}
return result;
}
That's the hardest one out of the way, and now it's easy to add other wrappers to convert to BSTR from LPWSTR, std::string, std::wstring etc.
5 Comments
-1 as the fourth argument to MultiByteToWideChar to have the function calculate the length of the string automatically; avoids the need to call lstrlenA yourself.char* says nothing about the encoding of the string; if it's an UTF-8 encoded string, consider using CP_UTF8 instead of CP_ACP.Here is a topic where string marshaling has been discussed.
It's needed to mark parameter with attribute
[MarshalAs(UnmanagedType.LPSTR)]
2 Comments
Here is an example of doing this through C#. I am calling Native function GetWindowText through C# by pInvoking. GetWindowText returns the caption of the window whose handle is passed to it.
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
public static string GetText(IntPtr hWnd)
{
// Allocate correct string length first
int length = GetWindowTextLength(hWnd);
StringBuilder sb = new StringBuilder(length + 1);
GetWindowText(hWnd, sb, sb.Capacity);
return sb.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
string str = GetText(this.Handle);
}
Comments
If you're returning a char *, and don't want to have to modify the C/C++ code to allocate memory for your return value (or you can't modify that code), then you can change your C# extern function-prototype to return an IntPtr, and do the marshaling yourself.
For instance, here's a snippet of the interop I wrote for Pocketsphinx:
[DllImport("sphinxbase.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr /* char const * */ jsgf_grammar_name(IntPtr /* jsgf_t * */ jsgf);
And here's the get-accessor for the C# JsgfGrammar class. m_pU is an IntPtr that points to the raw jsgf_t object.
public string Name
{
get
{
IntPtr pU = jsgf_grammar_name(m_pU);
if (pU == IntPtr.Zero)
strName = null;
else
strName = Marshal.PtrToStringAnsi(pU);
return strName;
}
}
Modifying this example for other string formats (e.g. Unicode) should be trivial.
10 Comments
PtrToStringAnsi() then forgets the IntPtr, leaving the native code as the sole owner. Said native code never gets informed that it can release the resource. This is called a "memory leak".