Returning a string from PInvoke?
First of all, as others have pointed out, your C++ is broken even before trying interop. You are returning a pointer to stri
's buffer. But because stri
is destroyed as soon as the function returns, the return value is not valid.
What's more, even if you fixed this, you need to do more. It won't work allocating memory in your C++ code which you would need the C# code to deallocate.
There are a few options to do it right.
Your C# code can ask the C++ code how long the string is. Then a C# StringBuilder is created and allocated to the appropriate size. Next the StringBuilder object is passed to the C++ code and its default marshalling is as a LPWSTR. In this approach the C# code allocates the string and your C++ code receives a C string to which it must copy the buffer.
Alternatively you can return a BSTR from the C++ which allows allocation in the native C++ code and deallocation in the C# code.
The BSTR approach is probably how I would do it. It looks like this:
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();
Update
Hans Passant added a couple of useful observations in the comments. First of all, most P/Invoke interop is done against an existing interface which cannot be changed and you do not have the luxury of picking your preferred interop interfacing approach. It would appear that is not the case here, so which approach should be chosen?
Option 1 is to allocate the buffer in the managed code, after having first asked the native code how much space is needed. Perhaps it is enough to use a fixed size buffer that both parties agree on.
Where option 1 falls down is when assembling the string is expensive and you don't want to do it twice (e.g. once to return its length, and once again for the contents). This is where option 2, the BSTR
comes into play.
Hans pointed out one drawback of the BSTR
, namely that it carries a UTF-16 payload but your source data may well char*
, which is a "bit of a hassle".
To overcome the hassle you can wrap up the conversion from char*
to 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 the way, and now it's easy to add other wrappers to convert to BSTR
from LPWSTR
, std::string
, std::wrstring
etc.
How to return string array using Pinvoke
Without more information about your specific case, it's impossible to say how you'd marshal the return value. Perhaps this page of interop marshaling samples will help: http://msdn.microsoft.com/en-us/library/e765dyyy.aspx
The above link no longer works. Try Marshaling Data with Platform Invoke.
Why can't I return a char* string from C++ to C# in a Release build?
Or maybe try to use
[DllImport("SimpleDll")]
public static extern IntPtr ReturnString();
and in your calling code, use the Marshal Class
string ret = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(PInvoke.ReturnString());
How to return std::wstring from C++ to C# using PInvoke without writing stringbuffer conversion
In your Node.cpp
file, use SysAllocString
from oleauto.h
(or include Windows.h
) to allocate the string for you like this:
MYAPI BSTR GetNodeName(NativeCore::Node* obj)
{
if (obj != NULL)
return SysAllocString(obj->GetName().c_str());
return NULL;
}
Then adjust your native method wrapper to use UnmanagedType.BStr
instead of UnmanagedType.LPWStr
:
class UnMangedWrapper
{
[DllImport("NativeCore.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GetNodeName(IntPtr ptr);
}
Using BSTR
has the advantage that you don't have to call into the unmanaged code twice (once for the buffer length and another time for the actual string content) and the marshaller can automatically take care of deallocating the unmanaged string.
Return string from unmanaged dll to C#
I have reached an answer, which I will record here for completeness, with grateful thanks to all those who pointed me in the right direction.
According to this post elsewhere, the use of .NET Reflector on similar VB code suggests the need to use the string
type in place of my StringBuilder
, as suggested here by Alex Mendez, JamieSee and Austin Salonen, together with explicit marshaling, as suggested by Nanhydrin, but utilising the unmanaged type VBByRefStr
rather than AnsiBStr
. The final key to the puzzle is that the string parameter then needs to be passed by reference using the ref
keyword.
I can confirm that this works, and that my final working C# code is therefore:
[DllImport("unmanaged.dll", CharSet = CharSet.Ansi)]
public static extern short DeviceSendRead(
[MarshalAs(UnmanagedType.VBByRefStr)] ref string sCommand,
[MarshalAs(UnmanagedType.VBByRefStr)] ref string sReply,
[MarshalAs(UnmanagedType.VBByRefStr)] ref string sError,
double Timeout);
short err;
string outstr = txtSend.Text;
string readstr = new string(' ', 4000);
string errstr = new string(' ', 100);
err = DeviceSendRead(ref outstr, ref readstr, ref errstr, 10);
I hope this is useful to others facing a similar issue.
pinvoke to function which returns string array
The C++ code increments the pointer like this:
++results;
That increments the address by sizeof(*results)
because that is how C++ pointer arithmetic works. So, suppose that sizeof(*results)
is equal to 4, as would be the case on a 32 bit machine. Then ++results
will increment the address by 4.
Now, your C# code is different. The pointer is untyped and the compiler knows nothing about the underlying array element type. So your code
arrayPtr = new IntPtr(arrayPtr.ToInt64() + 1);
increments the address by 1. Instead you need to supply the missing type information. Like this:
arrayPtr = new IntPtr(arrayPtr.ToInt64() + IntPtr.Size);
On top of that, your loop is implemented incorrectly. You fail to update ptr
at the correct time. It should be:
public static IEnumerable<string> MarshalStringArray(IntPtr arrayPtr)
{
if (arrayPtr != IntPtr.Zero)
{
IntPtr ptr = Marshal.ReadIntPtr(arrayPtr);
while (ptr != IntPtr.Zero)
{
string key = Marshal.PtrToStringAnsi(ptr);
yield return key;
arrayPtr = new IntPtr(arrayPtr.ToInt64() + IntPtr.Size);
ptr = Marshal.ReadIntPtr(arrayPtr);
}
}
}
You might prefer to re-cast the method so that the code that it contains only a single call to Marshal.ReadIntPtr
.
One final point. The C++ function looks like it might be using the cdecl
calling convention. You should check what the definition of SWDLLEXPORT
is. Your p/invoke is only correct if SWDLLEXPORT
specifies __stdcall
.
PInvoke for C function that returns char *
You must return this as an IntPtr. Returning a System.String type from a PInvoke function requires great care. The CLR must transfer the memory from the native representation into the managed one. This is an easy and predictable operation.
The problem though comes with what to do with the native memory that was returned from foo()
. The CLR assumes the following two items about a PInvoke function which directly returns the string type
- The native memory needs to be freed
- The native memory was allocated with CoTaskMemAlloc
Therefore it will marshal the string and then call CoTaskMemFree(...)
on the native memory blob. Unless you actually allocated this memory with CoTaskMemAlloc this will at best cause a crash in your application.
In order to get the correct semantics here you must return an IntPtr directly. Then use Marshal.PtrToString* in order to get to a managed String value. You may still need to free the native memory but that will dependent upon the implementation of foo.
C# pinvoke of C function returning char*
You cannot return a pointer to a local variable since those are allocated on the stack (and the stack may get destroyed when the function returns).
You'd need to allocate memory on the heap (preferably using CoTaskMemAlloc
so the CLR can free up the memory later) on your C function and return a pointer to that memory.
As a side note, you can directly return a string
when marshalling (assuming it's a c null-terminated unicode string), no need to return IntPtr
.
More info here: http://limbioliong.wordpress.com/2011/06/16/returning-strings-from-a-c-api/
Related Topics
Getting Selected Value of a Combobox
Multiple Insert Statements in One Connection
Getting The Ip Address of Server in Asp.Net
Finding the Concrete Type Behind an Interface Instance
Enter Key Pressed Event Handler
Insert into SQL Db a String That Contain Special Character '
Calling an Overridden Method from a Parent Class Ctor
How to Add Attributes for C# Xml Serialization
Swift: Convert Nsdate to C# Ticks
Openssl.Net Porting a Ruby Example to C# (From Railscasts 143 Paypal-Security)
C# - How to Prevent Mousewheel-Scrolling in My Combobox
How to Display a Loading Control While a Process Is Waiting for Be Finished
Ef5 Code First - Changing a Column Type with Migrations
Copying One Azure Blob to Another Blob in Azure Storage Client 2.0
Is the Destructor Called If the Constructor Throws an Exception
Best Practice: Direct SQL Access VS. Web Service
Perform Button Click Event When User Press Enter Key in Textbox