2
我在通過C#中的Win32 API執行串行通信時遇到問題。無論在調用SetCommTimeouts()時使用哪些值,除非接收到一個或多個字符,否則對ReadFile的調用將不會返回。如何從C#中調用Win32API SetCommTimeouts?
使用.Net System.IO.Port.SerialPort類不是一個選項。它對於USB連接的COM端口有嚴重的錯誤,這就是我試圖直接使用Win32 API的原因。
問題出現在編組CommTimeouts結構中,以便API接收不正確的值嗎?
完整的源代碼提供如下:
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();
}
}
}
}
你有沒有打過電話['GetCommTimeouts'(http://msdn.microsoft.com/en-us/library/windows/desktop/aa363261%28v=vs.85%29.aspx)後設定來確認你的理論?同樣,當你的定義與P/Invoke的([here](http://pinvoke.net/default.aspx/Structures/COMMTIMEOUTS.html)和[here](http://www.pinvoke。 net/default.aspx/kernel32/SetCommTimeouts.html))我不認爲這會有所作爲。 – Richard 2014-12-05 13:53:38
這是SetCommTimeouts定義不正確。當設置爲[In] ref CommTimeouts時,按預期工作。 我從我找到的例子中複製了定義,在使用前沒有仔細檢查。謝謝。 – 2014-12-05 14:16:44
非常重要的是,您首先識別錯誤,以避免重新創建錯誤。如果你做完作業,你會發現SerialPort本身就是一個使用pinvoke的薄包裝。因此,只需使用[ReferenceSource](http://referencesource.microsoft.com/#System/sys/system/io/ports/SerialStream.cs,412)即可開始使用。複製那裏的東西,減去錯誤:) – 2014-12-05 14:26:20