Returning a string from PInvoke? [duplicate] - c#

This question already has answers here:
Marshal "char *" in C#
(2 answers)
Closed 4 years ago.
I am using PInvoke for interoperability between Native Code (C++) and Managed Code (C#).
I just write a simple function which gets a string from C++ code. My code looks like
C# Code:
[DllImport("MyDll.dll")]
private static extern string GetSomeText();
public static string GetAllValidProjects() {
string s = GetSomeText();
return s;
}
C++ Code
char* GetSomeText() {
std::string stri= "Some Text Here";
char * pchr = (char *)stri.c_str();
return pchr;
}
All works fine at C++ end, i.e the variable pchr contains "Some Text Here" but at C# the string s contains noting in it. I don't know what I am doing wrong. Any help would be appreciated

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.

Another way to get a string from C++. In case the you cannot modify your c++ dll. You can declare the DllImport with IntPtr instead of string. When the function is invoked, you can marshal the Ptr back to String.
[DllImport("MyDll.dll")]
private static extern IntPtr GetSomeText();
public static string GetAllValidProjects()
{
string s = Marshal.PtrToStringAnsi(GetSomeText());
return s;
}
Note : as mention in the previous post. "Some Text Here" is allocated on the stack so as soon as the function returns, the stack will unwire. Therefore the data is potential be overridden. Hence you shall use Marshal.PtrToStringAnsi right after the call. Don't hold to the IntPtr.
char* GetSomeText()
{
std::string stri= "Some Text Here";
char * pchr = (char *)stri.c_str();
return pchr;
}

It is because
std::string stri= "Some Text Here";
is a stack object and it is getting destroyed after GetSomeText() call. After the call pchr pointer which you are returning is invalid.
You may need to allocate space for the text dynamically to be able to access it latter.
Change your C++ function definition 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...

GetSomeText() is returning pointer of char is wont work if u declare string variable to store it.
try
[DllImport("MyDll.dll")]
private static extern string GetSomeText();
public static string GetAllValidProjects()
{
string s = new string(GetSomeText());
return s;
}
there's a constructor that takes char*

Related

Passing char* buffer to C# as a StringBuilder

I have a C++ function that is called with a char* buffer to be used for the output. Now i want to write to it using C# so i need to call a managed method from the C++ function. What i can't figure out is how to pass it as a StringBuilder.
I previously used Robert Giesecke's Unmanaged Exports which worked fine and did this automatically using a default string marshalling scheme, but i want to use the size parameter for the MaxCapacity of the StringBuilder.
Is there a nicer way to do this than creating a new StringBuilder instance, writing to it, getting a CLR string with ToString(), and then copying the contents to the buffer? The strings in question might be as long as 10,000 characters and i don't really like the idea of copying it twice each time.
C#
public static void MyMethod(StringBuilder buffer)
{
//...
}
C++/CLI
extern "C" __declspec(dllexport)
void __stdcall MyFunction(char* buffer, int length)
{
MyNamespace::MyClass::MyMethod( /* ? */ );
}
Use a byte [] then c++ 8 bit character array will align correctly.
I prefer to do something similar to this because I use it for unmanaged code:
String ^ ToManagedString(const char * pString) {
return Marshal::PtrToStringAnsi(IntPtr((char *)pString)); // Marshal pString into Managed Memory. return as a C# string reference (^).
}
const std::string ToStdString(String ^ strString) {
IntPtr ptrString = IntPtr::Zero;
std::string strStdString;
try {
ptrString = Marshal::StringToHGlobalAnsi(strString); // Marshal a C# String reference into unmanaged HEAP memory space.
strStdString = (char *)ptrString.ToPointer(); // Convert C# IntPtr to char* implicitly, call assignment operator of std::string to copy.
}
finally {
if (ptrString != IntPtr::Zero) {
Marshal::FreeHGlobal(ptrString);
}
}
return strStdString; // return std::string copied out of interop unmanaged heap space
}

Passing a class containing a StringBuilder from C# to ada

I'm working in Unity C#, trying to integrate with a dll written in ada. I need to pass a class containing strings and doubles to the dll for it to modify and return, but I get the following error:
"Type System.Text.StringBuilder which is passed to unmanaged code must have a StructLayout attribute."
Now, these classes already have a StructLayoutAttribute. And I can't put a StructLayout attribute on a Stringbuilder, Unity tells me it only works on classes and structs. Which makes me really confused about the error message, because it seems like it's asking me for something that's impossible?
public class controller : MonoBehavior{
myClass student = new myClass();
[DllImport ("my_dll", EntryPoint="myFunction#12")]
public static extern void myFunction(myClass c);
void Start(){
myFunction (student);
}
}
[StructLayoutAttribute(LayoutKind.Sequential, size=2)]
public class myClass {
public double height = 0.0;
public System.Text.StringBuilder name = new System.Text.StringBuilder(16);
}
IIRC C# marshals StringBuilder as char* by default when using PlatformInvoke features. This is handy for interfacing with C call convention DLLs. Some Ada compilers (especially the GCC one) might allow Ada libraries to be optimised for C call conventions. The "almost magical" nature of this may in fact be true. :)
My suggestion: surround your C# call to the Ada functions with an unsafe{} block and fixed() your char array, then pass it to your Ada function, then drop out of the fixed block again after it returns.
I don't have a working copy of Visual Studio so I can't verify this.
(Edited to fix incorrect terminology)
Something about this definitely feels off. If your Ada DLL knows how to handle a System.Text.StringBuilder then that seems almost magical. You generally need to pass primitive types around when dealing with native libraries. Also, this Ada DLL is possibly not a native dll, which is what Mono is expecting to work with. See Interop With Native Libraries for more info on marshaling data.
Now lets assume that your Ada DLL is actually a native DLL. And it somehow knows what to do with a StringBuilder. You can possibly pass a void pointer to the memory address via IntPtr. But I highly doubt this would work. Most likely what you want to do is pass the actual string through:
public class controller : MonoBehavior{
myClass student = new myClass();
[DllImport ("my_dll", EntryPoint="myFunction#12")]
public static extern void myFunction(myClass c);
void Start(){
student.name = "John Doe";
myFunction (student);
}
}
[StructLayoutAttribute(LayoutKind.Sequential, size=2)]
public class myClass {
//The following MarshalAs may be unnecessary.
[MarshalAs (UnmanagedType.LPStr)]
public string name;
public double height = 0.0;
}
LPStr is a 'long pointer to a string'. The assumption is your Ada DLL will know what to do with that. If that doesn't work, then you can remove the MarshalAs and see if it likes it better that way.
This C function will take an input str that must be less than ~250 characters and returns a modified string. You'll see that it mallocs a string the size of sprintf's modified string and then strcopy's it. There is no corresponding free() because it will be freed by mono as mentioned here.
char* myFunction(char* input, double height)
{
char buffer[256];
int n = sprintf(buffer, "%s -- %f", input, height);
char* ret = (char*)malloc(n+1); // +1 for the newline
strcopy(ret, buffer);
return ret;
}

Calling C++ code from C# error using references in c++

In my C++ dll named "scandll.dll" I have the following function
extern "C" __declspec(dllexport) void scanfile(char * returnstring)
{
strcpy(returnstring, "return string");
}
in my C# code I'm doing this
[DllImport("scandll.dll", CharSet = CharSet.Ansi, SetLastError = true )]
public static extern int scanfile(ref IntPtr strReturn);
and this is the method that I'm using to get the value from the dll
public void Scan()
{
string filename = "";
IntPtr ptr = new IntPtr();
scanfile(ref ptr);//Here i get the error
filename = Marshal.PtrToStringAnsi(ptr);
}
I get an exception thrown as "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I'm following this link
Calling C++ code from C# error using references in c++ ref in c#
Any help would be appreciated.
Thanks
Your C++ code is wrong - all it's doing is changing the value of the pointer which has been passed to it to point to a constant string. The caller knows nothing about that change.
It's the same as if you'd done this:
void MyCfunction(int number)
{
number = 3;
}
and then hoped that a caller of that function would somehow see the number '3' anywhere.
You could do something like this in C++:
void MyCFunction(char* pReturnString)
{
strcpy(pReturnString, "Hello, world");
}
but you also need to make sure on the C# side that you had provided a buffer with pReturnString pointing to it. One way to do this by using a StringBuilder, and then having your C# declaration of the C function take a parameter like this:
[MarshalAs(UnmanagedType.LPStr)] StringBuilder returnString
You need to reserve space in the StringBuilder before calling into the C++ function.
In real life, C/C++ functions like this pretty much always take an additional length parameter, which allows the caller to tell the callee the maximum length of string it's allowed to copy into the buffer. There is no consistency of convention about whether the length includes the terminating \0 or not, so people are often careful about running right up to the end of the buffer.
memcpy ( destination, * source, size_t num );
Try this to assign the value to returnstring = "rturn name";

How to specify whether to take ownership of the marshalled string or not?

suppose I have x.dll in C++ which looks like this
MYDLLEXPORT
const char* f1()
{
return "Hello";
}
MYDLLEXPORT
const char* f2()
{
char* p = new char[20];
strcpy(p, "Hello");
return p;
}
Now, suppose I want to use this in C#
[DllImport("x.dll")]
public static extern string f1();
[DllImport("x.dll")]
public static extern string f2();
Is there any way to tell CLR to take strong ownership of the string returned from f2, but not f1? The thing is that the fact that the string returned from f1 will eventually be freed, deleted, or whatever by GC is equally bad with the fact that the string returned from f2 won't. Hope the question was clear. Thanks in advance
If you have any influence at all over the dll implementation, then I strongly suggest you simply don't do it like you showed in your example. Otherwise, please refine the question to mention that constraint.
If you have to return a heap allocated string from the dll, then you should also provide a cleanup function (always good practice when exporting dynamically allocated memory from a dll). You P/Invoke the allocating function with a return of IntPtr and marshal that with one of the Marshal.PtrToString... at http://msdn.microsoft.com/en-us/library/atxe881w.aspx and finish off by calling the cleanup function for the native side of things.
Another way is to use BSTR (example from Marshaling BSTRs in COM/Interop or P/Invoke):
Native:
__declspec(dllexport)
void bstrtest(BSTR *x)
{
*x = SysAllocString(L"Something");
}
Managed:
[DllImport("mydll.dll")]
extern static void bstrtest(ref IntPtr dummy);
static void Main(string[] args)
{
var bstr = IntPtr.Zero;
bstrtest(ref bstr);
var text = Marshal.PtrToStringBSTR(bstr);
Console.WriteLine(text);
Marshal.FreeBSTR(bstr);
}
I just found a similar question on SO: PInvoke for C function that returns char *

Marshalling string from c# to c++

I am new in microsoft world.
I have lot of problem trying to pass a simple string from c# to dll/c++
I have read a lot of post and documentation about but the problem is the same.
C++ code
extern "C" __declspec(dllexport) int Init( long l , char* url );
C# code
[DllImport("MCRenderer.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)]
public static extern int Init(long a, StringBuilder url);
Init(hndl.ToInt64(), str );
what haeppen is that long value is passed correctly while string parameter is
0x00000000 <Bad Ptr>
can you help me ... Iam really confused
thanks!!
AG
You should pass a string, url should be of type string and not StringBuilder.
It's because you're marshalling incorrectly: long and int in C++ are both (usually) int in C#.
From MSDN, you need to:
The only caveat is that the StringBuilder must be allocated enough space for the return value, or the text will overflow, causing an exception to be thrown by P/Invoke
Also from Marshaling between Managed and Unmanaged Code
Don't pass StringBuilder by reference (using out or ref). Otherwise, the CLR will expect the signature of this argument to be wchar_t ** instead of wchar_t *, and it won't be able to pin StringBuilder's internal buffer. Performance will be significantly degraded.
Use StringBuilder when the unmanaged code is using Unicode. Otherwise, the CLR will have to make a copy of the string and convert it between Unicode and ANSI, thus degrading performance. Usually you should marshal StringBuilder as LPARRAY of Unicode characters or as LPWSTR.
Always specify the capacity of StringBuilder in advance and make sure the capacity is big enough to hold the buffer. The best practice on the unmanaged code side is to accept the size of the string buffer as an argument to avoid buffer overruns. In COM, you can also use size_is in IDL to specify the size.
So in your example, you need to specify the native size as the parameter of the StringBuilder like this StringBuilder str = new StringBuilder(SIZE_OF_NATIVE_STRING);
Try using LPCSTR in the dll function parameter
extern "C" __declspec(dllexport) int Init( long l , **LPCTSTR** url );
Here is a good example that I found on how to do this.
C++:
extern "C" __declspec(dllexport) void doSomething(LPCTSTR asString)
{
std::cout << "here" << (wchar_t)asString << endl;
system ("pause");
}
C#:
class Program
{
static void Main(string[] args)
{
String myString = "String in C#";
doSomething(myString);
}
private const String path = #"C:\testdll2.dll"; //Make sure that the DLL is in this location
[DllImport(path)]
private static extern void doSomething(string asString);
}

Categories