Trouble with string marshaling - c#

I'm working on some code that calls into a native library for some computation. As you can see from the following code snippets I'm invoking a method that returns a std::string. My intention is to take that result convert it to a char* and marshal it back to my managed C# application. The catch here is, sometimes the execution_result will contain UTF-8 characters...
Code:
extern "C" __declspec(dllexport) int __stdcall execute_payload(example::native_code* instance, char* payload, char** result) {
std::string execution_result = instance->execute(payload);
char* result_array = new char[execution_result.size() + 1];
strcpy(result_array, execution_result.c_str());
*result = result_array;
return execution_result.length();
}
The following is a snippet of C# that yields expected results.
Code:
private HandleRef NativeInstance;
public Wrapper()
{
NativeInstance = new HandleRef(this, NativeCode.CreateNew());
}
public string ExecuteNativeCode(string payload)
{
IntPtr resultPointer = Marshal.AllocHGlobal(sizeof(int));
var resultLength = NativeCode.Execute(NativeInstance, payload, ref resultPointer);
var resultBytes = new byte[resultLength - 1];
Marshal.Copy(resultPointer, resultBytes, 0, resultLength - 1);
var resultString = Encoding.UTF8.GetString(resultBytes);
Marshal.FreeHGlobal(resultPointer);
return resultString;
}
Expected Result:
123,45 €
Actual Result:
123,45 €
The following is an example of some code that I've tried in order to "simply" the marshaling of the string result. But it doesn't really work...
Code:
private HandleRef NativeInstance;
public Wrapper()
{
NativeInstance = new HandleRef(this, NativeCode.CreateNew());
}
public string ExecuteNativeCode(string payload)
{
IntPtr resultPointer = Marshal.AllocHGlobal(sizeof(int));
var resultLength = NativeCode.Execute(NativeInstance, payload, ref resultPointer);
var resultString = Marshal.PtrToStringUni(resultPointer, resultLength);
Marshal.FreeHGlobal(resultPointer);
return resultString;
}
Expected Result:
123,45 €
Actual Result:
㈱ⰳ㔴ꃂ苢¬缀\0ᴹ襴翻\0
I have two questions.
Why doesn't the second snippet work?
Regardless of why the second snippet doesn't work, what can I do better?
Thanks!

Related

I can't get the input parameter of a PInvoke to C++ DLL from C# to be used as ouput with IntPtr

