Trying to get strings from managed code back to C++ - c#

(sorry if this has been asked before, but most of the examples that I've seen are passing data from managed -> native, not the other way around).
Short question: How do I fetch a string from the managed world from within native C++ code?
Long question + background:
I'm working with some legacy C++ code that formerly had the capability to get and set name/value string pairs (to greatly simplify the design). I wanted to move that name/value pair mechanism up into C# managed code with the rest of our application, so I put in function callbacks in the C++ world that call up into the managed code for getting and setting. The C++ function pointer types are as follows:
typedef int (GetConfigParamCallback)(const char* paramName, char* value);
typedef GetConfigParamCallback* LPGetConfigParamCallback;
typedef int (SetConfigParamCallback)(const char* paramName, const char* value);
typedef SetConfigParamCallback* LPSetConfigParamCallback;
As you can see, the tricky one is the get, where I want to provide memory to the caller to fill up. This will be the managed code moving forward.
These callbacks are represented by delegates in the C# world like so:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int Native_GetConfigParamCallBackMethodDelegate(
string paramName, StringBuilder paramValue);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int Native_SetConfigParamCallBackMethodDelegate(
string paramName, string paramValue);
And then my GetConfig wrapper function in the managed code that acquires the values looks like this (and puts the correct value into paramValue as seen by my debugging):
static int GetConfigParamCallBackWrapper(
string paramName,
System.Text.StringBuilder paramValue)
{
string valueTemp = // Fetch the string value here
if (valueTemp == null)
{
return 0;
}
paramValue.Append(valueTemp);
return 1;
}
So when the managed C# starts up, it sets these callback functions in the native world. Then, I have the native code run a series of what amounts to unit test methods that are getting and setting these strings. Now, on desktop this works fine, but when I try to run this on iOS with a Xamarin built app, the string comes back to the native world as garbage, AFAICT.
I’ve tried doing manual marshaling with IntPtr as well, and also no luck.

You can marshal the string as a BSTR:
C++:
typedef int (GetConfigParamCallback)(const char* paramName, BSTR* value);
typedef GetConfigParamCallback* LPGetConfigParamCallback;
// Usage:
LPGetConfigParamCallback pCallback = // ...
CComBSTR value;
int result = (*pCallback)("...", &value);
C#:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int Native_GetConfigParamCallBackMethodDelegate(
string paramName,
[MarshalAs(UnmanagedType.BStr)] ref string paramValue
);
static int GetConfigParamCallBackWrapper(
string paramName,
ref string paramValue
)
{
string valueTemp = // Fetch the string value here
if (valueTemp == null)
{
return 0;
}
paramValue = valueTemp;
return 1;
}

