[ACCEPTED]-Returning a string from PInvoke?-pinvoke

Accepted answer
Score: 21

First of all, as others have pointed out, your 46 C++ is broken even before trying interop. You 45 are returning a pointer to stri's buffer. But 44 because stri is destroyed as soon as the function 43 returns, the return value is not valid.

What's 42 more, even if you fixed this, you need to 41 do more. It won't work allocating memory 40 in your C++ code which you would need the 39 C# code to deallocate.

There are a few options 38 to do it right.

Your C# code can ask the 37 C++ code how long the string is. Then a 36 C# StringBuilder is created and allocated 35 to the appropriate size. Next the StringBuilder 34 object is passed to the C++ code and its 33 default marshalling is as a LPWSTR. In 32 this approach the C# code allocates the 31 string and your C++ code receives a C string 30 to which it must copy the buffer.

Alternatively 29 you can return a BSTR from the C++ which 28 allows allocation in the native C++ code 27 and deallocation in the C# code.

The BSTR 26 approach is probably how I would do it. It 25 looks like this:


#include <comutil.h>
BSTR GetSomeText()
    return ::SysAllocString(L"Greetings from the native world!");


[DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();


Hans Passant added a couple 24 of useful observations in the comments. First 23 of all, most P/Invoke interop is done against 22 an existing interface which cannot be changed 21 and you do not have the luxury of picking 20 your preferred interop interfacing approach. It 19 would appear that is not the case here, so 18 which approach should be chosen?

Option 1 17 is to allocate the buffer in the managed 16 code, after having first asked the native 15 code how much space is needed. Perhaps it 14 is enough to use a fixed size buffer that 13 both parties agree on.

Where option 1 falls 12 down is when assembling the string is expensive 11 and you don't want to do it twice (e.g. once 10 to return its length, and once again for 9 the contents). This is where option 2, the 8 BSTR comes into play.

Hans pointed out one drawback 7 of the BSTR, namely that it carries a UTF-16 6 payload but your source data may well char*, which 5 is a "bit of a hassle".

To overcome the hassle 4 you can wrap up the conversion from char* to 3 BSTR like this:

BSTR ANSItoBSTR(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 2 the way, and now it's easy to add other 1 wrappers to convert to BSTR from LPWSTR, std::string, std::wrstring etc.

Score: 1

Another way to get a string from C++. In 11 case the you cannot modify your c++ dll. You 10 can declare the DllImport with IntPtr instead 9 of string. When the function is invoked, you 8 can marshal the Ptr back to String.

private static extern IntPtr GetSomeText();
public static string GetAllValidProjects()
    string s = Marshal.PtrToStringAnsi(GetSomeText());
    return s;

Note 7 : as mention in the previous post. "Some 6 Text Here" is allocated on the stack so 5 as soon as the function returns, the stack 4 will unwire. Therefore the data is potential 3 be overridden. Hence you shall use Marshal.PtrToStringAnsi 2 right after the call. Don't hold to the 1 IntPtr.

char* GetSomeText()    
    std::string stri= "Some Text Here"; 
    char * pchr = (char *)stri.c_str();
    return pchr;
Score: 0

It is because

std::string stri= "Some Text Here";

is a stack object and it is 6 getting destroyed after GetSomeText() call. After 5 the call pchr pointer which you are returning 4 is invalid.

You may need to allocate space 3 for the text dynamically to be able to access 2 it latter.
Change your C++ function definition 1 to:

char* GetSomeText()    
   std::string stri = "Some Text Here";
   return strcpy(new char[stri.size()], stri.c_str());

Something like above. You got the idea...

More Related questions