2014-12-05 140 views
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(); 
      } 
     } 
    } 
} 
+1

你有沒有打過電話['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

+0

這是SetCommTimeouts定義不正確。當設置爲[In] ref CommTimeouts時,按預期工作。 我從我找到的例子中複製了定義,在使用前沒有仔細檢查。謝謝。 – 2014-12-05 14:16:44

+0

非常重要的是,您首先識別錯誤,以避免重新創建錯誤。如果你做完作業,你會發現SerialPort本身就是一個使用pinvoke的薄包裝。因此,只需使用[ReferenceSource](http://referencesource.microsoft.com/#System/sys/system/io/ports/SerialStream.cs,412)即可開始使用。複製那裏的東西,減去錯誤:) – 2014-12-05 14:26:20

回答

2

的SetCommTimeouts定義,我在網上找到是不正確的。感謝理查德,我現在使用正確的定義。

static extern bool SetCommTimeouts(IntPtr hFile, [In] ref COMMTIMEOUTS 
lpCommTimeouts);