Passing an array of strings from C++ to C# - 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>

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.

How to DllImport char*

I have a c++ function like this:
myExport.h
extern "C" { __declspec(dllexport) const int Run(char *input, char *output, int *length); }
myExport.cpp
const int Run(char *input, char *output, int *length) {
std::ostringstream value;
value
<< "FOO" << "|"
<< "BAR" << "|";
auto str = value.str();
auto i = stdext::checked_array_iterator<char*>(output, str.length());
std::copy(str.begin(), str.end(), i);
output[str.length()] = '\0';
return 1;
}
And in C# I have:
myImport.cs
[DllImport("MyExport.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern int Run(
[MarshalAs(UnmanagedType.LPStr)]string input,
StringBuilder output,
ref int length);
public static string Execute(string input)
{
var length = 1024;
var output = new StringBuilder(1024);
var result = Run(input, output, ref length);
return output.ToString();
}
However, the output buffer is always empty. What am I doing wrong?
Since the type is char * for the second parameter, and the DLL function will fill in the buffer that's passed, the C# declaration should be as follows:
[MarshalAs(UnmanagedType.LPStr)]System.Text.StringBuilder output

Unmanaged Exports, passing an array of strings from C++ to C#

I'm using Robert Giesecke's Unmanaged Exports package to be able to call from C++ to C#.
This has to use the C interface from within C++. I have managed to get most things working, by scouring the web and picking up bits here and there....
extern "C"
{
// Simple
__declspec(dllimport) int IntTest(int input);
__declspec(dllimport) double DoubleTest(double input);
// Array of simple types in
__declspec(dllimport) int passArray(int t[], int i, int xx);
// String in and out
__declspec(dllimport) int PassStringIn(wchar_t* str);
__declspec(dllimport) int PassStringOut(wchar_t** str);
__declspec(dllimport) wchar_t* PassStringInOut(wchar_t* str);
// Array of strings types in
//__declspec(dllimport) int passArrayStrings(char** t, int i);
}
....
// Int in and out
int aa = IntTest(4);
// Double in and out
double bb = DoubleTest(4.3);
// Pass array in
int arr[4] = { 1,2,3,4 };
int cc = passArray(arr, 4, 0);
// String in
wchar_t* a_str = L"input string from C++";
int dd = PassStringIn(a_str);
// String out
wchar_t* b_str = L"not used";
int ee = PassStringOut(&b_str);
// String in & out
wchar_t* d_str = L"bob";
wchar_t* result = PassStringInOut(d_str);
corresponding C#
[DllExport( CallingConvention = CallingConvention.Cdecl)]
static int IntTest(int input)
{
return input + 1;
}
[DllExport(CallingConvention = CallingConvention.Cdecl)]
static double DoubleTest(double input)
{
return input + 1;
}
[DllExport(CallingConvention = CallingConvention.Cdecl)]
public static int passArray([In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] int[] tab, int i, int x)
{
return tab[x];
}
[DllExport(CallingConvention = CallingConvention.Cdecl)]
public static int PassStringIn( [MarshalAs(UnmanagedType.LPWStr)] string inputString)
{
Console.WriteLine("Hi, the string passed in was :" + inputString);
return 1;
}
[DllExport(CallingConvention = CallingConvention.Cdecl)]
static int PassStringOut([MarshalAs(UnmanagedType.BStr)] out string outputString)
{
Console.WriteLine("Hi, I will return the time from c#");
outputString = DateTime.Now.ToLongTimeString();
return 0; // indicates success
}
[DllExport(CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.LPTStr)]
public static string PassStringInOut([MarshalAs(UnmanagedType.LPTStr)]string name)
{
return string.Format("Hello from .NET assembly, {0}!", name);
}
Which was nice! Anyway would anybody be able to help with passing arrays of strings in and out. I am pretty sure the C# section should look like this:
[DllExport(CallingConvention = CallingConvention.Cdecl)]
public static int passArrayStrings( [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 1)] string[] tab, int i)
{
return 1;
}
I need some help on the C++(C) side on how to structure the array of strings in, such that they can be marshaled correctly. The mixed mode assembly created has both C# and and a C interface. As it is C and not C++ the arguments types of the exposed functions are not visible.
Thanks
You can use an IntPtr parameter.
You'll have to allocate unmanaged memory and copy the array into that blob anyway. Otherwise the GC will eat your array at some point.
Unmanaged Exports with Arrays
ok so after a lot of messing about I came to a solution:
// Array of strings types in
__declspec(dllimport) int passArrayStrings(BSTR* bstrArray, int i);
BSTR bstrArray[10] = { 0 };
for (int i = 0; i < 10; i++)
{
bstrArray[i] = ::SysAllocString(L"My String.");
}
int ff = passArrayStrings(bstrArray, 10);
for (int i = 0; i < 10; i++)
{
::SysFreeString(bstrArray[i]);
}
and on the c# side:
[DllExport(CallingConvention = CallingConvention.Cdecl)]
public static int passArrayStrings([In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 1)] string[] tab, int iSize)
{
return 1;
}

