Alright I solved my problem, I'll post the code and explanation here if anyone is interested.
The DCB struct requires a C++ class known as a bitfield, which C# does not have, so instead they have a field called "Flags" stored as a
UInt32
. They have their own melarchy set up there as to how it's stored and read from, but they did put a method in the
SerialStream
called
SetDcbFlag
which accepts two
int
s. After digging through the .net source a bit, I managed to come up with a set of constants equating to the original DCB Fields and their new int code, I haven't yet made a list of values for these flags, but those can be easily found in the
DCB Documentation. I used system.reflection to gain access to the method as it was an internal method only to be used by internal .NET source.
So here it is, the code, it's an extension class for the
SerialPort
class which is shipped stock with .NET 2.0+. My extension class adds three methods,
SetField(string name, object value)
which will set any of the fields that aren't prefixed with "f",
SetFlag(int Flag, int Value)
which will take care of those fields prefixed with "f" (I'll provide a list of constants for use with the
Flag
parameter), and finally
UpdateComm()
this will update the serial connection once you have changed all of your values, it was originally part of the
SetField
method, but it takes slightly longer to complete things if it's calling that every single time during initialization.
NOTE:
The serial port must be opened before using any of these methods!
Usage:
SerialPort COM = new SerialPort("COM7");
COM.Open();
COM.DiscardInBuffer();
COM.DiscardOutBuffer();
COM.SetFlag(FBINARY, 1);
COM.SetFlag(FPARITY, 0);
COM.SetFlag(FDTRCONTROL, 0x00);
COM.SetFlag(FRTSCONTROL, 0x01);
COM.SetField("BaudRate", (UInt32)115200);
COM.SetField("StopBits", (byte)0);
COM.SetField("ByteSize", (byte)8);
COM.SetField("Parity", (byte)0);
COM.SetField("XonChar", (byte)0x11);
COM.SetField("XoffChar", (byte)0x13);
COM.SetField("EvtChar", (byte)0x1A);
COM.SetField("XonLim", (ushort)256);
COM.SetField("XoffLim", (ushort)256);
COM.UpdateComm();
COM.Close();
Constants:
internal const int FBINARY = 0;
internal const int FPARITY = 1;
internal const int FOUTXCTSFLOW = 2;
internal const int FOUTXDSRFLOW = 3;
internal const int FDTRCONTROL = 4;
internal const int FDSRSENSITIVITY = 6;
internal const int FTXCONTINUEONXOFF = 7;
internal const int FOUTX = 8;
internal const int FINX = 9;
internal const int FERRORCHAR = 10;
internal const int FNULL = 11;
internal const int FRTSCONTROL = 12;
internal const int FABORTONOERROR = 14;
internal const int FDUMMY2 = 15;
And finally, the extension class:
internal static class SerialPortExtensions
{
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void SetField(this SerialPort port, string field, object value)
{
if (port == null)
throw new NullReferenceException();
if (port.BaseStream == null)
throw new InvalidOperationException("Cannot change fields until after the port has been opened.");
try
{
object baseStream = port.BaseStream;
Type baseStreamType = baseStream.GetType();
FieldInfo dcbFieldInfo = baseStreamType.GetField("dcb", BindingFlags.NonPublic | BindingFlags.Instance);
object dcbValue = dcbFieldInfo.GetValue(baseStream);
Type dcbType = dcbValue.GetType();
dcbType.GetField(field).SetValue(dcbValue, value);
dcbFieldInfo.SetValue(baseStream, dcbValue);
}
catch (SecurityException) { throw; }
catch (OutOfMemoryException) { throw; }
catch (Win32Exception) { throw; }
catch (Exception)
{
throw;
}
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void SetFlag(this SerialPort port, int flag, int value)
{
object BaseStream = port.BaseStream;
Type SerialStream = BaseStream.GetType();
SerialStream.GetMethod("SetDcbFlag", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(BaseStream, new object[] { flag, value });
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void UpdateComm(this SerialPort port)
{
object baseStream = port.BaseStream;
Type baseStreamType = baseStream.GetType();
FieldInfo dcbFieldInfo = baseStreamType.GetField("dcb", BindingFlags.NonPublic | BindingFlags.Instance);
object dcbValue = dcbFieldInfo.GetValue(baseStream);
SafeFileHandle portFileHandle = (SafeFileHandle)baseStreamType.GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(baseStream);
IntPtr hGlobal = Marshal.AllocHGlobal(Marshal.SizeOf(dcbValue));
try
{
Marshal.StructureToPtr(dcbValue, hGlobal, false);
if (!SetCommState(portFileHandle, hGlobal))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
if (hGlobal != IntPtr.Zero)
Marshal.FreeHGlobal(hGlobal);
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetCommState(SafeFileHandle hFile, IntPtr lpDCB);
}