How do I define the TBBUTTON struct in C#? - c#

Greetings all,
The TBBUTTON struct is defined on MSDN as follows:
typedef struct {
int iBitmap;
int idCommand;
BYTE fsState;
BYTE fsStyle;
#ifdef _WIN64
BYTE bReserved[6];
#else
#if defined(_WIN32)
BYTE bReserved[2];
#endif
#endif
DWORD_PTR dwData;
INT_PTR iString;
} TBBUTTON, *PTBBUTTON, *LPTBBUTTON;
I need to do some interop in C# using this struct. How do I replicate this monster so that it's defined correctly when compiled for AnyCPU? Google is apparently full of dangerous misinformation!

Ahah, I knew there had to be a way. And here it is:
[StructLayout(LayoutKind.Sequential)]
public struct TBBUTTON {
public int iBitmap;
public int idCommand;
[StructLayout(LayoutKind.Explicit)]
private struct TBBUTTON_U {
[FieldOffset(0)] public byte fsState;
[FieldOffset(1)] public byte fsStyle;
[FieldOffset(0)] private IntPtr bReserved;
}
private TBBUTTON_U union;
public byte fsState { get { return union.fsState; } set { union.fsState = value; } }
public byte fsStyle { get { return union.fsStyle; } set { union.fsStyle = value; } }
public UIntPtr dwData;
public IntPtr iString;
}
Marshal.SizeOf returns 32 on x64 processes and 20 on x86 processes, and everything ends up where it should when I pass this to SendMessage. I knew you could do it, C#!

Your best bet is to define two versions, one for 32 bit and one for 64 bit.
public struct TBBUTTON32
{
int iBitmap;
int idCommand;
byte fsState;
byte fsStyle;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2)]
byte[] bReserved;
UIntPtr dwData;
IntPtr iString;
}
The 64 bit version is just the same but with SizeConst = 6 on the reserved bytes array.
Then you need to switch between them at runtime. Your C# code will have to detect whether it's running as a 32 or 64 bit process.

Related

Marshaling c# call to DLL library

