I am having problems performing serial communications via the Win32 API from C#. No matter which values I use when calling SetCommTimeouts(), the call to ReadFile will not return unless one or more characters are received.
Using the .Net System.IO.Port.SerialPort class is not an option. It has serious bugs regarding USB-connected COM-ports which is the reason why I am trying to use the Win32 API directly instead.
Could the problem be with marshalling the CommTimeouts structure so that the API receives incorrect values?
Complete source code provided below:
namespace SerialTest
{
using System;
using System.Globalization;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
[Flags]
internal enum AccessRights : uint
{
GenericRead = (0x80000000),
GenericWrite = (0x40000000),
GenericExecute = (0x20000000),
GenericAll = (0x10000000)
}
[Flags]
internal enum ShareModes : uint
{
FileShareRead = 0x00000001,
FileShareWrite = 0x00000002,
FileShareDelete = 0x00000004
}
internal enum CreationDispositions
{
CreateNew = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5
}
internal class CommTimeouts
{
public UInt32 ReadIntervalTimeout;
public UInt32 ReadTotalTimeoutMultiplier;
public UInt32 ReadTotalTimeoutConstant;
public UInt32 WriteTotalTimeoutMultiplier;
public UInt32 WriteTotalTimeoutConstant;
}
internal class Kernel32
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll", EntryPoint = "SetCommTimeouts", SetLastError = true)]
public static extern bool SetCommTimeouts(SafeHandle hFile, CommTimeouts timeouts);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadFile(SafeHandle hFile, [Out] byte[] lpBuffer,
uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
}
public class SerialTest
{
public void Test(string portName)
{
if (portName.Length > 5) portName = @"\\.\" + portName;
var hPort = Kernel32.CreateFile(portName,
(uint) (AccessRights.GenericRead | AccessRights.GenericWrite),
0, // Not shared
IntPtr.Zero, // Security attributes,
(uint) CreationDispositions.OpenExisting,
0,
IntPtr.Zero // Template file
);
if (hPort.IsInvalid)
{
throw new Exception("Could not open port " + portName + ". Error: " + Marshal.GetLastWin32Error().ToString(CultureInfo.InvariantCulture));
}
try
{
// Set timeout so call returns immediately
var timeouts = new CommTimeouts
{
ReadIntervalTimeout = 0xFFFFFFFF,
ReadTotalTimeoutMultiplier = 0,
ReadTotalTimeoutConstant = 0,
WriteTotalTimeoutMultiplier = 0,
WriteTotalTimeoutConstant = 0
};
if (!Kernel32.SetCommTimeouts(hPort, timeouts))
{
var error = Marshal.GetLastWin32Error();
throw new Exception("Could not set timeouts. Error: " + error.ToString(CultureInfo.InvariantCulture));
}
var buf = new byte[1];
uint readBytes;
if (!Kernel32.ReadFile(hPort,
buf,
1,
out readBytes,
IntPtr.Zero))
{
var error = Marshal.GetLastWin32Error();
throw new Exception("Could not read. Error: " + error.ToString(CultureInfo.InvariantCulture));
}
}
finally
{
hPort.Close();
}
}
}
}
The SetCommTimeouts definition which I found online was incorrect. Thanks to Richard, I now use the correct definition.
static extern bool SetCommTimeouts(IntPtr hFile, [In] ref COMMTIMEOUTS
lpCommTimeouts);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With