web-dev-qa-db-ja.com

なぜCOMポートへのアクセスが拒否されるのですか?

コード:

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リーダーで開かれ、複数のルートがあるためxmlを分割し、新しい各分割xmlのノードの値を取得してシリアルで送信します。最初のスプリットxmlで動作し、そのノードはシリアル経由で送信され、2番目のノードをシリアルに書き込もうとするとcomポートメッセージへのアクセスが拒否されて停止します。

私は同じシリアルコードで作成した別のアプリを持っていますが、正常に動作します(疲れた後、閉じました)...奇妙です。

13
Csharpz

シリアルポートを開くことができるのは1回だけです。ただし、コードのwhileループ内にOpen()呼び出しがあります。これは、ループの最初のパス、2番目のパスのkaboomでのみ機能します。 @cdhowieのソリューションも機能しません。SerialPortには、ドキュメントが警告している癖(別名バグ)があります。 Dispose()またはClose()呼び出しの後にワーカースレッドを終了させるには時間が必要です。時間の長さは指定されておらず、予測できません。

実際の解決策は単純で、whileループの前にOpen()呼び出しを移動するだけです。

28
Hans Passant

ハンスの答えに加えて:

私は同じ問題を抱えており、シリアルポートを開いてから閉じるまでに少しスリープ時間を置いて少し遊んでみました。私の場合、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);
    }
}

sleepTimeInMsmaxRetriesで遊ぶことができます。これらの値は、必要なユースケースでは十分であると思われたため、選択しました。

4
M. Hartner

ポートが開いている場合は、ポートが最後の接続で破棄されていないため、ポートを閉じて破棄する必要があります。

1

ハンスの答えはこれに取って代わります。コンテキストと情報提供のみを目的として残します。


使い終わったら、ポートを閉じる必要があります。ガベージコレクターは、別のハンドルを開こうとする前に、最初の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() + ",");
}
0
cdhowie

私のために働いた解決策は次のとおりです:1:try catchブロックを使用して、ポートを開閉するすべてのコードを入れてください。

2:IsOpen()関数を使用して、ポートが既に開いているかどうかを確認します。

3:例外(アクセスが拒否された)が発生した場合は、キャッチブロックにPort.Close()コードを記述して、そのポートを解放します。

4:キャッチブロックを試行する前にシリアルポートのオブジェクトを作成して、それをユニバーサルにします。

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();}}
0
Muhammad Abbas