I am having trouble calling a DLL library with a specific signature. This is on the xamarin platform and running on iOS. I am trying to get a list of DNS servers. I can call other functions without an issue, but the one I really need is not working. I am sure its becuase my struct is not correct. I believe the issue is the "union" type. I just dont know what my struct should look like to appease that parameter. Here are the relevant c++ types:
1: The DLL singature
`int res_getservers __P((res_state, union res_sockaddr_union * , int));`
2: The c++ structs
union res_sockaddr_union {
struct sockaddr_in sin;
#ifdef IN6ADDR_ANY_INIT
struct sockaddr_in6 sin6;
#endif
#ifdef ISC_ALIGN64
int64_t __align64; /* 64bit alignment */
#else
int32_t __align32; /* 32bit alignment */
#endif
char __space[128]; /* max size */
};
struct sockaddr_in6 {
__uint8_t sin6_len; /* length of this struct(sa_family_t) */
sa_family_t sin6_family; /* AF_INET6 (sa_family_t) */
in_port_t sin6_port; /* Transport layer port # (in_port_t) */
__uint32_t sin6_flowinfo; /* IP6 flow information */
struct in6_addr sin6_addr; /* IP6 address */
__uint32_t sin6_scope_id; /* scope zone index */
};
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
Any help on generating the proper c# structs would be greatly appreciated! Thank you.
Disclaimer: I don't have C++ installed on my machine any more but I believe this will either work, or at least give you a good shove in the right direction.
Based on: https://msdn.microsoft.com/en-us/library/windows/desktop/ms738571(v=vs.85).aspx
And: https://learn.microsoft.com/en-us/cpp/mfc/reference/sockaddr-in-structure
And: https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute(v=vs.110).aspx
And numerous other Google searches for the types in question.
using System;
using System.Runtime.InteropServices;
namespace SockStructTest
{
class Program
{
[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct struct_sin_zero
{
[FieldOffset(0)]
public byte s_b1;
[FieldOffset(1)]
public byte s_b2;
[FieldOffset(2)]
public byte s_b3;
[FieldOffset(3)]
public byte s_b4;
[FieldOffset(4)]
public byte s_b5;
[FieldOffset(5)]
public byte s_b6;
[FieldOffset(6)]
public byte s_b7;
[FieldOffset(7)]
public byte s_b8;
[FieldOffset(0)]
public UInt64 s_ui64;
};
[StructLayout(LayoutKind.Explicit, Size = 4)]
public struct in_addr
{
[FieldOffset(0)]
public byte s_b1;
[FieldOffset(1)]
public byte s_b2;
[FieldOffset(2)]
public byte s_b3;
[FieldOffset(3)]
public byte s_b4;
[FieldOffset(0)]
public UInt16 s_w1;
[FieldOffset(2)]
public UInt16 s_w2;
[FieldOffset(0)]
public UInt32 S_addr;
};
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct sockaddr_in
{
[FieldOffset(0)]
public Int16 sin_family;
[FieldOffset(2)]
public UInt16 sin_port;
[FieldOffset(4)]
public in_addr sin_addr;
[FieldOffset(8)]
public struct_sin_zero sin_zero;
};
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct in6_addr_u
{
[FieldOffset(0)]
public byte Byte0;
[FieldOffset(1)]
public byte Byte1;
[FieldOffset(2)]
public byte Byte2;
[FieldOffset(3)]
public byte Byte3;
[FieldOffset(4)]
public byte Byte4;
[FieldOffset(5)]
public byte Byte5;
[FieldOffset(6)]
public byte Byte6;
[FieldOffset(7)]
public byte Byte7;
[FieldOffset(8)]
public byte Byte8;
[FieldOffset(9)]
public byte Byte9;
[FieldOffset(10)]
public byte ByteA;
[FieldOffset(11)]
public byte ByteB;
[FieldOffset(12)]
public byte ByteC;
[FieldOffset(13)]
public byte ByteD;
[FieldOffset(14)]
public byte ByteE;
[FieldOffset(15)]
public byte ByteF;
[FieldOffset(0)]
public UInt16 Word0;
[FieldOffset(2)]
public UInt16 Word1;
[FieldOffset(4)]
public UInt16 Word2;
[FieldOffset(6)]
public UInt16 Word3;
[FieldOffset(8)]
public UInt16 Word4;
[FieldOffset(10)]
public UInt16 Word5;
[FieldOffset(12)]
public UInt16 Word6;
[FieldOffset(14)]
public UInt16 Word7;
}
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct in6_addr
{
[FieldOffset(0)]
public in6_addr_u u;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct sockaddr_in6
{
public UInt64 sin6_len; // length of this struct(sa_family_t)
public UInt32 sin6_family; // AF_INET6 (sa_family_t)
public UInt16 sin6_port; // Transport layer port # (in_port_t)
public UInt32 sin6_flowinfo; // IP6 flow information
public in6_addr sin6_addr; // IP6 address
public UInt32 sin6_scope_id; // scope zone index
};
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct res_sockaddr_union
{
[FieldOffset(0)]
public sockaddr_in sin;
[FieldOffset(0)]
public sockaddr_in6 sin6;
[FieldOffset(0)]
public Int64 __align64;
[FieldOffset(0)]
public Int32 __align32;
// This is the char __space[128] bit.
// I've cheated and used 16 8 byte values.
[FieldOffset(0)]
public UInt64 __space0;
[FieldOffset(8)]
public UInt64 __space1;
[FieldOffset(16)]
public UInt64 __space2;
[FieldOffset(24)]
public UInt64 __space3;
[FieldOffset(32)]
public UInt64 __space4;
[FieldOffset(40)]
public UInt64 __space5;
[FieldOffset(48)]
public UInt64 __space6;
[FieldOffset(56)]
public UInt64 __space7;
[FieldOffset(64)]
public UInt64 __space8;
[FieldOffset(72)]
public UInt64 __space9;
[FieldOffset(80)]
public UInt64 __spaceA;
[FieldOffset(88)]
public UInt64 __spaceB;
[FieldOffset(96)]
public UInt64 __spaceC;
[FieldOffset(104)]
public UInt64 __spaceD;
[FieldOffset(112)]
public UInt64 __spaceE;
[FieldOffset(120)]
public UInt64 __spaceF;
}
static void Main(string[] args)
{
sockaddr_in ad;
ad.sin_zero.s_ui64 = 0;
ad.sin_family = 0; // or whatever
ad.sin_port = 1234;
// Address as b bit values
ad.sin_addr.s_b1 = 0;
ad.sin_addr.s_b2 = 1;
ad.sin_addr.s_b3 = 2;
ad.sin_addr.s_b4 = 3;
// Address as 16 bits
ad.sin_addr.s_w1 = 0;
ad.sin_addr.s_w2 = 0;
}
}
}
Best I can do - don't feel under pressure to accept the answer if it doesn't solve your issue but please do take some time to try to understand what's going on in the example I've given you.
I haven't had chance to test it - but it does build.
Good luck!
Regards,
Adam.

C# P/Invoke 2 dimensional array

I am trying to call a C function from C# code:
typedef char IPTriggerNameType[256];
typedef unsigned long COMM_HANDLE;
typedef BYTE GUID_TYPE[16];
typedef long LONGINT;
typedef struct {
GUID_TYPE Guid;
IPTriggerNameType Name;
}IPCAM_GENERIC_EVENT_ID;
typedef struct {
IPCAM_GENERIC_EVENT_ID EventId;
LONGINT RelatedTriggerId;
LONGINT ObsoleteEvent;
}IPCAM_GENERIC_EVENT_INFO;
typedef struct
{
LONGINT NumOfEvents;
IPCAM_GENERIC_EVENT_INFO *GenericEventsList;
}VID_CHANNEL_GENERIC_EVENTS_STRUCT;
int __stdcall GetGenericEvents(
/* Inputs: */
COMM_HANDLE Handle,
LONGINT MaxNumOfChannelsInTable,
LONGINT MaxNumOfEventsPerChannel,
/* Outputs: */
LONGINT *NumOfChannels,
VID_CHANNEL_GENERIC_EVENTS_STRUCT *ChannelsEventsTable);
and the C# equivalent is as follows:
[StructLayout(LayoutKind.Sequential)]
public struct IPCAM_GENERIC_EVENT_ID
{
[MarshalAs(UnmanagedType.LPArray, SizeConst = 16)]
public byte[] Guid;
[MarshalAs(UnmanagedType.LPArray, SizeConst = 256)]
public char[] Name;
};
[StructLayout(LayoutKind.Sequential)]
public struct IPCAM_GENERIC_EVENT_INFO
{
public IPCAM_GENERIC_EVENT_ID EventId;
public int RelatedTriggerId;
public int ObsoleteEvent;
}
[StructLayout(LayoutKind.Sequential)]
public struct VID_CHANNEL_GENERIC_EVENTS_STRUCT
{
public int NumOfEvents;
[MarshalAs(UnmanagedType.LPArray, SizeConst = 100)]
public IPCAM_GENERIC_EVENT_INFO[] GenericEventsList;
}
[DllImport(dllName)]
public static extern int GetGenericEvents(
/* Inputs: */
int Handle,
int MaxNumOfChannelsInTable,
int MaxNumOfEventsPerChannel,
/* Outputs: */
out int NumOfChannels,
out VID_CHANNEL_GENERIC_EVENTS_STRUCT[] ChannelsEventsTable);
int numOfChannels = 16, actualNumOfChannels = 0;
int maxNumOfEvents = 100;
VID_CHANNEL_GENERIC_EVENTS_STRUCT[] genericEventsList = new VID_CHANNEL_GENERIC_EVENTS_STRUCT[numOfChannels];
for (int i = 0; i < numOfChannels; i++)
{
genericEventsList[i].GenericEventsList = new IPCAM_GENERIC_EVENT_INFO[maxNumOfEvents];
}
GetGenericEvents(conn, numOfChannels, maxNumOfEvents, out actualNumOfChannels, out genericEventsList);
when calling the C# method I get exception which crashes the application:
Managed Debugging Assistant 'FatalExecutionEngineError' has detected a
problem in 'My.exe'.
Additional information: The runtime has encountered a fatal error. The
address of the error was at 0x72a6cf41, on thread 0x5a98. The error
code is 0xc0000005. This error may be a bug in the CLR or in the
unsafe or non-verifiable portions of user code. Common sources of this
bug include user marshaling errors for COM-interop or PInvoke, which
may corrupt the stack.
What am I doing wrong ?
The field:
VID_CHANNEL_GENERIC_EVENTS_STRUCT.GenericEventsList
is a pointer, declared as:
IPCAM_GENERIC_EVENT_INFO *GenericEventsList
But you are declaring it as an array, included in the struct:
[MarshalAs(UnmanagedType.LPArray, SizeConst = 100)]
public IPCAM_GENERIC_EVENT_INFO[] GenericEventsList;
You may try to use an IntPtr and Marshal.AllocHGlobal. Check this out: How use pinvoke for C struct array pointer to C#

Translating C++ DLL into C# - How to translate a struct included in a struct

I have to translate a C++ DLL into C# to use it in a project. Here is the part that causes me trouble. This is the C++ code of the header of the DLL :
struct initiate_rb
{
unsigned char Rem_Add;
unsigned char Features_Supported_1_m;
struct add_addr_param Add_Addr_Param;
};
struct add_addr_param
{
unsigned char D_Type;
unsigned char D_Len;
struct
{
unsigned char Network_Address[6];
unsigned short MAC_Address_Len;
unsigned char * MAC_Address;
} S_Addr;
};
I am not sure how to handle this in C#. Here is what I have achieved so far :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct initiate_rb
{
public byte Rem_Add;
public byte Features_Supported_1_m;
public add_addr_param Add_Addr_Param;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct add_addr_param
{
public byte D_Type;
public byte D_Len;
[StructLayout(LayoutKind.Sequential)]
public struct S_Addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Network_Address;
public ushort MAC_Address_Len;
public byte* MAC_Address;
};
}
I used Pack = 1 because in my DLL header file there is the line #pragma pack (1)
The problem is, this doesn't work when I have to use this struct. It returns me an error code.
So first, concerning this struct translation, am I doing it right ?
Thanks for helping.
I'm not sure about the attributes but I believe the second struct shoudn't be nested in C# because the one in C++ defines a variable of the struct's type just after the closing parentheses.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct dpc2_add_addr_param
{
public byte D_Type;
public byte D_Len;
public S_Addr S_Addr;
}
[StructLayout(LayoutKind.Sequential)]
public struct S_Addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Network_Address;
public ushort MAC_Address_Len;
public byte* MAC_Address;
};
In fact I resolved this problem. The problem was that I was using a constructor for my initiate_rb struct
unsafe struct initiate_rb
{
public byte Rem_Add;
public byte Features_Supported_1_m;
public add_addr_param Add_Addr_Param;
public initiate_rb(int networkAddressLength) : this()
{
Rem_Add = (byte)0;
Features_Supported_1_m = (byte)0;
add_addr_param = new Add_Add_Param(networkAddressLength);
}
}
and this constructor for add_addr_param :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct add_addr_param
{
public byte D_Type;
public byte D_Len;
[StructLayout(LayoutKind.Sequential)]
public struct S_Addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Network_Address;
public ushort MAC_Address_Len;
public byte* MAC_Address;
};
public add_addr_param(int networkAddressLength) : this()
{
D_Type = (byte)0;
D_Len = (byte)0;
S_Addr.Network_Address = new byte[6]; //problem with this line
}
}
This was the problem. Since I remove this line, the code was operating correctly. In fact I didn't need constructors since all parameters have to be set to 0.
Thanks for helping!