I have a function in a C++ DLL that takes one input. I'm trying to have that input be used as an output to the C# call.
Here is my C++ function:
MYAPI int testStuff3(unsigned char* str)
{
printf("%s\n", str);
str = (unsigned char*)malloc(9);
str[0] = 'G';
str[1] = 'o';
str[2] = 'o';
str[3] = 'd';
str[4] = 'b';
str[5] = 'y';
str[6] = 'e';
str[7] = '!';
str[8] = '\0';
return 1;
}
Here is the C# code:
public class Program
{
[DllImport("NativeLib.dll")]
private static extern int testStuff3([In, Out] IntPtr str);
static void Main(string[] args)
{
IntPtr junk3 = IntPtr.Zero;
int ret = testStuff3(junk3);
Byte[] stuff3 = new byte[9];
Marshal.Copy(junk3, stuff3, 0, 9);
}
}
When the Marshal.Copy is called, it gives an error saying that the source (junk3) can not be null.
Will this not work, sending a null pointer to C++ DLL from C# and having the DLL allocate the memory and store something inside and return it to the caller? I want to keep it an IntPtr and not a StringBuilder because the data won't necessarily be a string in the final code. Just an unsigned char array in C++ and I want the IntPtr to point to it.
I've tried different variations of [In, Out], [Out], out and ref for the IntPtr passing.
Never ever allow memory allocations to cross a DLL boundary. That way lies madness, and/or Sparta.
(For the pedantic: you can allocate memory and then pass a pointer across, as long as you either pass ownership back to free it, or guarantee that the same allocator is used as part of a contract. But it's still something to avoid when possible.)
Typically to use a string output parameter you should pass a StringBuilder as the argument, setting its capacity to the maximum expected length. Then in the native code you simply fill this existing buffer.
See the "Fixed length string buffers" section here for an example.
Thanks for the help!
Here's what I ended up with.
C++ function:
MYAPI int testStuff4(wchar_t* str)
{
unsigned char* stuff = (unsigned char*)malloc(10);
stuff[0] = 'G';
stuff[1] = 'o';
stuff[2] = 'o';
stuff[3] = 'd';
stuff[4] = 'b';
stuff[5] = 'y';
stuff[6] = 'e';
stuff[7] = '!';
stuff[8] = '\0';
mbstowcs(str, (const char*)stuff, 1024);
free(stuff);
return 1;
}
C# function:
public class Program
{
[DllImport("NativeLib.dll")]
private static extern int testStuff4(IntPtr str);
static void Main(string[] args)
{
IntPtr junk4 = Marshal.AllocHGlobal(1024);
int ret = testStuff4(junk4);
string junkString = Marshal.PtrToStringUni(junk4);
Console.WriteLine(junkString);
Marshal.FreeHGlobal(junk4);
}
}
Your C++ function doesn’t modify the passed string. It allocates a new one with malloc, stores it in a local variable forgetting the passed value, then returns leaking the memory.
If for some reason you want to do manual marshalling, you probably want something like this (assuming this is for Windows):
MYAPI BOOL __stdcall testStuff3( char** pp )
{
if( nullptr == pp )
return FALSE; // null pointer
if( nullptr != *pp )
{ // Print & release an old string
printf( "%s\n", *pp );
CoTaskMemFree( *pp );
*pp = nullptr;
}
// Allocate a new one
const char* const str = CoTaskMemAlloc( 9 );
if( nullptr == str ) return FALSE;
strncpy( str, "Goodbye!", 9 );
*pp = str;
return TRUE;
}
C#:
public class Program
{
[DllImport( "NativeLib.dll" )]
private static extern bool testStuff3( [In, Out] ref IntPtr str );
static void Main( string[] args )
{
IntPtr ptr = IntPtr.Zero;
if( testStuff3( ref ptr ) )
{
Console.WriteLine( Marshal.PtrToStringAnsi( ptr ) );
Marshal.FreeCoTaskMem( ptr );
}
}
}
However, this is not something I recommend doing unless you have very good reasons. In most cases automatic marshalling is better. For C# -> C++ way it’s trivially simple, const char* or const wchar_t* in C++, string (with correct attributes) in C#. For C++ -> C# you can allocate a StringBuilder in C#, pass char* or wchar_t* to C++, and buffer length in another argument.

Passing an array of strings from C++ to C#

I've already written this piece of code which works fine:
C++ code
extern "C"
{
const MYLIBRARY_EXPORT char* giefStrPlx(char* addon)
{
return addon;
}
}
C# code
[DllImport("ClassLibrary1")]
private static extern IntPtr giefStrPlx(string x);
void Start()
{
IntPtr stringPtr = giefStrPlx("Huntsman");
string huntsman = Marshal.PtrToStringAnsi(echoedStringPtr);
}
After this huntsman contains "Huntsman".
My problem is the step of doing something similar for an array of strings. I wrote the following function
extern "C"
{
const MYLIBRARY_EXPORT bool fillStrArray(char** lizt, int* length)
{
char* one = "one";
char* two = "two";
char* three = "three";
lizt[0] = one;
lizt[1] = two;
lizt[2] = three;
*length = 3;
}
}
I then tried to write the following piece of code in C#
[DllImport("ClassLibrary1")]
private static extern bool fillStrArray(ref IntPtr array, ref int length);
void Start()
{
IntPtr charArray = IntPtr.Zero;
int charArraySize = 0;
fillStrArray(ref charArray, ref charArraySize);
IntPtr[] results = new IntPtr[charArraySize];
Marshal.Copy(charArray, results, 0, charArraySize);
foreach (IntPtr ptr in results)
{
string str = Marshal.PtrToStringAnsi(ptr);
}
}
Which does not work. So now I'm a bit lost on how to accomplish this.
Here are the two helper functions I have from CLR to std::string and from std::string to string CLR
std::string CLROperations::ClrStringToStdString(String^ str)
{
if (String::IsNullOrEmpty(str))
return "";
std::string outStr;
IntPtr ansiStr = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(str);
outStr = (const char*)ansiStr.ToPointer();
System::Runtime::InteropServices::Marshal::FreeHGlobal(ansiStr);
return outStr;
}
String ^ CLROperations::StdStringToClr(std::string str)
{
return gcnew String(str.c_str());
}
for using a List of strings you will need to use List<String^>^ mind the capital String. for a list of std::string use std::vector<std::string>

C/C# - Marshal.PtrToStringAnsi

What's the equivalent to Marshal.PtrToStringAnsi (C#) in C language ?
Because, I would like rewrite a function C# in C.
This is the C# function:
public int GetProcessName(uint processId, out string name)
{
IntPtr ptr = Marshal.AllocHGlobal((int)(0x211)); int result = -1;
result = getProcessName(processId, ptr);
name = String.Empty;
if(SUCCESS(result))
name = Marshal.PtrToStringAnsi(ptr);
Marshal.FreeHGlobal(ptr);
return result;
}
And actually my C function:
int
GetProcessName(uint processId, char *name)
{
HINSTANCE hLib = LoadLibrary("CCAPI.DLL");
__cGetProcessName getProcessName = (__cGetProcessName)GetProcAddress(hLib, "CCAPIGetProcessName");
int *ptr = malloc((int)(0x211));
int result = -1;
result = getProcessName(processId, ptr);
name = "";
if (SUCCESS(result))
{
name = /* ?? */;
}
free(ptr);
return (result);
}
In C#, Marshal.PtrToStringAnsi takes a pointer and returns a string.
In C, there is no string type and nothing equivalent out of the box. The closest you can have is a char* that points to an array of characters terminated with a null character ('\0'). By convention, we call this a "C string" (even though it's not a type of its own).
Thus, if you have a char*, you already have the C equivalent of the output of a Marshal.PtrToStringAnsi on that char* (as a IntPtr) in C#.
So this line:
name = Marshal.PtrToStringAnsi(ptr);
Would have been something like this:
name = ptr;
But since you seem to be freeing the ptr buffer at the end and you want the result in the externally provided buffer name, the correct alternative in C would actually be:
strcpy(name, ptr);

Convert IntPtr to char** in C#

I'd like to interpret the output of the following unmanaged function:
afc_error_t afc_get_device_info (afc_client_t client, char ***device_information)
I import the dll with the code:
[DllImport("libimobiledevice.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern short afc_get_device_info(IntPtr client, out IntPtr info);
As long as I only needed to convert response to string Marshal.PtrToStringAnsi was okay. However I have no idea how to convert that IntPtr back to char array.
It should be something like:
IntPtr di;
int result = afc_read_directory(client, #"C:\", out di);
if (di == IntPtr.Zero)
{
throw new Exception();
}
IntPtr di2 = di;
while (true)
{
IntPtr ptr = Marshal.ReadIntPtr(di2);
if (ptr == IntPtr.Zero)
{
break;
}
string str = Marshal.PtrToStringAnsi(ptr);
if (str == string.Empty)
{
break;
}
di2 = di2 + IntPtr.Size;
}
Try if it works, then I'll explain how...
important you are leaking memory here...
I've found this example in C:
char **dirs = NULL;
afc_read_directory(afc, "/eafaedf", &dirs);
if (!dirs)
afc_read_directory(afc, "/", &dirs);
printf("Directory time.\n");
for (i = 0; dirs[i]; i++) {
printf("/%s\n", dirs[i]);
free(dirs[i]);
}
if (dirs)
free(dirs);
you are responsible for freeing the memory (see the free inside the cycle and the final free?). In this case (and for other methods that return arrays of C-strings you can use afc_dictionary_free. Note that other methods like afc_receive_data that return a single block of memory you can't use it.

Return C++ char to C#

I have a C++ project in which I have to return some variables from C++ to C#.
These char variables are in the main program:
char test1[MAX_Q_LEN], test2[MAX_Q_LEN], test3[MAX_Q_LEN];
After I finish doing something with these variables in my C program, I have to return the values of these variables in a C# program.
ReturnChar.h
extern "C" RETURNCHAR_API TCHAR* __cdecl testString();
ReturnChar.cpp
extern "C" RETURNCHAR_API TCHAR* testString()
{
return ;
}
TestImport C#
static class TestImport
{
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr testString();
}
public partial class MainWindow : Window
{
public MainWindow()
{
try
{
InitializeComponent();
textBox1.Text = ReturnSomething()
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private static string ReturnSomething()
{
IntPtr t = TestImport.testString();
String result = Marshal.PtrToStringAuto(t);
}
I tried with the above approach but I am not able to find out how to return the above char values. Also, this should not be an independent function because the values shoud be fetched only after executing the main which will give the right values in these variables.
Any suggestions?
I will suggest a solution which would require you to change function signature to this:
extern "C" int __cdecl testString(char *output, int outputSize);
That is, pass an allocated buffer as the first argument to the function which will hold the output, and pass the size of the buffer as the second argument.
Note that I mention the return type of the function as int. It is because you could return the output actual size from the function, and the caller can interpret this value to confirm that outputSize value was large enough to hold the output string. For example, you could implement testString() as:
int testString(char *output, int outputSize)
{
std::string const & s = getString();
if ( s.size() <= outputSize )
{
std::strncpy(output, s.c_str(), s.size());
return s.size(); //return the actual size of output
}
else //means s.size() > outputSize, i.e outputSize is smaller than required!
{
std::strncpy(output, s.c_str(), outputSize);
return s.size(); //return what is required (the actual size of output)!
}
}
Then in C# code, do this:
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int testString(out StringBuilder output, int outputSize);
And call it as:
private static string ReturnSomething()
{
int bufferSize = 100;
StringBuilder buffer= new StringBuilder(bufferSize);
int outputSize = TestImport.testString(buffer, bufferSize);
if ( outputSize < bufferSize ) //output bufferSize was sufficient
{
return buffer.ToString();
}
else //output bufferSize was insufficient
{
//retry!
bufferSize = outputSize;
buffer = new StringBuilder(bufferSize); //reallocate!
outputSize = TestImport.testString(buffer, bufferSize);
if ( outputSize <= bufferSize )
return buffer.ToString();
else
{
throw new Exception("PANIC");
}
}
}
I'm not quite big c++ spec, but maybe to use bstrs in c++
_bstr_t text("saasas");
return text.Detach();
and for c# parameter
[MarshalAs(UnmanagedType.BStr)]
or to pass StringBuilder to your c++ func with some preallocated capacity

Categories

Resources