everyone, I'm newbie in C#, and I have a problem with using "C" dll in C#, below is the C side code which work fine.
// myCode.h
typedef struct _SubABC
{
unsigned short WordCount;
unsigned char *WordData;
unsigned char SpeedUp;
} SubABC;
typedef struct _ABC
{
unsigned char SubFontNum;
SubABC subABC[5];
} ABC
extern __declspec(dllimport) int __stdcall MY_SetSth(unsigned char SerialNum, ABC *pABC);
//myCode.c
ABC myFun = { '\0' };
unsigned char text_c[] =
{
0x00,0x27,0xff,0xA5,0xC3, 0x00,0x27,0xff,0xA6,0x26, 0x00,0x23,0xff,0xA7,0xAE, 0x00,0x27,0xff,0xBA,0x7E,
0x00,0x27,0xff,0xC1,0x52, 0x00,0x27,0xff,0xAC,0xF9, 0x00,0x27,0xff,0x20,0x00,0x27,0xff,0x31, 0x00,0x27,0xff,0x20, 0x00,0x27,0xff,0xA4,0xC0, 0x00,0x27,0xff,0xC4,0xC1,
};
myFun.SubFontNum = 1;
myFun.subMABC[0].WordCount = sizeof(text_c);
myFun.subMABC[0].WordData = text_c;
myFun.subMABC[0].SpeedUp = 0;
int retvalue = MY_SetSth(1, &myFun);
and my C# code is below
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SubABC
{
public ushort WordCount;
[MarshalAs(UnmanagedType.LPArray)]
public byte[] WordData;
public byte SpeedUp;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ABC
{
public byte SubFontNum;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public SubABC[] subABC;
}
[DllImport("myDLL.dll", CharSet = CharSet.Ansi)]
public extern static int MY_SetSth(byte SerialNum, ref ABC pABC);
ABC myFun = new ABC();
ABC.subABC = new SubABC[5];
unsigned char text_c[] =
{
0x00,0x27,0xff,0xA5,0xC3, 0x00,0x27,0xff,0xA6,0x26, 0x00,0x23,0xff,0xA7,0xAE, 0x00,0x27,0xff,0xBA,0x7E,
0x00,0x27,0xff,0xC1,0x52, 0x00,0x27,0xff,0xAC,0xF9, 0x00,0x27,0xff,0x20,0x00,0x27,0xff,0x31, 0x00,0x27,0xff,0x20, 0x00,0x27,0xff,0xA4,0xC0, 0x00,0x27,0xff,0xC4,0xC1,
};
myFun.SubFontNum = 1;
myFun.subMABC[0].WordCount = (ushort)text_c.Length;
myFun.subMABC[0].WordData = text_c;
myFun.subMABC[0].SpeedUp = 0;
int retvalue = MY_SetSth(1, ref myFun);
I always get TypeLoadException: Cannot marshal field 'WordData' of type 'subABC': Invalid managed/unmanaged type combination (Array fields must be paired with ByValArray or SafeArray), because WordData is unknown size, I can't use ByValArray, and SafeArray get AccessViolationException: Attempted to read or write protected memory
I try to use IntPtr like below
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SubABC
{
public ushort WordCount;
public IntPtr WordData;
public byte SpeedUp;
}
ABC.subABC[0].WordData = Marshal.AllocHGlobal(text_c.Length); ;
Marshal.Copy(text_c, 0, ABC.subABC[0].WordData, text_c.Length);
int retvalue = MY_SetSth(1, ref myFun);
I get error message AccessViolationException: Attempted to read or write protected memory
Can anyone help me? thank a lot.
I have a DLL developed in C++ that performs some computations. I am trying to link this DLL into my C# application.
I have a struct within a struct which contains a char array (C string). I have a pointer to the object created by the C++ DLL within my C# application.
namespace Test
[StructLayout(LayoutKind.Sequential)]
public unsafe struct Child
{
public float number1;
public float number2;
[MarshalAs(UnmanagedType.LPArray)]
public char[] name;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct DataStructure
{
public Child child;
...
}
.
.
.
public unsafe partial class Form1:Form {
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern IntPtr createInstance();
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern DataStructure* processData(IntPtr source); // Setup of data
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern int calculate(IntPtr calc); // Perform calculation
.
.
public Form2()
{
private void button1_Click_1(object sender, EventArgs e)
{
// test functions
unsafe
{
IntPtr ptr1 = createInstance(); // Returns pointer to the instance
DataStructure* data = processData(ptr1); // Returns pointer to DataStructure object created through DllImport
data->child.number1 = 1.234F; // Works
data->child.number1 = 9.876F; // Works
data->child.name = "......" // This doesn't work!!
int result = calculate(ptr1); // Returns value when name commented
}
}
}
}
If I don't comment name, I get the Error - Cannot take the address of managed object.
If I comment name, I am able to run the calculation without issue.
I wish to update the value of the char array name but can't seem to figure out the solution. Below is my usual C solution. Thanks!
#include "DataStructure.h"
#define DLLNAME "Calculation.dll"
int main(int argc, char** argv) {
HINSTANCE dllHandle = LoadLibraryA(fileNameDll);
const void* ptr1 = 0;
int result = 0;
DataStructure *data;
ptr1 = createInstance();
data = processData(ptr1);
data->child.number1 = 1.234;
data->child.number2 = 9.876;
sprintf(data->child.name, "3104-03");
result = calculate(ptr1);
}
as i am new to the unmanaged world, and after a success test of interacting with c++ DLL on a simple task,
i am now trying to consume in c#, data manipulated by c++ via an array of struct
i have also tried to wrap the struct[] within another struct to simplify the marshal but i did not succeed in any way.
what is the correct (and most efficient performance wise) way to handle the data returned from c++ given the code below.
C#
[StructLayout(LayoutKind.Sequential)]
public struct chars
{
public int V;
//[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 10)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] testStr;
}
[DllImport("ExportStructArr.dll", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void dodata(int requestedMasterArrLength, int requestedStringSize,[MarshalAs(UnmanagedType.LPArray), Out()] chars[] Cars);
//tried also this signature
public static extern void dodata(
int requestedMasterArrLength,
int requestedStringSize,
out IntPtr chars);
c++
#ifdef EXPORTSTRUCTARR_EXPORTS
#define EXPORTSTRUCTARR_API extern "C" __declspec(dllexport)
typedef struct {
int Id;
char *StrVal;
}Unit;
EXPORTSTRUCTARR_API void dodata(int requestedMasterArrLength,int requestedStringSize, Unit **UntArr)
{
int ccountr,count;
count=0;
*UntArr = (Unit*)LocalAlloc(0, requestedMasterArrLength * sizeof(Unit));
Unit *CurU = *UntArr;
while(count!= requestedMasterArrLength)
{
char dummyStringDataObject[]= "abcdefgHi";
CurU[count].StrVal = NULL;
dummyStringDataObject[0] = count+'0';
CurU[count].Id = count;
CurU[count].StrVal= (char*) LocalAlloc(0,(sizeof(char)* requestedStringSize));
ccountr=0;
while(ccountr!= requestedStringSize){
CurU[count].StrVal[ccountr] = dummyStringDataObject[ccountr];
++ccountr;
}
++count;
}
}
I'm trying to get back this struct from a native C library through P/Invoke:
struct ndb_mgm_cluster_state
{
int no_of_nodes;
struct ndb_mgm_node_state node_states[1];
};
where ndb_mgm_node_state is:
struct ndb_mgm_node_state {
int node_id;
enum ndb_mgm_node_type node_type;
enum ndb_mgm_node_status node_status;
int start_phase;
int dynamic_id;
int node_group;
int version;
int connect_count;
char connect_address[sizeof("000.000.000.000")+1 ];
int mysql_version;
};
The method's signature is:
ndb_mgm_cluster_state* WINAPI wrap_ndb_mgm_get_status(HANDLE handle);
All of this is provided by a 3rd party library, so nothing can be changed.
In C# i have the follow definitions:
[DllImport("Ndb_CWrapper.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr wrap_ndb_mgm_get_status(IntPtr handle);
the structures are:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ndb_mgm_cluster_state {
public int no_of_nodes;
public IntPtr node_states;
};
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ndb_mgm_node_state
{
public int node_id;
public ndb_mgm_node_type node_type;
public ndb_mgm_node_status node_status;
public int start_phase;
public int dynamic_id;
public int node_group;
public int version;
public int connect_count;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 17)]
public string connect_address;
public int mysql_version;
};
I tried to unmarshal the results without success (i recieve an oddly error (not an Exception) Fatal error execution, can be a CLR bug or a miscall P/invoke.
Obviously the reason is a problem in my P/Invoke call
I tried in this way:
First of all i unmarshalled the ndb_mgm_cluster_state structure:
var res=(ndb_mgm_cluster_state)Marshal.PtrToStructure(
tmpPtr, typeof(ndb_mgm_cluster_state));
where IntPtr is the result of the native call.
Up to this step everything "seems" to be done right, but when i try to unmarshall the node_states i get the error:
ndb_mgm_node_state tmpNode = (ndb_mgm_node_state)Marshal.PtrToStructure(
status.node_states, typeof(ndb_mgm_node_state));
What can be the problem? I supposed is something related to the strange declaration of ndb_mgm_cluster_state because is defined an array of 1 element, but it contain several elements. (the number of elements is in no_of_nodes)
WORKAROUND:
The only way to let everything work i found is to change a bit the signature, in this way:
ndb_mgm_node_state* WINAPI wrap_ndb_mgm_get_status(HANDLE handle,int* length);
in C#:
[DllImport("Ndb_CWrapper.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr wrap_ndb_mgm_get_status(IntPtr handle,out int length);
where length contains no_of_nodes
the unmarshall will be in this way:
IntPtr tmpPtr = wrap_ndb_mgm_get_status(raw,out length);
ndb_mgm_cluster_state tmpRes = new ndb_mgm_cluster_state();
tmpRes.no_of_nodes = length;
tmpRes.node_states = new ndb_mgm_node_state[length];
int step=0;
for (int i = 0; i < tmpRes.no_of_nodes; i++)
{
tmpRes.node_states[i] = (ndb_mgm_node_state)Marshal.PtrToStructure(
tmpPtr+(step*i), typeof(ndb_mgm_node_state));
step = Marshal.SizeOf(tmpRes.node_states[i]);
}
I know the step calculation is odd at the monent, but its not that the point.
there is no way to let the thing work returning directly the ndb_mgm_cluster_state struct instead of do all of this?
I have three C++ structures that I need to send as parameters when calling a DLL method from C#.
I simplified them as the following:
typedef struct
{
int data1;
int data2;
} A;
typedef struct
{
int numStructA;
A *pStructA;
int moreData;
} B;
typedef struct
{
TCHAR *pStr;
B structB;
} C;
The following is the C++ function I need to call from C#:
int func(C *pStructC, int numStructC);
In my C# code, I define the structs as the following:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct A
{
public int data1;
public int data2;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct B
{
public int numStuctA;
public IntPtr structA;
public int moreData;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct C
{
[MarshalAs(UnmanagedType.LPTStr)]
public string Str;
[MarshalAs(UnmanagedType.Struct)]
public B structB;
}
[DllImport("somedll.dll")]
private static extern int func(IntPtr pStructC, int size);
I tried to marshal the nested structures. However, the C++ function does not receive the data correctly. So far only the data that comes before the nested structure is sent correctly to the C++ function.
How can I marshal the nested structures so the C++ method receives the correct data? Thanks in advance.