Cannot p/invoke C method from C# due to marshalling issues

I'm trying to invoke the I2CTransfer function below, and immediately getting a System.NotSupportedException. I suspect my marshalling is wrong, but cannot work out the problem.
Here are the C structures:
BOOL I2CTransfer(HANDLE hDev, PI2C_TRANSFER_BLOCK pI2CTransferBlock);
typedef struct {
I2C_PACKET *pI2CPackets;
INT32 iNumPackets;
} I2C_TRANSFER_BLOCK, *PI2C_TRANSFER_BLOCK;
typedef struct {
BYTE byAddr;
BYTE byRW;
PBYTE pbyBuf;
WORD wLen;
LPINT lpiResult;
} I2C_PACKET, *PI2C_PACKET;
And here are the c# structures I'm attempting:
[DllImport("i2csdk.dll", EntryPoint = "I2CTransfer")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool I2CTransfer(IntPtr hI2C,ref I2C_TRANSFER_BLOCK pI2CTransferBlock);
[StructLayout(LayoutKind.Sequential)]
public struct I2C_TRANSFER_BLOCK
{
public I2C_PACKET[] pI2CPackets;
public int iNumPackets;
}
[StructLayout(LayoutKind.Sequential)]
public struct I2C_PACKET
{
public byte byAddr;
public byte byRW;
public byte[] pbyBuf;
public UInt16 wLen;
public IntPtr lpiResult;
}
Calling code:
I2C_TRANSFER_BLOCK i2CTransferBlock = new I2C_TRANSFER_BLOCK();
I2C_PACKET packet = new I2C_PACKET();
int result;
IntPtr resultPtr = IntPtr.Zero;
//Populating data...
byte[] pBuf = new byte[1 + pbData.Length];
pBuf[0] = (byte) ((regStart & 0x7F) << 1);
Array.Copy(pbData, 0, pBuf, 1, pbData.Length);
// Fill packet for register write
packet.pbyBuf = pBuf;
packet.wLen = (ushort) pBuf.Length;
packet.byRW = NativeConstants.I2C_RW_WRITE;
packet.byAddr = address;
packet.lpiResult = resultPtr;
// Fill transfer block
i2CTransferBlock.pI2CPackets = new I2C_PACKET[] {packet};
i2CTransferBlock.iNumPackets = 1;
// NotSupportedException here
bool brc = I2CTransfer(port, ref i2CTransferBlock);
The arrays are initialized in C# before calling the method.
I've tried adding
[MarshalAs(UnmanagedType.LPArray)]
to the arrays (pI2cPackets, and pbyBuf) to no avail.
This is on Windows CE - compact framework, .NET 3.5.
Is there something obviously wrong with the above translation?
Many thanks in advance.
By no means I'm an expert on Marshaling but i think i'll throw in few ideas just in case.
1) try to manually marshal arrays (as IntPtr) by allocating memory for them in your code.
2) This line IntPtr resultPtr = IntPtr.Zero; looks suspicious. Normally when you pass a pointer to unmanaged code from managed code it is your job to allocate (and free) memory for this pointer. Check this out for details http://msdn.microsoft.com/en-us/library/0szztey7%28v=VS.90%29.aspx
Your problems are in the pointers contained in the structs:
[StructLayout(LayoutKind.Sequential)]
public struct I2C_TRANSFER_BLOCK
{
public I2C_PACKET[] pI2CPackets; // here ....
public int iNumPackets;
}
[StructLayout(LayoutKind.Sequential)]
public struct I2C_PACKET
{
public byte byAddr;
public byte byRW;
public byte[] pbyBuf; // .... and here
public UInt16 wLen;
public IntPtr lpiResult;
}
You cannot persuade the p/invoke marshaller to marshal pointers to arrays that are embedded inside structs. That form of marshalling is only available for function parameters.
You'll need to declare both of those fields as IntPtr and do the marshalling by hand.

