Как использовать win32 readfile в C# с помощью dllimport для чтения из последовательного порта
I have written C++ code that is able to read from and write to a com port using the Win32 API method ReadFile. The code is for the purpose of sending HPGL instructions to a legacy graphics plotter and retrieving status data from it. I have ported this code to C# and added DllImport definitions for all the Win32 API methods that the code requires and everything is working fine except for ReadFile which never retrieves any data from the port and never returns an error or throws an exception. I have created a pair of small apps that make all the necessary Win32 API calls to communicate with the plotter, one in C++ which works correctly and the other in C# which fails consistently. This is the C++ app, followed by the C# app. I've been looking at this code for a few days trying various changes to the ReadFile call (Unicode vs. Ansi, out vs. ref, etc.) and nothing has changed. I've also done extensive searching online but I haven't been able to find anything relevant. I'd appreciate any suggestions anyone might have.
Что я уже пробовал:
int _tmain(int argc, _TCHAR* argv[]) { HANDLE hSerial = CreateFile (TEXT("\\\\.\\COM3"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); DCB dcbSerialParams; COMMTIMEOUTS timeouts; ZeroMemory (&dcbSerialParams, sizeof (DCB)); ZeroMemory (&timeouts, sizeof (COMMTIMEOUTS)); dcbSerialParams.DCBlength = sizeof (dcbSerialParams); if (GetCommState(hSerial, &dcbSerialParams) == 0) { CloseHandle (hSerial); return 1; } dcbSerialParams.BaudRate = 9600; dcbSerialParams.ByteSize = 8; dcbSerialParams.StopBits = ONESTOPBIT; dcbSerialParams.Parity = NOPARITY; if (SetCommState (hSerial, &dcbSerialParams) == 0) { CloseHandle (hSerial); return 2; } // Set COM port timeout settings timeouts.ReadIntervalTimeout = 50; timeouts.ReadTotalTimeoutConstant = 50; timeouts.ReadTotalTimeoutMultiplier = 10; timeouts.WriteTotalTimeoutConstant = 50; timeouts.WriteTotalTimeoutMultiplier = 10; if (SetCommTimeouts (hSerial, &timeouts) == 0) { CloseHandle (hSerial); return 3; } DWORD dwBytesWritten = 0; int iResult = WriteFile (hSerial, (LPVOID)"OI;", 3, &dwBytesWritten, NULL); FlushFileBuffers (hSerial); char szBuffer[10]; ::ZeroMemory (szBuffer, 10); DWORD dwBytesRead = 0, dwErrorFlags = 0; COMSTAT comstat; ::ZeroMemory ((void*)&comstat, sizeof (COMSTAT)); ClearCommError (hSerial, &dwErrorFlags, &comstat); //int iBytesRead = ReadData ((void*) szBuffer, BUFFER_SIZE - 1); int iRepeat = 100; // For a maximum of 1 second, a long time for some input to show up int iLastInQue = 0; while (iRepeat--) { iLastInQue = comstat.cbInQue; ClearCommError (hSerial, &dwErrorFlags, &comstat); if (comstat.cbInQue > 0 && comstat.cbInQue == iLastInQue) { break; } Sleep (10); } dwBytesRead = (DWORD) comstat.cbInQue; ReadFile (hSerial, (void*)szBuffer, dwBytesRead, &dwBytesRead, NULL); puts (szBuffer); CloseHandle (hSerial); return 0; }
class Program { #region DllImport statements [DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] private static extern UIntPtr CreateFileW (string lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, UIntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, UIntPtr hTemplateFile); [StructLayout (LayoutKind.Sequential)] private struct COMSTAT { public uint uiCtsHold; public uint uiDsrHold; public uint uiRlsdHold; public uint uiXoffHold; public uint uiXoffSent; public uint uiEof; public uint uiTxim; public UInt32 uiFlags; public UInt32 cbInQue; public UInt32 cbOutQue; } [DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] private static extern int ClearCommError (UIntPtr hFile, out UInt32 lpErrors, out COMSTAT lpStat); [DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] private static extern int CloseHandle (UIntPtr hObject); [DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] private static extern int FlushFileBuffers (UIntPtr hFile); [StructLayout (LayoutKind.Sequential)] public struct DCB { public UInt32 DCBlength; // sizeof(DCB) public UInt32 BaudRate; // Baudrate at which running public UInt32 uiFlagBits; // Defined separately public UInt16 wReserved; // Not currently used public UInt16 XonLim; // Transmit X-ON threshold public UInt16 XoffLim; // Transmit X-OFF threshold public byte ByteSize; // Number of bits/byte, 4-8 public byte Parity; // 0-4=None,Odd,Even,Mark,Space public byte StopBits; // 0,1,2 = 1, 1.5, 2 public char XonChar; // Tx and Rx X-ON character public char XoffChar; // Tx and Rx X-OFF character public char ErrorChar; // Error replacement char public char EofChar; // End of Input character public char EvtChar; // Received Event character public UInt16 wReserved1; // Fill for now. } [DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] private static extern int GetCommState (UIntPtr hFile, out DCB lpDCB); [DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] private static extern int ReadFile (UIntPtr hFile, out string lpBuffer, UInt32 nNumberOfBytesToRead, out UInt32 lpNumberOfBytesRead, UIntPtr lpOverlapped); [DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] private static extern int SetCommState (UIntPtr hFile, ref DCB lpDCB); private class COMMTIMEOUTS { public UInt32 ReadIntervalTimeout = 0; public UInt32 ReadTotalTimeoutMultiplier = 0; public UInt32 ReadTotalTimeoutConstant = 0; public UInt32 WriteTotalTimeoutMultiplier = 0; public UInt32 WriteTotalTimeoutConstant = 0; } [DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] private static extern int SetCommTimeouts (UIntPtr hFile, ref COMMTIMEOUTS lpCommTimeouts); [DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] private static extern int WriteFile (UIntPtr hFile, string lpBuffer, UInt32 nNumberOfBytesToWrite, out UInt32 lpNumberOfBytesWritten, UIntPtr lpOverlapped); #endregion // This code fails to retrieve the plotter's ID string static void Main (string[] args) { const uint OPEN_EXISTING = 3; const uint GENERIC_READ = 0x80000000; const uint GENERIC_WRITE = 0x40000000; const uint FILE_ATTRIBUTE_NORMAL = 0x00000080; const int NOPARITY = 0; const int ONESTOPBIT = 0; UIntPtr hSerial = CreateFileW ("COM3", GENERIC_READ | GENERIC_WRITE, 0, (UIntPtr)null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (UIntPtr)null); DCB dcbSerialParams = new DCB (); COMMTIMEOUTS timeouts = new COMMTIMEOUTS (); dcbSerialParams.DCBlength = (uint)Marshal.SizeOf (dcbSerialParams); if (GetCommState (hSerial, out dcbSerialParams) == 0) { CloseHandle (hSerial); return; } dcbSerialParams.BaudRate = 9600; dcbSerialParams.ByteSize = 8; dcbSerialParams.StopBits = ONESTOPBIT; dcbSerialParams.Parity = NOPARITY; if (SetCommState (hSerial, ref dcbSerialParams) == 0) { CloseHandle (hSerial); return; } // Set COM port timeout settings timeouts.ReadIntervalTimeout = 50; timeouts.ReadTotalTimeoutConstant = 50; timeouts.ReadTotalTimeoutMultiplier = 10; timeouts.WriteTotalTimeoutConstant = 50; timeouts.WriteTotalTimeoutMultiplier = 10; if (SetCommTimeouts (hSerial, ref timeouts) == 0) { CloseHandle (hSerial); return; } UInt32 dwBytesWritten = 0; int iResult = WriteFile (hSerial, "OI;", 3, out dwBytesWritten, (UIntPtr)null); FlushFileBuffers (hSerial); string strBuffer; UInt32 dwBytesRead = 0, dwErrorFlags = 0; COMSTAT comstat; ClearCommError (hSerial, out dwErrorFlags, out comstat); //int iBytesRead = ReadData ((void*) szBuffer, BUFFER_SIZE - 1); int iRepeat = 100; // For a maximum of 1 second, a long time for some input to show up int iLastInQue = 0; while (iRepeat-- > 0) { iLastInQue = (int)comstat.cbInQue; ClearCommError (hSerial, out dwErrorFlags, out comstat); if (comstat.cbInQue > 0 && comstat.cbInQue == iLastInQue) { break; } Thread.Sleep (10); } dwBytesRead = (UInt32)comstat.cbInQue; // ReadFile consistently returns no data and no indication of failure ReadFile (hSerial, out strBuffer, dwBytesRead, out dwBytesRead, (UIntPtr)null); Console.WriteLine (strBuffer); CloseHandle (hSerial); } }
11917640 Member
Запустите программу portmon https://docs.microsoft.com/en-us/sysinternals/downloads/portmon и сравнить поведение рабочих и нерабочих программ.