Could anyone please help me with the following?
I have a dll written in C and want to call a certain function in it from C#.
The function returns a pointer to a structure. Here is the structure:
typedef struct
{
char crv_name[40];
char crv_name2[12];
char units[40];
char creator[24];
char index_units[8];
double first_dep_tim;
double last_dep_tim;
double level_spacing;
EmptyValU empty_val;
long num_ele;
}
Now, I know (from calling this from a C client) that the last member (num_ele) will be set to 1.
Now, that penultimate member is of type EmptyValU which is defined as:
typedef union
{
double d;
float f;
long l;
ulong ul;
short s;
ushort us;
char c;
}EmptyValU;
Now, I can call this ok fom C# and read everything up until empty_val. My value for num_ele is nonsense as I am clearly misaligned in memory after empty_val.
Here is my C# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CurveInfo
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string crv_name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string crv_name2;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string units;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
public string creator;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string index_units;
public double first_dep_tim;
public double last_dep_tim;
public double level_spacing;
[MarshalAs(UnmanagedType.Struct, SizeConst = 8)]
public EmptyValU empty_val;
public long num_ele;
}
and I have defined EmptyValU as:
[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct EmptyValU
{
[FieldOffset(0)]
public double d;
[FieldOffset(0)]
public float f;
[FieldOffset(0)]
long l;
[FieldOffset(0)]
ulong ul;
[FieldOffset(0)]
short s;
[FieldOffset(0)]
ushort us;
[FieldOffset(0)]
char c;
}
Like I say, when I call the function which returns a pointer to such a structure all the values are populated correctly up until the EmptyValU member, which, along with num_ele is not correctly populated. Something about the way I have to define the union in C# is what I am missing in order to keep the alignment correct.
Thanks for any help,
Mitch.
I've solved this.
I C, a long (and ulong) is 4 bytes wide, but in C# they are 8 bytes wide.
So instead of:
[FieldOffset(0)]
long l;
[FieldOffset(0)]
ulong ul;
I should have had:
[FieldOffset(0)]
int l;
[FieldOffset(0)]
uint ul;
becasue in C# int and uint are 4 bytes wide.
For the same reason, on the C# side instead of:
public long num_ele;
I need:
public int num_ele;
Now it all works.
Related
I'm trying to create a wrapper for a C dll to use it in C#. I don't have the source code of the dll.
I'm having problem using a method that gives me "System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt'" when executing. I tried to search online for the same problem but I didn't find what the problem with my code is.
The method that gives the problem is the following:
C:
L3B6_API L3B6_ERR_t STDCALL L3B6_ActivateBoot(int nodeid, unsigned cpunum, unsigned ms_timeout, const char * devicenames, A * dev_param);
the wrapper I created in C# is this:
[DllImport("L3B6.dll", EntryPoint = "L3B6_ActivateBoot")]
return: MarshalAs(UnmanagedType.I4)]
static extern L3B6ErrorCode ActivateBoot(
int nodeid,
uint cpunum,
uint msTimeout,
[MarshalAs(UnmanagedType.LPStr)]
string deviceNames,
ref A devParams
);
Where L3B6ErrorCode is an enum I created but it doesn't give any problem (I used it for other methods that work).
I think the problem is in the structure A. The original structure is like this:
struct A
{
unsigned char blv;
unsigned char cpunum;
unsigned char nodeid;
unsigned char hwverA;
unsigned char hwverB;
unsigned char hwverC;
unsigned char hwverD;
unsigned char cputype;
unsigned char hwcode;
B memini_devrec;
};
struct B
{
unsigned hwCode;
unsigned cpuCode;
unsigned dualCPU;
unsigned cpuNumber;
unsigned internalFlashStart;
unsigned internalFlashEnd;
unsigned externalFlashAccess;
unsigned externalFlashStart;
unsigned externalFlashEnd;
unsigned isResetVectorSpecified;
unsigned resetVectors;
char bootName[32];
unsigned isS19toPhyConversionAllowed;
unsigned noFlashPaging;
char deviceName[32];
char cpuName[32];
};
The corresponding structures I created are the following:
[StructLayout(LayoutKind.Sequential)]
public struct A
{
public byte bootloaderversion;
public byte cpuNum;
public byte nodeId;
public byte hwVerA;
public byte hwVerB;
public byte hwVerC;
public byte hwVerD;
public byte cpuType;
public byte hwCode;
public B meminiDeviceRecord;
};
[StructLayout(LayoutKind.Sequential)]
public struct B
{
public uint hwCode;
public uint cpuCode;
[MarshalAs(UnmanagedType.Bool)]
public bool dualCPU;
public uint cpuNumber;
public uint internalFlashStart;
public uint internalFlashEnd;
public uint externalFlashAccess;
public uint externalFlashStart;
public uint externalFlashEnd;
[MarshalAs(UnmanagedType.Bool)]
public bool isResetVectorSpecified;
public uint resetVectors;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string bootName;
[MarshalAs(UnmanagedType.Bool)]
public bool isS19toPhyConversionAllowed;
[MarshalAs(UnmanagedType.Bool)]
public bool noFlashPaging;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string deviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string cpuName;
};
I've used the structure B in another method that needed to fill it and didn't have any problem, so I suppose the problem is in the A structure, but I can't understand what I'm doing wrong. I think the problem is that structure because also another method that takes that structure as parameter is giving me the same exception. I checked the size of the structs in C and C# and it is the same.
I am receiving data packets over UDP that I parse into structs and in a specific situation my data gets all messed up and I can't figure out what the problem might be. Let me give you an overview of what I am doing.
UDP Receiver receives the data packet and passes it to a callback - size of the byte array at this stage is 1307 bytes which is the expected size for the packet
The byte array is marshalled (is that the right term?) into a struct - size of the struct is now 1484 bytes (not sure if that is relevant)
In this example the data is send from the Formula 1 2020 game and the curious thing is that the first entry in the m_carTelemetryData array (see below) is always fine, and the data is all sound. However, every entry of the 22 after the first one is totally messed up with either 0 values, null values or completely outlandish values for all the different fields in the struct.
I tried several things already to pinpoint the issue, but I have now reached the end of my knowledge about the things I am dealing with here. My best guess is that something is going wrong when converting the data into a struct or something else is going on that causes a misalignment (?) of the data.
What I tried so far
Changed my code from "magic marshalling" to manual reading the data byte-by-byte using BinaryReader - no luck
Manually checked the data using BitConverter.ToFoo(bytes, offset) - no luck
Yolo changed the Pack attribute assuming that's where I got wrong - no luck
Double-checked the documentation to make sure I got the data types right - I am fairly confident that I "translated" them correctly
Banged my head against a wall - still no luck
My questions:
Is there something obvious wrong with my code that I am simply not seeing?
Side question: am I wrong in my assumption that the size of the struct should match the size of the byte array it has been created from?
Here is the code for reference (if anything helpful is missing, please let me know):
Packet Header
[StructLayout(LayoutKind.Sequential), Pack = 1]
struct PacketHeader2020 {
public ushort m_packetFormat;
public byte m_gameMajorVersion;
public byte m_gameMinorVersion;
public byte m_packetVersion;
public byte m_packetId;
public ulong m_sessionUUID;
public float m_sessionTime;
public uint m_frameIdentifier;
public byte m_playerCarIndex;
public byte m_secondaryPlayerCarIndex;
}
Packet
public struct CarTelemetryPacket2020
{
public PacketHeader2020 m_header;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)]
public CarTelemetryData2020[] m_carTelemetryData;
public ButtonFlag m_buttonStatus;
public byte m_mfdPanelIndex;
public byte m_mfdPanelIndexSecondaryPlayer;
public byte m_suggestedGear;
};
CarTelemetryData2020
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CarTelemetryData2020
{
public ushort m_speed;
public float m_throttle;
public float m_steer;
public float m_brake;
public byte m_clutch;
public sbyte m_gear;
public ushort m_engineRPM;
public byte m_drs;
public byte m_revLightsPercent;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public ushort[] m_brakesTemperature;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public ushort[] m_tyresSurfaceTemperature;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public ushort[] m_tyresInnerTemperature;
public ushort m_engineTemperature;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public float[] m_tyresPressure;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public SurfaceType[] m_surfaceType;
}
byte[] -> struct
public static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try
{
return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
}
finally
{
handle.Free();
}
}
I just pasted your code into LINQPad and found a few problems. The CarTelemetryPacket2020 structure needed a pack clause. Also m_tyresSurfaceTemperature and m_tyresInnerTemperature should have been a byte. The following returns a size of 1307 as per the protocol specification. You're right that the sizes should have matched up and I don't see any other obvious problems with your code.
void Main()
{
System.Runtime.InteropServices.Marshal.SizeOf(typeof(CarTelemetryPacket2020)).Dump();
}
[StructLayout(LayoutKind.Sequential, Pack = 1) ]
public struct PacketHeader2020
{
public ushort m_packetFormat;
public byte m_gameMajorVersion;
public byte m_gameMinorVersion;
public byte m_packetVersion;
public byte m_packetId;
public ulong m_sessionUUID;
public float m_sessionTime;
public uint m_frameIdentifier;
public byte m_playerCarIndex;
public byte m_secondaryPlayerCarIndex;
}
// Added pack = 1
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CarTelemetryPacket2020
{
public PacketHeader2020 m_header;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)]
public CarTelemetryData2020[] m_carTelemetryData;
public UInt32 m_buttonStatus;
public byte m_mfdPanelIndex;
public byte m_mfdPanelIndexSecondaryPlayer;
public byte m_suggestedGear;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CarTelemetryData2020
{
public ushort m_speed;
public float m_throttle;
public float m_steer;
public float m_brake;
public byte m_clutch;
public sbyte m_gear;
public ushort m_engineRPM;
public byte m_drs;
public byte m_revLightsPercent;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public ushort[] m_brakesTemperature;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
// Changed following to byte
public byte[] m_tyresSurfaceTemperature;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
// Changed following to byte
public byte[] m_tyresInnerTemperature;
public ushort m_engineTemperature;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public float[] m_tyresPressure;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] m_surfaceType;
}
I'm write a plugin with c# . the program with c# will use a dll which writes by c ,so I have to call c function in my c# program ,but unfortunately the c function's parameter is a struct and it is so complex that I never find any
help information about how to convert it to c# parameter.
the struct has an embedded function and void* parameter ,I didn't find anyway to convert them to c#.
the struct is mostly like this
struct first_struct{
char* parameter1;
int parameter2;
unsigned long parameter3;
unsigned short parameter4;
void* parameter5;
int (*parameter6)(int,void *);
second_struct parameter7;
};
struct second_struct{
char parameter8[64] ;
char parameter9[256];
};
I want change this c struct to c# struct but I have no idea how to do it
thanks #Biesi Grr and #Ian Abbott ‘s help it sames that I can change the c struct to c# like that.
public delegate int parameter6(int volcnt, System.IntPtr vod);
[StructLayout(LayoutKind.Sequential)]
public struct csharp_firstSturct
{
[MarshalAs(UnmanagedType.LPStr)]
public string parameter1;
public int parameter2;
public ulong parameter3;
public ushort parameter4;
public System.IntPtr parameter5;
public parameter6 m_parameter6;
}
but I still has no idea about how to convert parameter7 from C to C#,what should I do to convert parameter7 form c to c#.
Assuming your char arrays are null terminated strings you can do it the following way
public delegate int parameter6(int volcnt, System.IntPtr vod);
[StructLayout(LayoutKind.Sequential)]
public struct csharp_firstSturct
{
[MarshalAs(UnmanagedType.LPStr)]
public string parameter1;
public int parameter2;
public ulong parameter3;
public ushort parameter4;
public System.IntPtr parameter5;
public parameter6 m_parameter6;
[MarshalAs(UnmanagedType.Struct)]
public second_struct parameter7;
}
[StructLayout(LayoutKind.Sequential)]
public struct second_struct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string parameter8;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string parameter9;
}
I'm trying to call a C DLL in my C# project using marshaling and have some functions working but I have trouble with others. Like the one below.
My first problem is getting the structs correct and next problem is to return PROFILE_INFO as an array with the list of program files or maybe it won't return a list and proNum is an index.
function in C
extern "C" __declspec(dllexport) int WINAPI GetProgramFileList (unsigned long proNum, PROFILE_INFO *proFile);
typedef struct{
PROINFO proInfo;
__int64 proSize;
PROGRAM_DATE createDate;
PROGRAM_DATE writeDate;
}PROFILE_INFO;
typedef struct{
char wno[33];
char dummy[7];
char comment[49];
char dummy2[7];
char type;
char dummy3[7];
}PROINFO;
typedef struct{
short year;
char month;
char day;
char hour;
char min;
char dummy[2];
}PROGRAM_DATE;
My function
[DllImport(#".\IFDLL.dll", EntryPoint = "GetProgramFileList", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern int GetProgramFileListTest(ulong proNum, ref PROFILE_INFO pro);
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct PROFILE_INFO
{
[MarshalAs(UnmanagedType.Struct)]
public PROINFO proInfo; // WNo/name/type
public long proSize; // Program size
[MarshalAs(UnmanagedType.Struct)]
public PROGRAM_DATE createDate; // Program creating date
[MarshalAs(UnmanagedType.Struct)]
public PROGRAM_DATE writeDate; // Program updating date
}
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct PROINFO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)] public string wno; // WNo.
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy; // dummy
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 49)] public string comment; // program name
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy2; // dummy
public char type; // program type
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy3; // dummy
}
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct PROGRAM_DATE
{
public short year; // Date (Year) 4-digit
public char month; // Date (Month)
public char day; // Date (Day)
public char hour; // Date (Time)
public char min; // Date (Minutes)
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)] private char dummy; // Dummy
}
The PROGRAM_DATE createDate in C# struct PROFILE_INFO will throw:
Cannot marshal field 'createDate' of type 'CClient.Models.PROFILE_INFO': The type definition of this field has layout information but has an invalid managed/unmanaged type combination or is unmarshalable.
Changing PROGRAM_DATE fields to string makes it accept it but the function returns an argument(-60) error instead. Though not sure if I'm closer to success.
Other attempts, including attempts to get PROFILE_INFO to return as an array (ref PROFILE_INFO[]), landed in:
A call to PInvoke function has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature.
I have got these descriptions following the C dll:
Explanation
Obtain the program list in the standard area
Argument
proNum [in]
Specify the number of the data to be obtained.
proFile [out]
Store the program information in the standard area, in PROFILE_INFO type.
Before executing this function, make sure to ensure the data area for the number of the data to be obtained.
Return value
If it succeeded, “0” is returned. If there is an error, a value other than “0” is returned.
Other functions I have working is GetProgramDirInfo, SendProgram, ReceiveProgram, SearchProgram, etc. but they don't return any arrays, so I assume marshaling arrays is my problem here. Also I'm trying to avoid using unsafe pointers and I'm unsure if I need to do the copying myself.
Any help is appreciated.
Few points when working with p/invoke:
don't add what's obvious for .NET (structs are structs, ...)
don't add pack if you're unsure, by default .NET should behave like C/C++ on that matter
don't add Ansi information if there's no string in the definition (only when there are strings or TStr, etc.). It doesn't cause problems but is useless
in general, don't add attributes if you don't know what they are used for
int and long in C/C++ are (in general) 32-bit. long is not 64-bit in C/C++
Here is a code that should be better:
[DllImport(#".\IFDLL.dll", EntryPoint = "GetProgramFileList")]
public static extern int GetProgramFileListTest(uint proNum, ref PROFILE_INFO pro);
[StructLayout(LayoutKind.Sequential)]
public struct PROFILE_INFO
{
public PROINFO proInfo; // WNo/name/type
public long proSize; // Program size
public PROGRAM_DATE createDate; // Program creating date
public PROGRAM_DATE writeDate; // Program updating date
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct PROINFO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)] public string wno; // WNo.
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy; // dummy
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 49)] public string comment; // program name
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy2; // dummy
public char type; // program type
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)] private string dummy3; // dummy
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct PROGRAM_DATE
{
public short year; // Date (Year) 4-digit
public char month; // Date (Month)
public char day; // Date (Day)
public char hour; // Date (Time)
public char min; // Date (Minutes)
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)] private string dummy; // Dummy
}
I have the following C function:
int w_ei_connect_init(ei_cnode* ec, const char* this_node_name,
const char *cookie, short creation);
ei_cnode looks like this:
typedef struct ei_cnode_s {
char thishostname[EI_MAXHOSTNAMELEN+1];
char thisnodename[MAXNODELEN+1];
char thisalivename[EI_MAXALIVELEN+1];
char ei_connect_cookie[EI_MAX_COOKIE_SIZE+1];
short creation;
erlang_pid self;
} ei_cnode;
Which I have converted to C#:
[StructLayout(LayoutKind.Sequential)]
public struct cnode {
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_HOSTNAME_LEN + 1)]
public string thishostname;
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_NODE_LEN + 1)]
public string thisnodename;
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_ALIVE_LEN + 1)]
public string thisalivename;
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_COOKIE_SIZE + 1)]
public string ei_connect_cookie;
public short creation;
public erlang_pid self;
}
I'm not good with pointers or C in general, so I'm not sure how I'm supposed to supply a cnode to ei_connect_init.
What would the equivalent C# signature be for the C function above?
Whenever you want to pass a C# struct to a parameter value containing the equivalent native struct but with a pointer, the typical method is to label the parameter as "ref". This causes the PInvoke layer to essentially pass the address in.
[DllImport("somedll")]
public static extern w_ei_connect_init(
ref cnode v,
[In] string this_node_name,
[In] string cookie,
int16 creation);
Something like this should work:
int w_ei_connect_init(ref cnode ec,
[MarshalAs(UnmanagedType.LPStr)] string this_node_name,
[MarshalAs(UnmanagedType.LPStr)] string cookie, short creation);
You should also consider marking your struct with
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
attribute, so those TStr will be ansi-strings, not unicode.