コード:
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ポートメッセージへのアクセスが拒否されて停止します。
私は同じシリアルコードで作成した別のアプリを持っていますが、正常に動作します(疲れた後、閉じました)...奇妙です。
シリアルポートを開くことができるのは1回だけです。ただし、コードのwhileループ内にOpen()呼び出しがあります。これは、ループの最初のパス、2番目のパスのkaboomでのみ機能します。 @cdhowieのソリューションも機能しません。SerialPortには、ドキュメントが警告している癖(別名バグ)があります。 Dispose()またはClose()呼び出しの後にワーカースレッドを終了させるには時間が必要です。時間の長さは指定されておらず、予測できません。
実際の解決策は単純で、whileループの前にOpen()呼び出しを移動するだけです。
ハンスの答えに加えて:
私は同じ問題を抱えており、シリアルポートを開いてから閉じるまでに少しスリープ時間を置いて少し遊んでみました。私の場合、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
で遊ぶことができます。これらの値は、必要なユースケースでは十分であると思われたため、選択しました。
ポートが開いている場合は、ポートが最後の接続で破棄されていないため、ポートを閉じて破棄する必要があります。
ハンスの答えはこれに取って代わります。コンテキストと情報提供のみを目的として残します。
使い終わったら、ポートを閉じる必要があります。ガベージコレクターは、別のハンドルを開こうとする前に、最初の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() + ",");
}
私のために働いた解決策は次のとおりです: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();}}