【问题标题】:C# check if a COM (Serial) port is already openC# 检查 COM(串行)端口是否已打开
【发布时间】:2022-03-12 01:12:09
【问题描述】:

是否有一种简单的方法可以以编程方式检查串行 COM 端口是否已打开/正在使用?

通常我会使用:

try
{
    // open port
}
catch (Exception ex)
{
    // handle the exception
}

但是,我想以编程方式进行检查,以便尝试使用另一个 COM 端口或类似的端口。

【问题讨论】:

    标签: c# .net serial-port


    【解决方案1】:

    前段时间我需要类似的东西来搜索设备。

    我获得了可用 COM 端口的列表,然后简单地遍历它们,如果它没有引发异常,我尝试与设备通信。有点粗糙但工作。

    var portNames = SerialPort.GetPortNames();
    
    foreach(var port in portNames) {
        //Try for every portName and break on the first working
    }
    

    【讨论】:

    • 我真的希望有一个更“优雅”的解决方案......但是当所有其他方法都失败时,“使用任何可能的方式使程序运行”!
    • 只是为了让一些不知道 SerialPort 行为的人清楚,上面的答案不使用 port.Open 只检查 CALLER 是否打开了端口,而不是说另一个应用程序有它 -为此,必须尝试通过端口进行通信并抛出异常 - 端口正在使用,访问被拒绝。
    • 我不喜欢这种“蛮力”的方法。我认为这将清空所有 COM 端口上的硬件接收缓冲区。
    【解决方案2】:

    我就是这样做的:

          [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr securityAttrs, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
    

    稍后

            int dwFlagsAndAttributes = 0x40000000;
    
            var portName = "COM5";
    
            var isValid = SerialPort.GetPortNames().Any(x => string.Compare(x, portName, true) == 0);
            if (!isValid)
                throw new System.IO.IOException(string.Format("{0} port was not found", portName));
    
            //Borrowed from Microsoft's Serial Port Open Method :)
            SafeFileHandle hFile = CreateFile(@"\\.\" + portName, -1073741824, 0, IntPtr.Zero, 3, dwFlagsAndAttributes, IntPtr.Zero);
            if (hFile.IsInvalid)
                throw new System.IO.IOException(string.Format("{0} port is already open", portName));
    
            hFile.Close();
    
    
            using (var serialPort = new SerialPort(portName, 115200, Parity.None, 8, StopBits.One))
            {
                serialPort.Open();
            }
    

    【讨论】:

    • 使用它比捕获尝试使用 SerialPort.Open 时抛出的异常有什么好处?
    • 因为你不希望异常来控制应用程序的流程。尝试提前处理问题而不是抛出异常而不是处理它更干净。
    • @Jeff:但是,如果有人在您检查和打开它之间的瞬间打开串行端口,您的程序就会爆炸。通过使用异常,您可以避免潜在的灾难。
    • 我非常同意@jcmcbeth:这种方法是一种竞争条件。
    • 如果 hFile.IsInvalid 属性为 true 而不是抛出异常,是否有替代方案?想继续打开串口怎么办?
    【解决方案3】:

    对于那些不能使用 SerialPort.GetPortNames(); 的人,因为他们没有针对 .net framework(比如在我的情况下,我使用的是 .Net Core 而不是 .Net Framework),这就是我最终要做的事情:

    如果你在命令提示符下输入 mode 你会得到类似这样的信息:

    mode 是位于C:\Windows\System32\mode.com 的可执行文件。只需使用如下正则表达式解析该可执行文件的结果:

    // Code that answers the question
    
    var proc = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            FileName = @"C:\Windows\System32\mode.com",
            UseShellExecute = false,
            RedirectStandardOutput = true,
            CreateNoWindow = true
        }
    };
    
    proc.Start();
    proc.WaitForExit(4000); // wait up to 4 seconds. It usually takes less than a second
    
    // get ports being used
    var output = proc.StandardOutput.ReadToEnd();
    

    现在,如果您想解析输出,我就是这样做的:

    List<string> comPortsBeingUsed = new List<string>();
    Regex.Replace(output, @"(?xi) status [\s\w]+? (COM\d) \b ", regexCapture =>
    {
        comPortsBeingUsed.Add(regexCapture.Groups[1].Value);
        return null;
    });
    
    foreach(var item in comPortsBeingUsed)
    {
        Console.WriteLine($"COM port {item} is in use");
    }
    

    【讨论】:

    • 好主意。直到今天我才知道mode.com。但是注意多语言使用,因为mode.com 是本地化的。因此,当“状态”的翻译不同时,RegEx 对于 Windows 安装可能会失败。 (在德语中是同一个词。)
    • 在我的命令行测试中,我得到了一个反转的结果。未使用的 COM 端口由mode.com 列出,使用的 COM 端口不可见。
    【解决方案4】:

    我想打开下一个可用端口并这样做。 请注意,它不是针对 WPF 而是针对 Windows 窗体的。 我用可用的 com 端口填充了一个组合框。 然后我尝试打开第一个。如果失败,我从组合框中选择下一个可用项目。如果选择的索引最终没有改变,则没有可用的备用 com 端口,我们会显示一条消息。

    private void GetPortNames()
    {
        comboBoxComPort.Items.Clear();
        foreach (string s in SerialPort.GetPortNames())
        {
            comboBoxComPort.Items.Add(s);
        }
        comboBoxComPort.SelectedIndex = 0;
    }
    
    private void OpenSerialPort()
    {
        try
        {
            serialPort1.PortName = comboBoxComPort.SelectedItem.ToString();
            serialPort1.Open();
        }
        catch (Exception ex)
        {
            int SelectedIndex = comboBoxComPort.SelectedIndex;
            if (comboBoxComPort.SelectedIndex >= comboBoxComPort.Items.Count - 1)
            {
                comboBoxComPort.SelectedIndex = 0;
            }
            else
            {
                comboBoxComPort.SelectedIndex++;
            }
            if (comboBoxComPort.SelectedIndex == SelectedIndex)
            {
                buttonOpenClose.Text = "Open Port";
                MessageBox.Show("Error accessing port." + Environment.NewLine + ex.Message, "Port Error!!!", MessageBoxButtons.OK);
            }
            else
            {
                OpenSerialPort();
            }
        }
    
        if (serialPort1.IsOpen)
        {
            StartAsyncSerialReading();
        }
    }
    

    【讨论】:

      【解决方案5】:

      SerialPort class 有一个Open 方法,它会抛出一些异常。 上面的参考资料包含详细的例子。

      另请参阅IsOpen 属性。

      一个简单的测试:

      using System;
      using System.IO.Ports;
      using System.Collections.Generic;
      using System.Text;
      
      namespace SerPort1
      {
      class Program
      {
          static private SerialPort MyPort;
          static void Main(string[] args)
          {
              MyPort = new SerialPort("COM1");
              OpenMyPort();
              Console.WriteLine("BaudRate {0}", MyPort.BaudRate);
              OpenMyPort();
              MyPort.Close();
              Console.ReadLine();
          }
      
          private static void OpenMyPort()
          {
              try
              {
                  MyPort.Open();
              }
              catch (Exception ex)
              {
                  Console.WriteLine("Error opening my port: {0}", ex.Message);
              }
          }
        }
      }
      

      【讨论】:

      • IsOpen 只有在调用过 Open() 方法时才会生效。它不会说端口是否已被另一个应用程序打开。
      • com1 不存在怎么跳过?
      • 本示例仅使用 COM1 作为说明。不存在的设备将导致 SerialPort 构造函数引发 IOException:“无法找到或打开指定的端口”。
      • 如果您这样做 - 所有端口上硬件缓冲区中的所有现有数据都将被移动到软件缓冲区(默认为 4096 字节),然后在您关闭端口时销毁。
      【解决方案6】:

      分享对我有用的方法(一个简单的辅助方法):

      private string portName { get; set; } = string.Empty;
      
          /// <summary>
          /// Returns SerialPort Port State (Open / Closed)
          /// </summary>
          /// <returns></returns>
          internal bool HasOpenPort()
          {
              bool portState = false;
      
              if (portName != string.Empty)
              {
                  using (SerialPort serialPort = new SerialPort(portName))
                  {
                      foreach (var itm in SerialPort.GetPortNames())
                      {
                          if (itm.Contains(serialPort.PortName))
                          {
                              if (serialPort.IsOpen) { portState = true; }
                              else { portState = false; }
                          }
                      }
                  }
              }
      
              else { System.Windows.Forms.MessageBox.Show("Error: No Port Specified."); }
      
              return portState;
      }
      


      注意事项:
      - 对于更高级的技术,我建议使用ManagementObjectSearcher Class
      更多信息Here
      - 对于 Arduino 设备,我会让端口保持打开状态。
      - 如果需要捕获异常,建议使用 Try Catch 块。
      - 还要检查:“TimeoutException”
      - 有关如何获取 SerialPort (Open) Exceptions Here 的更多信息。

      【讨论】:

        【解决方案7】:
          public void MobileMessages(string ComNo, string MobileMessage, string MobileNo)
            {
                if (SerialPort.IsOpen )
                    SerialPort.Close();
                try
                {
                    SerialPort.PortName = ComNo;
                    SerialPort.BaudRate = 9600;
                    SerialPort.Parity = Parity.None;
                    SerialPort.StopBits = StopBits.One;
                    SerialPort.DataBits = 8;
                    SerialPort.Handshake = Handshake.RequestToSend;
                    SerialPort.DtrEnable = true;
                    SerialPort.RtsEnable = true;
                    SerialPort.NewLine = Constants.vbCrLf;
                    string message;
                    message = MobileMessage;
        
                    SerialPort.Open();
                    if (SerialPort.IsOpen  )
                    {
                        SerialPort.Write("AT" + Constants.vbCrLf);
                        SerialPort.Write("AT+CMGF=1" + Constants.vbCrLf);
                        SerialPort.Write("AT+CMGS=" + Strings.Chr(34) + MobileNo + Strings.Chr(34) + Constants.vbCrLf);
                        SerialPort.Write(message + Strings.Chr(26));
                    }
                    else
                        ("Port not available");
                    SerialPort.Close();
                    System.Threading.Thread.Sleep(5000);
                }
                catch (Exception ex)
                {
        
                        message.show("The port " + ComNo + " does not exist, change port no ");
                }
            }
        

        【讨论】:

          【解决方案8】:

          我已经与这个问题作斗争了几个星期了。感谢这里和网站上的建议,https://www.dreamincode.net/forums/topic/91090-c%23-serial-port-unauthorizedaccessexception/

          我终于想出了一个似乎可行的解决方案。

          我正在开发的应用程序允许用户连接到 USB 设备并显示其中的数据。

          我正在解决的问题。 除了我正在编写的应用程序之外,我还使用另一个串行终端应用程序进行测试。有时我忘记断开其他应用程序上使用的 COMport。如果我这样做,并尝试连接我正在编写的应用程序,我会收到“UnAuthorizedAccessException”错误。除了这个例外,还有一些副作用,例如双行数据被吐出以及应用程序在关闭时锁定。

          我的解决方案

          感谢此处的建议和引用的其他网站,这是我的解决方案。

                  private void checkAndFillPortNameList()
                  {
                     SerialPort _testingSerialPort;
          
          
                      AvailablePortNamesFound.Clear();
                      List<string> availablePortNames = new List<string>();//mySerial.GetAvailablePortNames();
          
                      foreach (string portName in SerialPortDataAccess.GetAvailablePortNames())
                      {
                          try
                          {
                              _testingSerialPort = new SerialPort(portName);
                              _testingSerialPort.Open();
          
                              if (_testingSerialPort.IsOpen)
                              {
                                  availablePortNames.Add(portName);
                                  _testingSerialPort.Close();
                              }
                          }
                          catch (Exception ex)
                          {
           
                          }
                      }
                      availablePortNames.Sort();
                      AvailablePortNamesFound = new ObservableCollection<string>(availablePortNames);
                  }
          
          

          此例程连接到一个组合框,该组合框包含可供选择的 Comport。如果 Comport 已被另一个应用程序使用,则该端口名称将不会出现在组合框中。

          【讨论】:

            【解决方案9】:

            您可以尝试以下代码来检查端口是否已打开。我假设您不知道具体要检查哪个端口。

            foreach (var portName in Serial.GetPortNames()
            {
              SerialPort port = new SerialPort(portName);
              if (port.IsOpen){
                /** do something **/
              }
              else {
                /** do something **/
              }
            }
            

            【讨论】:

            • 如果人们能说出他们投反对票的原因会很好,这样其他人就可以知道为什么这是错误的。我假设这就是原因:只有在调用过 Open() 方法时 IsOpen 才会正确。它不会说端口是否已被另一个应用程序打开。 – Mark Cidade 2008 年 10 月 12 日 14:28
            • 如果 comport 根本不存在,比如端口 20,您仍然可以检查它是否打开返回 false,这可能会误导开发人员尝试打开端口...最好检查是否端口在假设它是打开还是关闭之前就已经存在。
            • 之所以被否决,是因为此解决方案仅适用于在自己的应用程序中打开的端口,而不适用于系统范围。因此它并没有真正的帮助。很遗憾。我也在寻找一种很好的检查方法,但似乎没有超过 try..catch 块。
            • 正如@AllDayPiano 所说,请查看备注部分:docs.microsoft.com/en-us/dotnet/api/…
            猜你喜欢
            • 1970-01-01
            • 2012-08-03
            • 2013-03-29
            • 1970-01-01
            • 2012-03-04
            • 2010-09-26
            • 2012-06-09
            相关资源
            最近更新 更多