I'm making a dll in c++ and I want to pass an array to a c# program. I already managed to do this with single variables and structures. Is it possible to pass an array too?
I'm asking because I know arrays are designed in a different way in those two languages and I have no idea how to 'translate' them.
In c++ I do it like that:
extern "C" __declspec(dllexport) int func(){return 1};
And in c# like that:
[DllImport("myDLL.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "func")]
public extern static int func();
Using C++/CLI would be the best and easier way to do this.
If your C array is say of integers, you would do it like this:
#using <System.dll> // optional here, you could also specify this in the project settings.
int _tmain(int argc, _TCHAR* argv[])
{
const int count = 10;
int* myInts = new int[count];
for (int i = 0; i < count; i++)
{
myInts[i] = i;
}
// using a basic .NET array
array<int>^ dnInts = gcnew array<int>(count);
for (int i = 0; i < count; i++)
{
dnInts[i] = myInts[i];
}
// using a List
// PreAllocate memory for the list.
System::Collections::Generic::List<int> mylist = gcnew System::Collections::Generic::List<int>(count);
for (int i = 0; i < count; i++)
{
mylist.Add( myInts[i] );
}
// Otherwise just append as you go...
System::Collections::Generic::List<int> anotherlist = gcnew System::Collections::Generic::List<int>();
for (int i = 0; i < count; i++)
{
anotherlist.Add(myInts[i]);
}
return 0;
}
Note I had to iteratively copy the contents of the array from the native to the managed container. Then you can use the array or the list however you like in your C# code.
You can write simple C++/CLI wrapper for the native C++ library. Tutorial.
You could use Platform Invoke. This will definitely be simpler if you have only one array to pass. Doing something more complicated might be impossible though (such as passing nontrivial objects). Documentation.
In order to pass the array from C++ to C# use CoTaskMemAlloc family of functions on the C++ side. You can find information about this on
http://msdn.microsoft.com/en-us/library/ms692727
I think this will be suffice for your job.
Related
How do I access a managed 2D array on the C++ side?
I know we are supposed to use pin_ptr<T> in order to access the managed array from the C++ side. It's straightforward with a 1D array, but with a 2D array I don't know how to use pin_ptr<T> correctly.
My code looks like this, where foo() will be called from the C# side:
nativeFunc(double **ptr);
foo(array<double,2> ^A)
{
const int len = A->GetLength(0);
double **ptr = (double**) alloca(sizeof(double*) * len);
for(int i = 0; i < len; i++)
{
pin_ptr<double> pinned = &A[i,0]; //Will go out of scope!
ptr[i] = pinned;
}
nativeFunc(ptr);
}
The problem is that my pin_ptr<T> will go out of scope too early, as it's located inside the loop body, and thus I think above code is NOT safe. But how can I avoid this? Apparently, it's not allowed to make an array of pin_ptr<T>, neither managed nor unmanaged. It also cannot be added to a std::vector and it cannot be made a class member either. So I'm kind of stuck here...
Thanks for any suggestions...
Okay, after some more digging, I found out that GCHandle::Alloc(x, GCHandleType::Pinned) may work as a more flexible replacement for pin_ptr<T> here. However, it seems we can only pin down the managed array as a whole. It does not seem to be possible to pin down a single sub-array (inner array) this way, like the pin_ptr<T> would do. Furthermore, by "try and error" method I have figured out that from the GCHandle handle I can get an unmanaged pointer via hdl.AddrOfPinnedObject().ToPointer() and that this one points to a continuous block of memory which contains the whole 2D array in a "flattened" (serialized) form. From here I can reconstruct the unmanaged 2D array, by using the proper base-pointer and stride. But is this considered a "safe" method and does it always work or is it even implementation specific ???
So I have hacked together a solution like this:
class ArrayPinHandlerRAII
{
public:
ArrayPinHandlerRAII(array<double,2> ^managedArray)
{
m_dimOuter = managedArray->GetLength(0);
m_dimInner = managedArray->GetLength(1);
m_handle = GCHandle::Alloc(managedArray, GCHandleType::Pinned);
m_ptr = new double*[m_dimOuter];
double *basePointer = reinterpret_cast<double*>
(m_handle.AddrOfPinnedObject().ToPointer());
for(size_t d = 0; d < m_dimOuter; d++)
{
m_ptr[d] = basePointer;
basePointer += m_dimInner;
}
}
~ArrayPinHandlerRAII(void)
{
delete [] m_ptr;
m_handle.Free();
}
inline double **data(void)
{
return m_ptr;
}
inline const size_t &dimOuter(void) const
{
return m_dimOuter;
}
inline const size_t &dimInner(void) const
{
return m_dimInner;
}
private:
GCHandle m_handle;
double **m_ptr;
size_t m_dimOuter;
size_t m_dimInner;
};
Any opinions? ;-)
Okay, one of the examples in MSDN has the following important info:
Pinning a sub-object defined in a managed object has the effect of pinning the entire object. For example, if any element of an array is pinned, then the whole array is also pinned. There are no extensions to the language for declaring a pinned array. To pin an array, declare a pinning pointer to its element type, and pin one of its elements.
So the code can actually be simplified to:
void nativeFunc(double **ptr);
void foo(array<double,2> ^A)
{
int dimOuter = managedArray->GetLength(0);
int dimInner = managedArray->GetLength(1);
pin_ptr<double> pinned = &A[i,0]; //This pins the *entire* array!
double **ptr = (double**) alloca(sizeof(double*) * dimOuter);
double *basePtr = pinned;
for(int i = 0; i < dimOuter; i++)
{
ptr[i] = basePtr;
basePtr += dimInner;
}
nativeFunc(ptr);
}
I have legacy c library and exported it all in a c++ class library. So far, I have problem with passing arguments of type double** which simply is an out 2D array. Here is my c++ method signature:
public: int GetVariableValues(double **time_values) {
return LegacyGetVariableValues(time_values);}
And here is my c# client call:
double[][] a;
Legacy.GetVariableValues(a);
However it does not work and I get type errors.
Anyone knows how to pass **(out) of course without using unsafe tag in C# client.
You don't need the C++/CLI library is this case, pinning a multidimensional array is harder than just pinning a one-dimensional array as you need to pin the inner arrays manually.
If you do it with C#, the compiler does it automatically for you.
With C#, you can do this:
[DllImport('legacylibrary.dll')]
public static extern int LegacyGetVariableValues(double[][] timeValues);
For further reading, check http://msdn.microsoft.com/en-us/library/fzhhdwae(v=vs.110).aspx
Edit:
As you need the C++/CLI layer, you need to do something like this:
ref class Legacy {
public:
int GetVariableValues(array<array<double> ^> ^values) {
array<GCHandle> ^handles = gcnew array<GCHandle>(values->Length);
double **valuesPtr = calloc(sizeof(double *), values->Length);
int result;
for (int i = 0; i < values->Length; i++) {
handles[i] = GCHandle::Alloc(values[i]);
valuesPtr[i] = (double *)GCHandle::ToIntPtr(handles[i]).GetPointer();
}
result = LegacyGetVariableValues(valuesPtr);
for (int i = 0; i < values->Length; i++) {
handles[i].Free();
}
return result;
}
}
PS: Don't know if the syntax is completely correct as I don't write C++/CLI in a long time.
I am writing a C# application that passes an empty string array of size 30 to a C++ DLL. This string array needs to be filled in the DLL and given back to the C# application.
I my code I observe memory corruption at the end of function call from DLL.
My C++ DLL code is as follows:
SAMPLEDLL_API BOOL InitExecution (wchar_t **paszStrings, int count)
{
for (int i = 0 ; i < count; i++)
{
mbstowcs(*(paszStrings + i), "Good",4);
//*(paszStrings + i) = "Good";
}
return TRUE;
}
My C# code is
string[] names = new[] { "Britto", "Regis" };
if (Wrapper1.InitExecution(ref names, names.Length) == 1)
MessageBox.Show("Passed");
[DllImport("MFCLibrary1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 InitExecution(ref string[] Names, int count);
To make this current approach work you'd need to pass StringBuilder instances rather than string. That's because the data is flowing from caller to callee. The strings are out parameters. And that means that the caller has to allocate the buffers for each string, and know how large the buffers need to be.
It's much easier to use BSTR here. This allows you to allocate the strings in the native code, and have them deallocated in the managed code. That's because BSTR is allocated on the shared COM heap, and the p/invoke marshaller understands them. Making this slight change means that the caller does not need to know how large the strings are up front.
The code would look like this:
SAMPLEDLL_API BOOL InitExecution(BSTR* names, int count)
{
for (int i = 0 ; i < count; i++)
names[i] = SysAllocStr(...);
return TRUE;
}
And on the C# side you write it like this:
[DllImport(#"mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool InitExecution(
[Out] IntPtr[] names,
int count
);
And then you've got a bit of work to marshal from BSTR to C# string.
IntPtr[] namePtrs = new IntPtr[count];
InitExecution(namePtrs, namePtrs.Length);
string[] names = new string[namePtrs.Length];
for (int i = 0; i < namePtrs.Length; i++)
{
names[i] = Marshal.PtrToStringBSTR(namePtrs[i]);
Marshal.FreeBSTR(namePtrs[i]);
}
consider the following c++ code
#include "stdafx.h"
#include<iostream>
using namespace std;
this much part i want in c#..
void ping(int,char* d[]);
void ping(int a,char *b[])
{
int size;
size=sizeof(b)/sizeof(int); // total size of array/size of array data type
//cout<<size;
for(int i=0;i<=size;i++)
cout<<"ping "<<a<<b[i]<<endl;
}
and below part is in c++
int _tmain(int argc, _TCHAR* argv[])
{
void (*funcptr)(int,char* d[]);
char* c[]={"a","b"};
funcptr= ping;
funcptr(10,c);
return 0;
}
how can i implement the same in c#..
m new to c#. how can i have char pointer array in c#?
You usually avoid char* or char[] in favor of the string class. Rather than having a char* d[], you would have a string[] d instead, if you want an array of strings, or a simple string d if you want a single list of characters.
Interop between C++ and C# is always tricky. Some good references include Pass C# string to C++ and pass C++ result (string, char*.. whatever) to C# and Using arrays and pointers in C# with C DLL.
A string is a list of characters. With your mention of character manipulation and your use of loops I'm assuming your concern is with targetting particular characters from one list/array - and in this sense you can code almost identically when interrogating particular characters from a string (as if it were a char array).
For example:
string testString = "hello";
char testChar = testString[2];
testChar, in this case, will be equal to 'l'.
Firstly, your "C++" code is actually C and bad C at that- it won't execute correctly at all. sizeof(b) will not give you the size of the array or anything like it, it will give you the size of a pointer. Consider writing some correct C++ before attempting to convert to C#.
template<int N> void ping(int a, std::array<std::string, N>& arr) {
for(int i = 0; i < N; i++) std::cout << a << arr[i] << "\n";
}
int _tmain(int argc, _TCHAR* argv[]) {
std::array<std::string, 2> c = { "a", "b" };
ping(10, c);
return 0;
}
C# doesn't have statically sized arrays, but the rest is easily done
public static void ping(int a, string[] arr) {
for(int i = 0; i < arr.Length; i++) {
System.Console.Write(a);
System.Console.Write(arr[i]);
}
}
public static void Main(string[] args) {
string[] arr = { "a", "b" };
ping(10, arr);
}
This should help you, although note that the size of the output buffer is fixed so this won't work for dynamic sized strings, you need to know the size beforehand.
public unsafe static void Foo(char*[] input)
{
foreach(var cptr in input)
{
IntPtr ptr = new IntPtr(cptr);
char[] output = new char[5]; //NOTE: Size is fixed
Marshal.Copy(ptr, output, 0, output.Length);
Console.WriteLine(new string(output));
}
}
Remember to allow unsafe code, since that's the only way you can use fixed pointers in C# (Right-click project, properties, build, allow unsafe code).
Next time be more specific and clear, and try to act more respectfully towards people here, we're not getting paid to help you you know :-)
we can do it as
in DLL we will have
extern "C" __declspec(dllexport) void __stdcall Caller()
{
static char* myArray[3];
myArray[0]="aasdasdasdasdas8888";
myArray[1]="sssss";
FunctionPtr1(2,myArray);
}
and in C# i just added following lines
public static void ping(int a, IntPtr x)
{
Console.WriteLine("Entered Ping Command");
//code to fetch char pointer array from DLL starts here
IntPtr c = x;
int j = 0;
string[] s = new string[100]; //I want this to be dynamic
Console.WriteLine("content of array");
Console.WriteLine("");
do
{
s[j] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(c, 4 * j));
Console.WriteLine(s[j]);
j++;
} while (s[j - 1] != null);
//**********end****************
Console.WriteLine("Integer value received from DLL is "+a);
}
I am having a really hard time getting this marshalling down.
I have umanaged code that looks like this:
WORD HLP_GetDeviceNames (LPSTR *DevNames, WORD Max_Len, WORD Max_Num)
Just FYI I did not write this unmanaged code but must use it.
Returns: WORD indicating an error.
DevNames: Pointer to an array of char arrays. Basically an array of strings that will be modified and returned back to me!
Max_Len: Length of each string (I am told this must be 256)
Max_Num: Length of array. I am using another Invoke call that is working that tells me number of devices so i know exactly how many strings to send.
I have used P/Invoke interop signatureToolkit to figure alot of this out but also read a bunch to get even further. Where I am now is here:
[DllImport("UsbMeasLib.dll")]
public static extern ushort HLP_GetDeviceNames([MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] ref StringBuilder[] DevNames, ushort Max_Len, ushort Max_Num);
I call my code like this:
StringBuilder[] DevNames = new StringBuilder[deviceCount];
for(int i = 0; i< deviceCount; i++)
{
DevNames[i] = new StringBuilder().Append(' ', 256);
}
HachUsbMeasLib.HLP_GetDeviceNames(ref DevNames, 256, Convert.ToUInt16(DevNames.Count()));
I am using string builder array because I need the unmanaged code to modify the string builder so that it can return the new string since string is unmutable.
When I run the code, My array is unmodified!
I'm not really sure what is going on but I think it has something to do with CLR telling unmanaged code to not modify my array in place but instead creates a new reference(pointer). Even if this is the case, I dont know how to fix it.
Thanks for any insight anybody can offer!
Try to work on low level. Declare DevNames parameter as IntPtr[]. Prepare it by the following way:
IntPtr[] devNames = new IntPtr[deviceCount];
for(int i = 0; i < deviceCount; i++)
{
devNames[i] = Marshal.AllocHGlobal[256];
}
Pass this array to HLP_GetDeviceNames. To handle output data, apply Marshal.PtrToStringAnsi to every DevNames member. Don't forget to release DevNames[i] with Marshal.FreeHGlobal in the end.
I figured this one out. Thanks to anybody who replied.
I found out how it works. I simply supply the memory space but I have to let the marshaling know that I expect in and out with this object so it allows the unmanaged code to modify the allocated space.
I did it like this:
[DllImport("UsbMeasLib.dll")]
private static extern ushort HLP_GetDeviceNames([In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] string[] DevNames, ushort Max_Len, ushort Max_Num);
I use string instead of string builder because the unmanaged code will simply replace the string which is ok. I am getting the array pointer back, not modified strings. the managed code is just changing an array of pointers to point to new string objects (I think).
int numDev = HLP_GetNumDevices();
string[] names = new string[numDev];
for (int i = 0; i < names.Length; i++)
{
names[i] = new StringBuilder().Append(' ', 256).ToString();
}
ushort errorCode = HLP_GetDeviceNames(names, 256, Convert.ToUInt16(numDev));
I allocate memory for the unamanged code then let the unmanaged code chane the strings there.
This works but I dont know if I have any potential memory leaks or other potential problems.