34

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.

Daniel Grillo
  • 2,368
  • 4
  • 37
  • 62
Jame
  • 21,150
  • 37
  • 80
  • 107

5 Answers5

48

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.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • @rturrado Many thanks indeed for your corrections in the edit. – David Heffernan Jun 16 '11 at 19:29
  • the answer is very helpful, so it deserves it. BTW, just realising that 'may well char*' is missing a 'be'. – rturrado Jun 16 '11 at 22:50
  • You can pass `-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. – Frerich Raabe Aug 09 '11 at 11:42
  • 1
    The source data type `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`. – Frerich Raabe Aug 09 '11 at 11:45
  • @Frerich That is a good point. My answer was written under the assumption that text on Windows is almost always encoded using the local ANSI codepage, or UTF-16. UTF-8 is quite rarely seen in Windows. – David Heffernan Aug 09 '11 at 11:47
5

Here is a topic where string marshaling has been discussed.

It's needed to mark parameter with attribute

[MarshalAs(UnmanagedType.LPSTR)]
Pang
  • 9,564
  • 146
  • 81
  • 122
Disposer
  • 667
  • 1
  • 7
  • 23
4

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);
    }
Aamir
  • 14,882
  • 6
  • 45
  • 69
1

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.

ulatekh
  • 1,311
  • 1
  • 14
  • 19
-1

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;
             };