Click to See Complete Forum and Search --> : [RESOLVED] Marshalling Structure to kernel32.dll


RaleTheBlade
August 31st, 2009, 04:56 PM
Very simple example but I cant seem to get it to work accurately, I have created a managed version of the DCB structure within the Windows.h header file:


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 8, Size = 28)]
internal struct DCB
{
public DWORD DCBlength; // sizeof(DCB)
public DWORD BaudRate; // current baud rate
public DWORD fBinary; // binary mode, no EOF check
public DWORD fParity; // enable parity checking
public DWORD fOutxCtsFlow; // CTS output flow control
public DWORD fOutxDsrFlow; // DSR output flow control
public DWORD fDtrControl; // DTR flow control type
public DWORD fDsrSensitivity; // DSR sensitivity
public DWORD fTXContinueOnXoff; // XOFF continues Tx
public DWORD fOutX; // XON/XOFF out flow control
public DWORD fInX; // XON/XOFF in flow control
public DWORD fErrorChar; // enable error replacement
public DWORD fNull; // enable null stripping
public DWORD fRtsControl; // RTS flow control
public DWORD fAbortOnError; // abort on error
public DWORD fDummy2; // reserved
public WORD wReserved; // not currently used
public WORD XonLim; // transmit XON threshold
public WORD XoffLim; // transmit XOFF threshold
public BYTE ByteSize; // number of bits/byte, 4-8
public BYTE Parity; // 0-4=no,odd,even,mark,space
public BYTE StopBits; // 0,1,2 = 1, 1.5, 2
public CHAR XonChar; // Tx and Rx XON character
public CHAR XoffChar; // Tx and Rx XOFF character
public CHAR ErrorChar; // error replacement character
public CHAR EofChar; // end of input character
public CHAR EvtChar; // received event character
public WORD wReserved1; // reserved; do not use
}


The datatypes have been coerced with "using" statements to be as close to the Win32 data types Microsoft has been using when developing Win32 C++ application. ex. DWORD = System.UInt32 and CHAR = System.SByte.

Here is my function "prototype" for the function I am attempting to use:


[DllImport("kernel32.dll", SetLastError = true)]
private static unsafe extern bool GetCommState(SafeFileHandle hFile, DCB* lpDCB);


Seems ok to me... But when you call the GetCommState function passing in a handle to an open serial port, you end up getting back a DCB structure which has the first few fields properly set (BaudRate, DCBlength) but a few of the fields are really odd. For example:

fBinary = 6161
fParity = 134217728
fOutxCtsFlow = 524800

At first I thought it may have been a data misalignment problem. But I set the LayoutKind to Explicit and went through the structure and set the FieldOffset attribute for each member manually and it STILL was messed up. I cant seem to set the Parity, StopBits, and some of the other fields using the function SetCommState either since my DCB structure is messed up. Anyone got anyone ideas?

Mutant_Fruit
August 31st, 2009, 05:18 PM
Leave out the Pack = 8 and Size=28 parameters unless you *really* know what you're doing and fully understand exactly what they do. My guess is you're forcing a packing which you shouldn't be using which is making the data be stored in the wrong location.

RaleTheBlade
August 31st, 2009, 05:21 PM
I did that beforehand and it still didn't work. They're there as one of my flailing attempts to try to get it working. I keep thinking its a data misalignment problem but I don't see how... All of the data types are precise CLR substitutes for Win32 data types. Could you try the code on your own machine and see if it works? I'm using a CP-US-03 adapter but I dont know if that will make a difference. Stupid USB serial adapters... theyve always been problematic for us even before I came along 2 years ago. Ugh!

darwen
August 31st, 2009, 05:31 PM
DCB is defined as :