So I was able to find a slightly less pretty, but valid solution, thanks to this excellent blog post: http://randomencoding.tumblr.com/post/48564128118/returning-a-string-from-a-c-callback-to-c.
I used an IntPtr and did manual Marshaling:
static int GetConfigParamCallBackWrapper(
string paramName,
IntPtr paramValue)
{
string valueTemp = // Acquire valueTemp here.
IntPtr sPtr = Marshal.StringToHGlobalAnsi(valueTemp);
try
{
// Create a byte array to receive the bytes of the unmanaged string
var sBytes = new byte[valueTemp.Length + 1];
// Copy the the bytes in the unmanaged string into the byte array
Marshal.Copy(sPtr, sBytes, 0, valueTemp.Length);
// Copy the bytes from the byte array into the buffer passed into this callback
Marshal.Copy(sBytes, 0, paramValue, sBytes.Length);
// Free the unmanaged string
}
finally
{
Marshal.FreeHGlobal(sPtr);
}

Related

Reading a stream from native lib to C#

I have the following native c++ function:
// Decode binary format from file 'filename' into stream 'output'
bool read_private_format(const char * filename, std::ostringstream & output);
Reading previous post on SO on StringBuilder and delegate, I have created an intermediate C function to be exposed to the C# layer as:
extern "C" {
typedef char *(*StringBuilderCallback)(int len);
__attribute__ ((visibility ("default")))
bool c_read_private_format(const char * filename, StringBuilderCallback ensureCapacity, char *out, int len) {
std::ostringstream oss;
if( read_private_format(filename, oss) ) {
const std::string str = oss.str();
if( str.size() > len )
out = ensureCapacity(str.size());
strcpy(out, str.c_str());
return true;
}
return false;
}
}
while on the C# side:
private delegate System.Text.StringBuilder StringBuilderEnsureCapacity(int capacity);
[System.Runtime.InteropServices.DllImport(NativeLibraryName, EntryPoint="c_read_private_format")]
private static extern bool c_read_private_format(string filename, System.IntPtr aCallback, System.Text.StringBuilder data, int size);
private static System.Text.StringBuilder callback(int capacity)
{
buffer.EnsureCapacity( capacity );
return buffer;
}
public static string readIntoString(string filename) {
StringBuilderEnsureCapacity del = new StringBuilderEnsureCapacity(callback);
System.IntPtr ptr = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(del)
if( c_read_private_format( ptr, buffer, buffer.Capacity ) ) {
string str = buffer.ToString();
return str;
}
return null;
}
For some reason this is not working as expected, when printing the adress of the char* as returned by callback it acts as if the pointer returned was the one before the call to EnsureCapacity (I can verify by doing a second call, in which case the char* in the C layer is different).
My questions is:
How can I efficiently retrieve a UTF-8 string from C in .NET SDK (5.0.202) ?
I do not know in advance how long the string will be. Technically I could overestimate the StringBuilder Capacity so that I can re-use across my files, but it feels as if there could a better approach to passing a growing stream to the c layer.
There is no point in trying to optimize the posted code since by definition the pinvoke layer is missing the most important point:
❌ AVOID StringBuilder parameters. StringBuilder marshaling always
creates a native buffer copy. As such, it can be extremely
inefficient.
https://learn.microsoft.com/en-us/dotnet/standard/native-interop/best-practices#string-parameters

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 String from Native C++ DLL to C# App

I have written a DLL in C++. One of the functions writes to a character array.
C++ Function
EXPORT int xmain(int argc, char argv[], char argv2[])
{
char pTypeName[4096];
...
//Other pTypeName ends up populated with "Portable Network Graphics"
//This code verifies that pTypeName is populated with what I think it is:
char szBuff[64];
sprintf(szBuff, pTypeName, 0);
MessageBoxA(NULL, szBuff, szBuff, MB_OK);
//The caption and title are "Portable Network Graphics"
...
//Here, I attempt to copy the value in pTypeName to parameter 3.
sprintf(argv2, szBuff, 0);
return ret;
}
C# Import
//I believe I have to use CharSet.Ansi because by the C++ code uses char[],
[DllImport("FirstDll.dll", CharSet=CharSet.Ansi)]
public static extern int xmain(int argc, string argv, ref string zzz);
C# Function
private void button2_Click(object sender, EventArgs e)
{
string zzz = "";
int xxx = xmain(2, #"C:\hhh.bmp", ref zzz);
MessageBox.Show(zzz);
//The message box displays
//MessageBox.Show displays "IstuÈst¼ÓstÄstlÄstwÄstiÑstõÖstwÍst\
// aÖst[ÖstÃÏst¯ÄstÐstòÄstŽÐstÅstpÅstOleMainThreadWndClass"
}
I have attempted to pass a parameter from C# by reference and have the C++ DLL populate the parameter. Even though I have verified that the value is correct in the DLL, gibberish gets passed to the C# application.
What can I do to write the correct string value to the C# string?
Use a StringBuilder to pass a character array that native code can fill in (see Fixed-Length String Buffers).
Declare the function:
[DllImport("FirstDll.dll", CharSet=CharSet.Ansi)]
public static extern int xmain(int argc, string argv, StringBuilder argv2);
Use it:
// allocate a StringBuilder with enough space; if it is too small,
// the native code will corrupt memory
StringBuilder sb = new StringBuilder(4096);
xmain(2, #"C:\hhh.bmp", sb);
string argv2 = sb.ToString();
Give some other information to the DLLImport call. Look at the following example of my own:
[DllImport("tcpipNexIbnk.dll", EntryPoint = "SendData", CallingConvention = CallingConvention.Cdecl)]
public static extern int Send([MarshalAs(UnmanagedType.LPWStr)]string message);
Notice two things, the CallingConvention parameter:
CallingConvention = CallingConvention.Cdecl)
Use that as it is.
And then just behind the c# string type, you can play with the different Unmanaged types using the MarshalAS instruction, that will cast your C# string parameter to the native string type you have in your c++ program:
public static extern int Send([MarshalAs(UnmanagedType.LPWStr)]string message);
Hope it helps.

Returning a string from PInvoke? [duplicate]

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*

Calling DLL function with char* param from C#?

I have a C++ dll that I need to call from C#. One of the functions in the dll requires a char* for an input parameter, and another function uses a char* as an output parameter.
What is the proper way to call these from C#?
string should work if the parameter is read-only, if the method modifies the string you should use StringBuilder instead.
Example from reference below:
[DllImport ("libc.so")]
private static extern void strncpy (StringBuilder dest,
string src, uint n);
private static void UseStrncpy ()
{
StringBuilder sb = new StringBuilder (256);
strncpy (sb, "this is the source string", sb.Capacity);
Console.WriteLine (sb.ToString());
}
If you don't know how p/invoke marshaling works you could read http://www.mono-project.com/Interop_with_Native_Libraries
If you are only conserning with strings, read only the section: http://www.mono-project.com/Interop_with_Native_Libraries#Strings
Just using strings will work fine for input parameters, though you can control details about the string with the MarshalAs attribute. E.g.
[DllImport("somedll.dll", CharSet = CharSet.Unicode)]
static extern void Func([MarshalAs(UnmanagedType.LPWStr)] string wideString);
As for returning char* parameters, that's a little more complex since object ownership is involved. If you can change the C++ DLL you can use CoTaskMemAllocate, with something like:
void OutputString(char*& output)
{
char* toCopy = "hello...";
size_t bufferSize = strlen(toCopy);
LPVOID mem = CoTaskMemAlloc(bufferSize);
memcpy(mem, toCopy, bufferSize);
output = static_cast<char*>(mem);
}
The C# side then just uses an 'out string' parameter, and the garbage collector can pick up the ownership of the string.
Another way of doing it would be to use a StringBuilder, but then you need to know how big the string will be before you actually call the function.
Not sure this works, but have you tried with
StringObject.ToCharArray();
Not sure about initialising the String from char * tho. Mybe just assign to a string object, its worth a try.
Have you tried StringBuilder? I found this in a Google search:
[DllImport("advapi32.dll")]
public static extern bool GetUserName(StringBuilder lpBuffer, ref int nSize);
If you post the call you're making we can help you assemble it.
If the DLL function is expecting an allocated buffer of char* (not a wide/multibyte buffer) then the following will work:
[DllImport("somedll.dll", CharSet = CharSet.Ansi)]
static extern void TheFunc(byte[] someBuffer, int someSize);
Here a buffer allocated in c# is passed to TheFunc which fills it with a string of characters (of type char). Bytes aren't "interpreted" by C# they are treated like 8 bit integers, so are perfect for holding 8 bit characters.
An example code snipped would therefore be:
byte[] mybuffer;
int bufSize;
bufSize = 2048;
mybuffer = new byte[bufSize];
TheFunc(mybuffer, bufSize);
string value;
for(value = "", int ix = 0; (mybuffer[ix] != 0) && (ix < bufSize); ix++)
value += (char) mybuffer[ix];
DoSomethingWithTheReturnedString(value);

Categories