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 thi
Here is a topic were string marshaling has been discussed.
It's need to mark parameter with attribute
[MarshalAs(UnmanagedType.LPSTR)]
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.
why not building your own text struct :
struct mystr { mystr(const char *_) : _text((_ == 0)
? 0
: ::strdup(_)
)
{}
~mystr() { if(this->_text != 0) delete [] this->_text; }
operator char * & () { return this->_text; }
private : char *_text;
};
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);
}
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.