typedef struct _DCB {
DWORD DCBlength;
DWORD BaudRate;
DWORD fBinary :1;
DWORD fParity :1;
DWORD fOutxCtsFlow :1;
DWORD fOutxDsrFlow :1;
DWORD fDtrControl :2;
DWORD fDsrSensitivity :1;
DWORD fTXContinueOnXoff :1;
DWORD fOutX :1;
DWORD fInX :1;
DWORD fErrorChar :1;
DWORD fNull :1;
DWORD fRtsControl :2;
DWORD fAbortOnError :1;
DWORD fDummy2 :17;
WORD wReserved;
WORD XonLim;
WORD XoffLim;
BYTE ByteSize;
BYTE Parity;
BYTE StopBits;
char XonChar;
char XoffChar;
char ErrorChar;
char EofChar;
char EvtChar;
WORD wReserved1;
}DCB, *LPDCB;


Note the ':' after some of the members. This indicates it is a bitfield - which is why you're getting peculiar results.

See here (http://msdn.microsoft.com/en-us/library/ewwyfdbe(VS.71).aspx) for more information about bitfields.

I'd normally give a solution for the pinvoke, but in this case since it'd take quite a while to explain how to properly marshal these try going here (http://www.pinvoker.com/Downloads.aspx).

There's a free download of a full pinvoke interface for kernel32.dll available which should be of help.

Darwen.

RaleTheBlade
August 31st, 2009, 05:32 PM
Here is the contents of the DCB structure when reading from a REAL RS-232 serial port:


--- DCB ---
DCBlength = 28
BaudRate = 9600
fBinary = 1
fParity = 134217728
fOutxCtsFlow = 459264
fOutxDsrFlow = 1249536
fDtrControl = 0
fDsrSensitivity = 0
fTXContinueOnXoff = 0
fOutX = 0
fInX = 0
fErrorChar = 0
fNull = 0
fRtsControl = 0
fAbortOnError = 0
fDummy2 = 0
wReserved = 0
XonLim = 0
XoffLim = 0
ByteSize = 0
Parity = 0
StopBits = 0
XonChar = 0
XoffChar = 0
ErrorChar = 0
EofChar = 0
EvtChar = 0
wReserved1 = 0
--- END ---


And here are the contents of the structure when reading from a serial port which is emulated with a CP-US-03 adapter:


--- DCB ---
DCBlength = 28
BaudRate = 9600
fBinary = 6161
fParity = 134217728
fOutxCtsFlow = 524800
fOutxDsrFlow = 2
fDtrControl = 0
fDsrSensitivity = 0
fTXContinueOnXoff = 0
fOutX = 0
fInX = 0
fErrorChar = 0
fNull = 0
fRtsControl = 0
fAbortOnError = 0
fDummy2 = 0
wReserved = 0
XonLim = 0
XoffLim = 0
ByteSize = 0
Parity = 0
StopBits = 0
XonChar = 0
XoffChar = 0
ErrorChar = 0
EofChar = 0
EvtChar = 0
wReserved1 = 0
--- END ---


I cant figure this out, Ive been working on it for days. If I build a small test project in C++ and call GetCommState, passing in a pointer to a DCB structure it works perfectly. Something is going bad when I try to invoke those same functions across the P/Invoke layer from C# to Win32.

RaleTheBlade
August 31st, 2009, 05:34 PM
DCB is defined as :


typedef struct _DCB {
DWORD DCBlength;
DWORD BaudRate;
DWORD fBinary :1;
DWORD fParity :1;
DWORD fOutxCtsFlow :1;
DWORD fOutxDsrFlow :1;
DWORD fDtrControl :2;
DWORD fDsrSensitivity :1;
DWORD fTXContinueOnXoff :1;
DWORD fOutX :1;
DWORD fInX :1;
DWORD fErrorChar :1;
DWORD fNull :1;
DWORD fRtsControl :2;
DWORD fAbortOnError :1;
DWORD fDummy2 :17;
WORD wReserved;
WORD XonLim;
WORD XoffLim;
BYTE ByteSize;
BYTE Parity;
BYTE StopBits;
char XonChar;
char XoffChar;
char ErrorChar;
char EofChar;
char EvtChar;
WORD wReserved1;
}DCB, *LPDCB;


Note the ':' after some of the members. This indicates it is a bitfield - which is why you're getting peculiar results.

See here (http://msdn.microsoft.com/en-us/library/ewwyfdbe(VS.71).aspx) for more information about bitfields.

I wouldn't normally do this, but since it'd take quite a while to explain how to properly marshal these try going here (http://www.pinvoker.com/Downloads.aspx).

There's a free download of a full pinvoke interface for kernel32.dll available which should be of help.

Darwen.

Thanks Darwen, I knew those items after the : may have had something to do with it but I wasnt fully sure of what they meant. I dont interop but once a blue moon! :wave:

RaleTheBlade
September 1st, 2009, 01:19 PM
Ok, after realizing that I was WAY overreaching my memory when my DCB structure was getting marshalled over the P/Invoke layer I got to thinking that I could just use a BitVector32 object and store all of the bits from fBinary through fDummy and it worked! Now whenever I need to change some of the individual bits I read in the DCB structure from a call to GetCommState, set the bits I need in the BitVector32 object in the DCB structure, and pass it back to SetCommState and it worked like a charm =D Heres the code I used to get this to work:

The DCB structure:


[StructLayout(LayoutKind.Explicit, Size = 28, CharSet = CharSet.Ansi)]
internal struct DCB
{
[FieldOffset(0)]
public DWORD DCBlength; // sizeof(DCB)
[FieldOffset(4)]
public DWORD BaudRate; // current baud rate
[FieldOffset(8)]
[MarshalAs(UnmanagedType.U4)]
public BitVector32 bitField; // bit fields
[FieldOffset(12)]
public WORD wReserved; // not currently used
[FieldOffset(14)]
public WORD XonLim; // transmit XON threshold
[FieldOffset(16)]
public WORD XoffLim; // transmit XOFF threshold
[FieldOffset(18)]
public BYTE ByteSize; // number of bits/byte, 4-8
[FieldOffset(19)]
public BYTE Parity; // 0-4=no,odd,even,mark,space
[FieldOffset(20)]
public BYTE StopBits; // 0,1,2 = 1, 1.5, 2
[FieldOffset(21)]
public CHAR XonChar; // Tx and Rx XON character
[FieldOffset(22)]
public CHAR XoffChar; // Tx and Rx XOFF character
[FieldOffset(23)]
public CHAR ErrorChar; // error replacement character
[FieldOffset(24)]
public CHAR EofChar; // end of input character
[FieldOffset(25)]
public CHAR EvtChar; // received event character
[FieldOffset(26)]
public WORD wReserved1; // reserved; do not use
}


And the code to set the bits:


// 4113 = 000000000000000000001000000010001 in binary
dcb.bitField = new BitVector32(4113);

// You can set them individually by using the decimal values of each
// binary digit too:

dcb.bitField[1] = true; // fBinary
dcb.bitField[2] = false; // fParity
dcb.bitField[4] = false; // fOutxCtsFlow
dcb.bitField[8] = false; // fOutxRtsFlow
dcb.bitField[16] = false; // fDtrControl
dcb.bitField[32] = false; // fDtrControl (always false)
dcb.bitField[64] = false; // fDsrSensitivity
dcb.bitField[128] = false; // fTxContinueOnXOff
dcb.bitField[256] = false; // fOutX
dcb.bitField[512] = false; // fInX
dcb.bitField[1024] = false; // fErrorChar
dcb.bitField[2048] = false; // fNull
dcb.bitField[4096] = false; // fRtsControl
dcb.bitField[8192] = false; // fRtsControl (always false)
dcb.bitField[16384] = false;// fAbortOnError

// This creates a bit field of 32 bits and represents the number 4113 in decimal.


Thanks for the help Darwin, without knowing that the C implementation made the use of bit fields I would probably have been stuck on this STILL :D