The issue is passing a 2D string array (non-blitable) from managed C# to unmanaged C++.
I not sure if the DllImport and MarshalAs conventions are fully correct for this type of string array. Maybe, the pointer / memory allocation definition has a missing attribute. Many thanks for your comments.
public struct TestStruct
{
public string[,] stringArray;
}
[DllImport("C:\\Users\\Win32Project2.dll",
EntryPoint = "DDentry",
CallingConvention = CallingConvention.StdCall)]
public static extern void DDentry
(
[In][MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.LPStr)] string[,] arrayReadDat, int iDim1, int iDim2
);
private void button6_Click_1(object sender, EventArgs e)
{
TestStruct arrayReadDat = new TestStruct();
arrayReadDat.stringArray = new string[lastRow+1, lastCol+1];
for (int i = 2; i <= lastRow; i++)
{
for (int j = 1; j <= lastCol; j++)
{
arrayReadDat.stringArray[i, j] = i;
}
}
int size = Marshal.SizeOf(typeof(TestStruct));
IntPtr strPointer = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(arrayReadDat, strPointer, false);
DDentry(arrayReadDat.stringArray, lastRow+1, lastCol+1);
Marshal.FreeHGlobal(strPointer);
}
Here the unmanaged C++ code, which don not show the data from the C# code:
_declspec(dllexport) void DDentry(string *p2DIntArray, int iDim1, int iDim2)
{
int iIndex = 0;
for (int i = 2; i <= iDim1; i++)
{
for (int j = 1; j <= iDim2; j++)
{
arrayREAD[i][j] = p2DIntArray[iIndex++];
}
}
}
It looks like, that instead of importing the DLL in C++ code and exporting it in C# code, You are doing it exactly vice versa.
An example how to call a managed DLL from native Visual C++ code can be found here:
https://support.microsoft.com/en-us/kb/828736
It is written for VS2005, but the overall logic should be same in newer VS versions also.
Related
I'm having issues with C++ Dll Integration with Unity. I found some code in the web (link at the end of article) but it doesn't work.
This is my C++ DLL code (I want to send unity a structure with some points):
struct Pontos {
int i;
float f;
};
Pontos **pontos;
DllExport bool SetPoints(Pontos *** a, int *i)
{
pontos = new Pontos*[4];
for (int j = 0; j < 4; j++) {
pontos[j] = new Pontos; // Actually create each object.
pontos[j]->i = j;
pontos[j]->f = (float)j;
}
*a = pontos;
*i = 4;
return true;
}
From the unity code, I get the following error:
No MonoBehaviour scripts in the file, or their names do not match the file name
I don't know what this means.
Here I try to get those points and save them in c#:
[DllImport("dll")]
private static extern bool SetPoints(out IntPtr ptrResultVerts, out int resultVertLength);
public struct Pontos
{
int i;
float f;
};
void Start()
{
IntPtr ptrNativeData = IntPtr.Zero;
int itemsLength = 0;
bool success = SetPoints(out ptrNativeData, out itemsLength);
if (!success)
{
return;
}
Pontos[] SArray = new Pontos[itemsLength]; // Where the final data will be stored.
IntPtr[] SPointers = new IntPtr[itemsLength];
Debug.Log("Length: " + itemsLength); // Works!
Marshal.Copy(ptrNativeData, SPointers, 0, itemsLength); // Seems not to work.
for (int i = 0; i < itemsLength; i++)
{
Debug.Log("Pointer: " + SPointers[i]);
SArray[i] = (Pontos)Marshal.PtrToStructure(SPointers[i], typeof(Pontos));
}
I got this code from Can't marshal array of stucts from C++ to C# in Unity.
You must import the dll specifying the calling convention
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
I don't think the error
No MonoBehaviour scripts in the file, or their names do not match the
file name
has anything to do with your code. Check your class name and your script name. Do they correspond?
Finally, I'm not sure about the out modifier in the method signature as I never have to deal with triple pointers on the C++ side. If your goal is to fill an array of Pontos structures than you shouldn't need it.
See also Pass structure (or class) from C++ dll to C# (Unity 3D)
this is a very clean and nice solution to marsahall a struct array from unmanaged C++ code.
it is allmost perfect solution when it comes to simplicity, it took me a while to get to that level of understanding the concept, so that in few lines of code, as you can see C# Main(), i have a populated array of struct ready to be 'harvested'..
typedef struct {
int Id;
BSTR StrVal;
}Package;
extern "C" __declspec(dllexport) void dodata(int requestedLength,int StringSize, Package **Packs){
int count;
count=0;
*Packs = (Package*)LocalAlloc(0, requestedLength * sizeof(Package));
Package *Cur = *Packs;
while(count!= requestedLength)
{
Cur[count].StrVal = NULL;
Cur[count].Id = count;
Cur[count].StrVal=SysAllocString(L"abcdefghij");
Cur[count].StrVal[StringSize-1]=count+'0';
++count;
}
}
C#
[DllImport(#"ExportStructArr.dll", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void dodata(int requestedLength, int StringSize, out IntPtr csPkPtr);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct csPk
{
public int V;
[MarshalAsAttribute(UnmanagedType.BStr, SizeConst = 10)]
public string testStr;
}
static void Main(string[] args){
int ArrL = 16000;
csPk[] Cpk = new csPk[ArrL];
IntPtr CpkPtr = IntPtr.Zero;
int szPk = Marshal.SizeOf(typeof(csPk));
dodata(ArrL, 10, out CpkPtr);
}
now all i have to do is :
for (int i = 0; i < Cpk.Length; i++)
{
Cpk[i] = (csPk)Marshal.PtrToStructure(new IntPtr(CpkPtr.ToInt32() + (szPk * i)), typeof(csPk));
}
the solution is quite easy as you can see
the question is using unsafe or any kind of transformation to the data, even going down to bytes...
how could i optimize it to perform better returning the data ?
Edit:
links i have tried to learn from other answers here in SO:
answer from Hans Passant
answer from AbdElRaheim
answer from supercat
also tried google : wikipedia , a github post by stephentoub
this is a complete blazing fast solution to populate a list of objects, i did my best
and i will be happy to have comments and suggestions.
c++
typedef struct _DataPacket
{
BSTR buffer;
UINT size;
} DataPacket;
extern "C" __declspec(dllexport) void GetPacksUnsafe( int size, DataPacket** DpArray )
{
int szr = size;int count=0;
*DpArray = (DataPacket*)CoTaskMemAlloc( szr * sizeof( DataPacket ));
if ( DpArray != NULL )
{
DataPacket* CurPack = *DpArray;
for ( int i = 0; i < szr; i++, CurPack++ )
{
CurPack->size = i;
CurPack->buffer = SysAllocString(L"SomeText00");
CurPack->buffer[9]=i+'0';
}
}
}
C#
[DllImport(#"ExportStructArr.dll", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void GetPacksUnsafe(int size, PackU** outPackUArr);
[StructLayout(LayoutKind.Sequential)]
public unsafe struct PackU
{
public char* StrVal;
public int IntVal;
}
public static unsafe List<PackU> PopulateLstPackU(int ArrL)
{
PackU* PackUArrOut;
List<PackU> RtLstPackU = new List<PackU>(ArrL);
GetPacksUnsafe(ArrL, &PackUArrOut);
PackU* CurrentPack = PackUArrOut;
for (int i = 0; i < ArrL; i++, CurrentPack++)
{
RtLstPackU.Add(new PackU(){ StrVal = CurrentPack->StrVal, IntVal=CurrentPack->IntVal});
}
Marshal.FreeCoTaskMem((IntPtr)PackUArrOut);
for (int i = 0; i < 10; i++)
{
Console.WriteLine("{0}", new string(RtLstPackU[i].StrVal));
}
return RtLstPackU;
}
using the code is as simple as it could possibly be
static unsafe void Main(string[] args)
{
int ArrL = 100000;
List<PackU> LstPackU;
LstPackU = PopulateLstPackU(ArrL);
}
there you have a list of custom data as fast as a bullet..
EDIT
using pointers instead of strings :
typedef struct _DataPackCharPnt
{
char* buffer;
UINT IntVal;
} DataPackCharPnt;
extern "C" __declspec(dllexport) void GetPacksPnt( int size, DataPackCharPnt** DpArrPnt )
{
int count = 0;
int TmpStrSize = 10;
*DpArrPnt = (DataPackCharPnt*)CoTaskMemAlloc( size * sizeof( DataPackCharPnt ));
DataPackCharPnt* CurPackPnt = *DpArrPnt;
char dummyStringDataObject[]= "abcdefgHi";
for ( int i = 0; i < size; i++,CurPackPnt++ )
{
dummyStringDataObject[9] = i+'0';
CurPackPnt->IntVal=i;
CurPackPnt->buffer = (char*)malloc(sizeof(char)*TmpStrSize);
strcpy(CurPackPnt->buffer, dummyStringDataObject);
}
}
reduced the time taken from 11 to 7 ms populating 100k elements
is there any part of creating the buffer i could omit ?
the duty of dummyStringDataObject is to simulate work, say getting a file name then set the buffer with its value, so except for this extra time which is the whole purpose of this function, to return some unknown values and lengths of the strings...
could you optimize it even further ?
How to convert the IntPtr to an array. Actually I called the function from unmanaged dll. It returns IntPtr. Now I need to convert it to an array. Please any one give an idea.Code snippet is below.
Unmanaged function declared
[DllImport("NLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern unsafe IntPtr N_AllocPt1dArray(NL_INDEX n, ref stacks S);
Calling the function
void Function1()
{
IntPtr PPtr=N_AllocPt1dArray(n, ref S);
}
Now I need to convert PPtr to an array(array is demo[]).where demo is defined by
public unsafe struct demo
{
public int x ;
public int y ;
public int z ;
}demo DEMO;
Try this:
array[0] = (demo)System.Runtime.InteropServices.Marshal.PtrToStructure(PPtr , typeof(demo));
UPDATE :
Solution 2 of this page is what you need.
It depends of what type of data you are pointing to, the next code get an array of strings from an IntPtr:
nstring is the number of elements you expect to get.
You can modify the code to satisfy your needs, but this can give you an idea of how to retrive data from an IntPtr in an unmaganed block code.
private string[] ConvertIntPtrToStringArray(IntPtr p, int nstring)
{
//Marshal.ptr
string[] s = new string[nstring];
char[] word;
int i, j, size;
unsafe
{
byte** str = (byte**)p.ToPointer();
i = 0;
while (i < nstring)
{
j = 0;
while (str[i][j] != 0)
j++;
size = j;
word = new char[size];
j = 0;
while (str[i][j] != 0)
{
word[j] = (char)str[i][j];
j++;
}
s[i] = new string(word);
i++;
}
}
return s;
}
cheers,
Kevin
I have C code which will be build as a dynamic library (DLL) , which i would like
to call C function from C# using dLL created from the C code
C code :
struct data
{
char data_val1[100];
float data_val2;
float data_val3[50];
};
typedef struct data data;
#ifdef __cplusplus
extern "C" __declspec(dllexport) void cfun_call(data *pdata,long count);
#endif
#ifdef __cplusplus
extern "C"
{
#endif
__declspec(dllexport) void cfun_call(data *pdata,long count)
{
int x = 0;
for(x=0;x<count;x++)
{
data[x].data_val2 = (pdata->data_val3[49] + pdata->data_val3[48]) / 2.0;
}
}
#ifdef __cplusplus
}
#endif
Here i wanted to import the function "cfun_call" in C# code, and pass values to the fucntion call
and manipulate the passed values in C function from the dll and wanted to display the updated values
back to the C# code and display it, Since my expertise in C# is limited i need some help to solve this issue
C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
class Program
{
public class data
{
public char[] data_val1 = new char[100];
public float data_val2;
public float[] data_val3 = new float[50];
};
[DllImport("mycdll.dll", EntryPoint = "cfun_call", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
// void cfun_call(data *pdata,long count); //C function for reference
public static extern void cfun_call([In, Out] data[] ouputdata, long count);
static void Main(string[] args)
{
data[] objData = new data[10];
for (int i = 0; i < 10; i++)
{
//Fill the data in objitemData
objData[i] = new objData();
for (int j = 0; j < 100; j++)
{
objData[i].data_val1[j] = '\0';
}
for (int k = 0; k < 50; k++)
{
objData[i].data_val3[k] = 20.00;
}
objData[i].data_val2 = 0.00;
}
cfun_call(objData,10); //Making call to C dll function
for (int i = 0; i < 10; i++)
Console.WriteLine("{0} ", objData[i].data_val2);
Console.WriteLine("");//new line
Console.ReadLine();
}
}
Here the values (objData) passed from C# function is not updated by using the C dll fucntion , I am not sure why.
Can anyone point me to right direction ?
Edit 1:
I have updated code as suggested ,
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct data
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public char[] data_val1;
public float data_val2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
public float[] data_val3;
};
Initialized struct elements like below ,
data[] objData = new data[10];
for (int i = 0; i < 10; i++)
{
//Fill the data in objitemData
objData[i] = new objData();
for (int j = 0; j < 100; j++)
{
objData[i].data_val1[j] = '\0'; //I am getting exception here
}
for (int k = 0; k < 50; k++)
{
objData[i].data_val3[k] = 20.00;
}
objData[i].data_val2 = 0.00;
}
Runtime i am getting null ptr exception , like
An unhandled exception of type 'System.NullReferenceException' occurred in mybinary.exe
Additional information: Object reference not set to an instance of an object.
How to initialize the struct array elements properly in manged code ?
Edit 2:
Hi one more question , when i add , objData[i].data_val3[k] = randomData; //randomvalues, It is not updated when making cfun_call while using contnt value it is updated why ?
Your translation of the struct is incorrect. You need it to be like so:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct data
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public char[] data_val1;
public float data_val2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
public float[] data_val3;
};
You have to make this a struct since you need to pass an array of values. Your declaration using class leads to you passing an array of references.
You will need to initialize the arrays explicitly now. That might look like so:
data[] objData = new data[10];
for (int i = 0; i < 10; i++)
{
objData[i].data_val1 = new char[100];
objData[i].data_val2 = 0.00;
objData[i].data_val3 = new float[50];
for (int k = 0; k < 50; k++)
{
objData[i].data_val3[k] = 20.0f;
}
}
Further, C++ long is 32 bits wide, but C# long is 64 bits wide. You therefore have a mismatch. Your p/invoke should be:
[DllImport("mycdll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void cfun_call(
[In, Out] data[] ouputItem_data,
int count
);
I have the following simple DLL in c++ un-managed code;
extern "C" __declspec(dllexport) void ArrayMultiplier(float (*pointerArray)[3], int scalar, int length);
void ArrayMultiplier(float (*pointerArray)[3], int scalar, int length)
{
for (int i = 0 ; i < length ; length++)
{
for (int j = 0; j < 3; j++)
{
pointerArray[i][j] = pointerArray[i][j] * scalar;
}
}
}
I have tried writing the following wrapper function for the above in c#:
[DllImport("sample.dll")]
public static extern void ArrayMultiplier(ref float elements, int scalar, int length);
where elements is a 2 dimentional 3x3 array:
public float[][] elements =
{
new float[] {2,5,3},
new float [] {4,8,6},
new float [] {5,28,3}
};
The code given above compiles, but the program crashes when the wrapper function is called:
Wrapper.ArrayMultiplier(ref elements, scalar, length);
Please help me here, and tell me whats wrong with the code above, or how a wrapper can be written for a simple c++ function:
void SimpleFunction(float (*pointerToArray)[3]);
Thank you all in advance
There are a few ways to do this.
The unsafe route, which works well with 2D arrays (that you have):
[DllImport("fastprocessing.dll", EntryPoint = "MyFunc")]
public static extern void MyFuncViaDLL(int inPtr, int outPtr, int inSize1, int size2, int param);
called via
private unsafe float[] MyFunc(float[] inData, int inSize1, int inSize2, int param1, int param2) {
float[] theOutData = new float[inChannelData.Length];
fixed (float* inBufferPtr = &inChannelData[0]) {
fixed (float* outBufferPtr = &theOutData[0]) {
MyFuncViaDLL((int)inBufferPtr, (int)outBufferPtr, inSize1, inSize2, param);
}
}
return theOutData;
}
That will work in an unsafe way, but you'd need to change your input arrays into 1D arrays. I think that's a better idea anyway, but that's just the way that I think.
If you want to be safe about it, add another parameter that is the size of the array itself, and then do some marshalling. Again, though, you'll need to go into 1D arrays:
Instead, you want to do some marshalling, like so:
[DllImport("fastprocessing.dll", EntryPoint = "MyFunc")]
public static extern void MyFuncViaDLL([MarshalAs(UnmanagedType.LPArray)]float[] inPtr, int size1, int size2, int totalSize, int param2);
Then just call the function directly:
MyFuncViaDLL(array, size1, size2, size1*size2, param1, param2);
Your C++ would then change to:
void ArrayMultiplier(float *pointerArray, int inSize1, int inSize2, int inTotalSize, int scalar)
{
int i, j, index;
for (i = 0 ; i < size1; i++)//note that length++ would be very very wrong here
{
for (j = 0; j < size2; j++)
{
index = i*size2 + j;
if(index >= inTotalSize) { return; } //avoid walking off the end
pointerArray[i*size2 + j] *= scalar;
}
}
}
If you want, you can add in the check against total length to ensure that you don't walk off the end, but that'll be a pretty big speed hit (enough to want to not use C++), as if statements aren't free.
Having done all of that, however, I have to ask-- why not just do this directly in C#, and save yourself the hassle of interop services like marshalling? C++ tends to be faster for complicated things, but for a quick array walk, I've seen C# behave pretty well. It can be pretty quick in C# too, once it's a 1D array:
int i;
for (i = 0; i < array.length; i++){
array[i] *= scalar;
}