为什么访问COM端口被拒绝?

17

代码:

static void Main(string[] args)
{
    Console.WriteLine("Memory mapped file reader started");

    using (var file = MemoryMappedFile.OpenExisting("AIDA64_SensorValues"))
    {
        using (var readerz = file.CreateViewAccessor(0, 0))
        {
            var bytes = new byte[567];
            var encoding = Encoding.ASCII;
            readerz.ReadArray<byte>(0, bytes, 0, bytes.Length);

            File.WriteAllText("C:\\myFile.txt", encoding.GetString(bytes));

            var readerSettings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment };
            using (var reader = XmlReader.Create("C:\\myFile.txt", readerSettings))
            {
                while (reader.Read())
                {
                    using (var fragmentReader = reader.ReadSubtree())
                    {
                        if (fragmentReader.Read())
                        {

                            reader.ReadToFollowing("value");
                            SerialPort port = new SerialPort("COM2", 9600, Parity.None, 8, StopBits.One);
                            port.Open();
                            port.Write(reader.ReadElementContentAsString() + ",");
                        }
                    }
                }
            }    
        }
    }

    Console.WriteLine("Press any key to exit ...");
    Console.ReadLine();
}

该程序从共享内存读取数据,将共享内存中的数据写入文件。然后使用 XML Reader 打开该文件并拆分 XML,因为它具有多个根节点。接着在每个新的拆分 XML 中获取一个节点的值,并通过串口发送。在第一个拆分的 XML 上运行正常且其节点已经被发送到串口,但是当尝试将第二个节点写入串口时,会收到访问 COM 端口被拒绝的消息。

我创建了另一个与串口代码相同的应用程序,它可以正常工作(我只是尝试了一下然后关闭了它)。所以这很奇怪。


也许您没有正确关闭一些引用,并且打开的访问器意味着拒绝来自同一应用程序/线程的更多访问? - Marek Sebera
5个回答

32

您只能打开一次串口。但是您的代码在while循环中具有Open()调用。这只适用于第一次通过循环,但在第二次通过时会崩溃。@cdhowie的解决方案也不起作用,因为SerialPort存在一个怪癖(又称为bug),文档对此进行了警告。在Dispose()或Close()调用后,它需要时间让工作线程退出。这段时间是未指定和不可预测的。

真正的解决方案很简单,只需将Open()调用移至while循环之前即可。


4
除了Hans的回答之外: 我也遇到了同样的问题,对于打开和关闭串口之间的睡眠时间进行了一些尝试。在我的情况下,250毫秒就足够了。 也许这能帮助到某些人。
编辑: 我优化了我的解决方案,这是我得出的结论:
int maxRetries = 20;
const int sleepTimeInMs = 50;
string loggingMessage = string.Empty;

while (maxRetries > 0)
{
    try
    {
        loggingMessage = "Opening serial port '" + mSerialPort.PortName + "'...";
        mSerialPort.Open();
        loggingMessage += "Succeeded.";
        IOLogger.LogInfo(loggingMessage);
    }
    catch (UnauthorizedAccessException unauthorizedAccessException)
    {
        maxRetries--;
        loggingMessage += "Failed (UnauthorizedAccessException): ";
        IOLogger.LogError(string.Format(loggingMessage + unauthorizedAccessException.Message + " -> Retrying in about {0} milliseconds...", sleepTimeInMs));
        Thread.Sleep(sleepTimeInMs);
    }
    catch (Exception exception)
    {
        loggingMessage += "Failed: ";
        IOLogger.LogError(loggingMessage + exception.Message);
    }
}

您可以尝试调整sleepTimeInMs和/或maxRetries的值。我选择这些值是因为它们似乎足以满足任何需要的用例。


2

如果端口已打开,则需要关闭和释放它,因为在上次连接时它未被释放。


0
Hans的答案比这个更好,我将保留它以提供背景和信息。

当你完成端口操作后,需要关闭它。垃圾回收器在你尝试打开另一个句柄之前不会收集第一个SerialPort对象。请更改以下代码:

SerialPort port = new SerialPort("COM2", 9600, Parity.None, 8, StopBits.One);
port.Open();
port.Write(reader.ReadElementContentAsString() + ",");

目标:

using (SerialPort port = new SerialPort("COM2", 9600, Parity.None, 8, StopBits.One))) 
{
    port.Open();
    port.Write(reader.ReadElementContentAsString() + ",");
}

嗯,这很有趣。我留下我的答案供信息参考。 - cdhowie
在清理过程中,使用的时候是否调用了port.close();函数? - undefined

0
我的解决方案如下: 1:使用try catch块,并将所有打开和关闭端口的代码放入其中。
2:使用IsOpen()函数检查端口是否已经打开。
3:如果出现任何异常(访问被拒绝),请在catch块中编写Port.Close()代码以释放该端口。
4:在try catch块之前创建Serial port对象,使其成为通用对象。
5:Open()调用不应该在循环内部。您只应该打开端口一次,并在循环后关闭它。
如果您遵循所有这些步骤,您将永远不会再遇到此问题。
以下是示例代码:
GsmCommMain comm = new GsmCommMain(COMPort.ToString(), 9600, 500);

try{ for (int i = 0; i < dtObj.Rows.Count; i++)
                                    {
                                        if (dtObj.Rows[i]["smsNumber"] != null)
                                        {
                                            if (dtObj.Rows[i]["smsNumber"].ToString() != "")
                                            {
                                                if (dtObj.Rows[i]["status"].ToString() != "Sent")
                                                {
                                                    Thread.Sleep(Convert.ToInt32("50000"));
                                                    string txtMessage = dtObj.Rows[i]["sms"].ToString();
                                                    string txtDestinationNumbers = dtObj.Rows[i]["smsNumber"].ToString();
                                                    bool unicode = true;
                                                    SmsSubmitPdu[] pdu = SmartMessageFactory.CreateConcatTextMessage(txtMessage, unicode, txtDestinationNumbers);
                                                    comm.SendMessages(pdu);
                                                    obj_bal_ForAll.bal_forAll_Delete("tbl_SMS", "smsId", dtObj.Rows[i]["smsId"].ToString());
                                                }
                                            }
                                        }
                                    }
                                    if (comm.IsOpen())
                                    {
                                        comm.Close();
                                    }  
                                    }catch(Exception ex){if (comm.IsOpen()){comm.Close();}}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接