C++ Struct in C#

I'm using a DLL written in C++ in my C# project by using DllImport and one of the functions I'm using looks like this:
[DllImport("dds.dll", CharSet = CharSet.Auto)]
private static extern int Par(
ddTableResults2 tableResult,
ref parResults ParResult,
int vul
);
The parResults struct is defined in C++ like this:
struct parResults {
/* index = 0 is NS view and index = 1
is EW view. By 'view' is here meant
which side that starts the bidding. */
char parScore[2][16];
char parContractsString[2][128];
};
The start of the C++ function
int STDCALL Par(struct ddTableResults * tablep, struct parResults *presp,
int vulnerable)
How should I define the above struct in C# to able to send that struct as en reference into the DLL function?
This is what I have tried but don't work at all and I just get a Access Violation Error
[StructLayout(LayoutKind.Sequential)]
public struct parResults
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[,] parScore;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[,] parContractsString;
public parResults(int x)
{
parScore = new char[2,16];
parContractsString = new char[2,128];
}
}
This is quite a tricky struct to marshal in C#. There are various ways to attempt it, but I think that it will be cleanest to represent the character arrays as byte arrays and marshal to and from strings by hand. Here is a demonstration of what I mean:
C++
#include <cstring>
struct parResults {
char parScore[2][16];
char parContractsString[2][128];
};
extern "C"
{
__declspec(dllexport) void foo(struct parResults *res)
{
strcpy(res->parScore[0], res->parContractsString[0]);
strcpy(res->parScore[1], res->parContractsString[1]);
}
}
C#
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[StructLayout(LayoutKind.Sequential)]
class parResults
{
private const int parScoreCount = 2;
private const int parScoreLen = 16;
private const int parContractsStringCount = 2;
private const int parContractsStringLen = 128;
[MarshalAs(UnmanagedType.ByValArray,
SizeConst = parScoreCount * parScoreLen)]
private byte[] parScoreBuff;
[MarshalAs(UnmanagedType.ByValArray,
SizeConst = parContractsStringCount * parContractsStringLen)]
private byte[] parContractsStringBuff;
public string getParScore(int index)
{
string str = Encoding.Default.GetString(parScoreBuff,
index * parScoreLen, parScoreLen);
int len = str.IndexOf('\0');
if (len != -1)
str = str.Substring(0, len);
return str;
}
public void setParScore(int index, string value)
{
byte[] bytes = Encoding.Default.GetBytes(value);
int len = Math.Min(bytes.Length, parScoreLen);
Array.Copy(bytes, 0, parScoreBuff, index * parScoreLen, len);
Array.Clear(parScoreBuff, index * parScoreLen + len,
parScoreLen - len);
}
public string parContractsString(int index)
{
string str = Encoding.Default.GetString(parContractsStringBuff,
index * parContractsStringLen, parContractsStringLen);
int len = str.IndexOf('\0');
if (len != -1)
str = str.Substring(0, len);
return str;
}
public void setParContractsString(int index, string value)
{
byte[] bytes = Encoding.Default.GetBytes(value);
int len = Math.Min(bytes.Length, parContractsStringLen);
Array.Copy(bytes, 0, parContractsStringBuff,
index * parContractsStringLen, len);
Array.Clear(parContractsStringBuff,
index * parContractsStringLen + len,
parContractsStringLen - len);
}
public parResults()
{
parScoreBuff = new byte[parScoreCount * parScoreLen];
parContractsStringBuff =
new byte[parContractsStringCount * parContractsStringLen];
}
};
[DllImport(#"...", CallingConvention = CallingConvention.Cdecl)]
static extern void foo([In,Out] parResults res);
static void Main(string[] args)
{
parResults res = new parResults();
res.setParContractsString(0, "foo");
res.setParContractsString(1, "bar");
foo(res);
Console.WriteLine(res.getParScore(0));
Console.WriteLine(res.getParScore(1));
Console.ReadLine();
}
}
}
Here I've used a class to represent the struct. Since a class in C# is a reference, we don't declare the parameters of that type with ref. I've also used __cdecl for convenience to avoid having to work out what the decorated name of the function would be. But your library used __stdcall and so you need to stick to that.
The class demonstrates sending data in both directions. You could probably simplify the code if the data flow was more restricted.

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