How use struct with union in c#

Hi I writing a wrapper in c# and i have some problem. I have this struct in c++.
typedef struct pjmedia_format
{
pj_uint32_t id;
pjmedia_type type;
pjmedia_format_detail_type detail_type;
union
{
pjmedia_audio_format_detail aud;
pjmedia_video_format_detail vid;
char user[PJMEDIA_FORMAT_DETAIL_USER_SIZE];
} det;
} pjmedia_format;
This is link to this struct pjmedia_format
in c# I have this:
[StructLayout(LayoutKind.Sequential)]
public struct pjmedia_format
{
public uint id;
public pjmedia_type type;
public pjmedia_format_detail_type detail_type;
public det_t det;
}
[StructLayout(LayoutKind.Explicit)]
public struct det_t
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.Struct)]
public pjmedia_audio_format_detail aud;
[FieldOffset(36)]
[MarshalAs(UnmanagedType.Struct)]
public pjmedia_video_format_detail vid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
[FieldOffset(60)]
public char[] user;
}
[StructLayout(LayoutKind.Sequential)]
public struct pjmedia_audio_format_detail
{
public uint clock_rate;
public uint channel_count;
public uint frame_time_usec;
public uint bits_per_sample;
public int avg_bps;
public int max_bps;
}
[StructLayout(LayoutKind.Sequential)]
public struct pjmedia_video_format_detail
{
public pjmedia_rect_size size;
public pjmedia_ratio fps;
public int avg_bps;
public int max_bps;
}
and when i want to use this struct i get this error
System.Runtime.InteropServices.MarshalDirectiveException was unhandled.
Message="A method signature is not PInvoke compatible with the element."
I try to use some attributes like size or pack but it doesn't help (probably i use it wrong). I tested singly other struct e.g. pjmedia_video_format_detail and they works well. Any advice?
Best regards
Andrzej
As this is a union, shouldn't that be:
[StructLayout(LayoutKind.Explicit)]
public struct det_t
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.Struct)]
public pjmedia_audio_format_detail aud;
[FieldOffset(0)]
[MarshalAs(UnmanagedType.Struct)]
public pjmedia_video_format_detail vid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
[FieldOffset(0)]
public char[] user;
}
i.e. overlapping? Also, you might need user to be a fixed buffer rather than an array.
In the C++ code, det is a union. That means that all the fields have zero offset, they are overlayed. Simply change your C# declaration to match by using [FieldOffset(0)] for all fields in det_t.

Categories

Resources