문제

직렬 MAG 스트라이프 리더 및 릴레이 보드 (액세스 제어 시스템)와의 커뮤니케이션을 위해 Windows 서비스를 작성하고 있습니다.

다른 프로그램이 내 서비스와 동일한 직렬 포트를 열어 프로세스를 "중단"한 후 코드가 작동을 중지하는 문제가 발생합니다 (IOExceptions가 있습니다).

코드의 일부는 다음과 같습니다.

public partial class Service : ServiceBase
{
    Thread threadDoorOpener;
    public Service()
    {
        threadDoorOpener = new Thread(DoorOpener);
    }
    public void DoorOpener()
    {
        while (true)
        {
            SerialPort serialPort = new SerialPort();
            Thread.Sleep(1000);
            string[] ports = SerialPort.GetPortNames();
            serialPort.PortName = "COM1";
            serialPort.BaudRate = 9600;
            serialPort.DataBits = 8;
            serialPort.StopBits = StopBits.One;
            serialPort.Parity = Parity.None;
            if (serialPort.IsOpen) serialPort.Close();
            serialPort.Open();
            serialPort.DtrEnable = true;
            Thread.Sleep(1000);
            serialPort.Close();
        }
    }
    public void DoStart()
    {
        threadDoorOpener.Start();
    }
    public void DoStop()
    {
        threadDoorOpener.Abort();
    }
    protected override void OnStart(string[] args)
    {
        DoStart();
    }
    protected override void OnStop()
    {
        DoStop();
    }
}

내 샘플 프로그램은 작업 스레드를 성공적으로 시작하고 DTR의 오프닝/닫기 및 인상으로 인해 Mag-Stripe Reader가 전원을 켜고 (대기 1sec), 셧다운 (1 초) 등을 만듭니다.

Hyperterminal을 시작하고 동일한 COM 포트에 연결하면 Hyperterminal은 현재 포트가 사용 중이라고 알려줍니다. Hyperterminal에서 Enter를 반복적으로 누르면 포트를 다시 열려고 시도하면 몇 번의 회수 후에도 성공합니다.

이것은 내 작업 읽기에서 IOExceptions를 유발하는 효과가 있습니다. 그러나, 초기 말단을 닫더라도, 나는 여전히 내 작업 읽기에서 동일한 ioexception을 얻는다. 유일한 치료법은 실제로 컴퓨터를 다시 시작하는 것입니다.

이 시점에서 다른 프로그램 (포트 액세스에 .NET 라이브러리를 사용하지 않음)은 정상적으로 작동하는 것으로 보입니다.

이것을 원인하는 것에 대한 아이디어가 있습니까?

도움이 되었습니까?

해결책

@Thomask

예, Hyperterminal은 실제로 Setcommstate의 DCB에서 Fabortonerror를 가능하게합니다. 이는 Serialport 객체에 의해 발생 된 대부분의 IOExceptions에 대해 설명합니다. 일부 PC / 핸드 헬드에는 또한 기본적으로 오류 플래그에 중단 된 UART가 있습니다. 따라서 직렬 포트의 Init Routine이이를 지우는 것이 필수적입니다 (Microsoft는 소홀히하지 않았습니다). 나는 최근에 이것을 더 자세히 설명하기 위해 긴 기사를 썼다 ( 이것 관심이 있다면).

다른 팁

당신은 다른 사람의 포트 연결을 닫을 수 없습니다. 다음 코드는 작동하지 않습니다.

if (serialPort.IsOpen) serialPort.Close();

객체가 포트를 열지 않았기 때문에 닫을 수 없습니다.

또한 예외가 발생한 후에도 직렬 포트를 폐쇄하고 폐기해야합니다.

try
{
   //do serial port stuff
}
finally
{
   if(serialPort != null)
   {
      if(serialPort.IsOpen)
      {
         serialPort.Close();
      }
      serialPort.Dispose();
   }
}

프로세스를 방해하려면 포트가 열려 있는지 확인한 다음 기간 동안 다시 꺼낸 다음 다시 시도해야합니다.

while(serialPort.IsOpen)
{
   Thread.Sleep(200);
}

응용 프로그램에서 포트를 열어두고 Dtrenable을 켜거나 끄는 다음 응용 프로그램이 닫힐 때 포트를 닫으려고 했습니까? 즉:

using (SerialPort serialPort = new SerialPort("COM1", 9600))
{
    serialPort.Open();
    while (true)
    {
        Thread.Sleep(1000);
        serialPort.DtrEnable = true;
        Thread.Sleep(1000);
        serialPort.DtrEnable = false;
    }
    serialPort.Close();
}

나는 DTR 시맨틱에 익숙하지 않으므로 이것이 효과가 있는지 모르겠습니다.

신뢰할 수있는 비동기 통신을 수행하는 방법

차단 방법을 사용하지 않으면 내부 도우미 클래스에는 미묘한 버그가 있습니다.

세션 상태 클래스와 함께 APM을 사용하고, 인스턴스는 호출을 통해 공유 된 버퍼 및 버퍼 커서를 관리하고 랩하는 콜백 구현 EndRead 안에 try...catch. 정상 작동에서 마지막으로 try 블록은 다음으로 겹쳐진 I/O 콜백을 설정합니다. BeginRead().

일이 잘못되면 catch 비동기 적으로 대의원을 다시 시작하는 방법으로 호출해야합니다. 콜백 구현은 즉시 종료해야합니다 catch 다시 시작 로직이 현재 세션을 파괴 할 수 있도록 차단하고 (세션 상태가 거의 손상 되었음) 새 세션을 만듭니다. 재시작 방법이 있어야합니다 ~ 아니다 세션 상태 클래스에서 구현되면 세션이 파괴되고 재생성되지 않기 때문입니다.

시리얼 포트 개체가 닫히면 (응용 프로그램이 종료 될 때 발생) 보류중인 I/O 작업이있을 수 있습니다. 이것이 그렇게되면, 시리얼 포트를 닫으면 콜백이 트리거되고 이러한 조건에서 EndRead 일반적인 통신과 구별 할 수없는 예외를 던질 것입니다. 당신은 당신의 세션 상태에 플래그를 설정하여 다시 시작하는 동작을 억제해야합니다. catch 차단하다. 이렇게하면 재시작 방법이 자연스러운 셧다운을 방해하는 것을 막을 수 있습니다.

이 아키텍처는 예기치 않게 Serialport 객체를 유지하지 않기 위해 의존 할 수 있습니다.

재시작 메소드는 직렬 포트 개체의 마감 및 재 개방을 관리합니다. 전화 후 Close()SerialPort 대상, 전화 Thread.Sleep(5) 그것을 놓을 기회를주기 위해. 다른 것이 항구를 잡을 수 있으므로 다시 열면서 이것을 처리 할 준비를하십시오.

나는 초기 말이 잘 작동하지 않는다는 결론에 도달했다고 생각합니다. 다음 테스트를 실행합니다.

  1. "콘솔 모드"에서 서비스를 시작하면 장치를 켜거나 끄기 시작합니다 (LED로 알 수 있음).

  2. 하이퍼 터미널을 시작하고 항구에 연결하십시오. 장치는 (Hyperterminal Rains DTR) 내 서비스를 이벤트 로그에 작성하여 포트를 열 수 없다고합니다.

  3. Hyperterminal을 중지하면 작업 관리자를 사용하여 올바르게 닫혀 있는지 확인합니다.

  4. 장치가 꺼져 있습니다 (Hyperterminal이 DTR을 낮추 었음), 내 앱은 이벤트 로그에 계속 글을 쓰고 포트를 열 수 없다고 말합니다.

  5. 세 번째 응용 프로그램 (공존 해야하는 응용 프로그램)을 시작하고 포트에 연결하도록 지시합니다. 나는 그렇게한다. 여기에 오류가 없습니다.

  6. 위에서 언급 한 응용 프로그램을 중지합니다.

  7. Voila, 내 서비스가 다시 시작되고 포트가 성공적으로 열리고 LED가 켜지거나 꺼집니다.

나는 동일한 결과와 함께 이와 같은 작업을 바꾸려고 시도했습니다. Hyperterminal이 한 번 "포트 캡처"(내 스레드가자는 동안)에 성공하면 서비스가 포트를 다시 열 수 없습니다.

public void DoorOpener()
{
    while (true)
    {
        SerialPort serialPort = new SerialPort();
        Thread.Sleep(1000);
        serialPort.PortName = "COM1";
        serialPort.BaudRate = 9600;
        serialPort.DataBits = 8;
        serialPort.StopBits = StopBits.One;
        serialPort.Parity = Parity.None;
        try
        {
            serialPort.Open();
        }
        catch
        {
        }
        if (serialPort.IsOpen)
        {
            serialPort.DtrEnable = true;
            Thread.Sleep(1000);
            serialPort.Close();
        }
        serialPort.Dispose();
    }
}

이 코드는 제대로 작동하는 것 같습니다. Procomm Plus를 사용하여 포트를 열고 닫는 콘솔 애플리케이션에서 로컬 컴퓨터에서 테스트했으며 프로그램이 계속 똑딱 거리고 있습니다.

    using (SerialPort port = new SerialPort("COM1", 9600))
    {
        while (true)
        {
            Thread.Sleep(1000);
            try
            {
                Console.Write("Open...");
                port.Open();
                port.DtrEnable = true;
                Thread.Sleep(1000);
                port.Close();
                Console.WriteLine("Close");
            }
            catch
            {
                Console.WriteLine("Error opening serial port");
            }
            finally
            {
                if (port.IsOpen)
                    port.Close();
            }
        }
    }

이 답변은 댓글이되기 위해 오랫동안 ...

귀하의 프로그램이 Thread.Sleep (1000)에 있고 초기 연결을 열면 하이퍼 터미널은 일련 포트를 제어한다고 생각합니다. 프로그램이 깨어나 직렬 포트를 열려고 할 때 IOException이 발생합니다.

방법을 재 설계하고 다른 방식으로 포트의 개구부를 처리하려고 노력하십시오.

편집 : 프로그램이 실패하면 컴퓨터를 재부팅해야합니다 ...

프로그램이 실제로 닫히지 않았기 때문에 TaskManager를 열고 프로그램 서비스를 찾을 수 있는지 확인하십시오. 응용 프로그램을 종료하기 전에 모든 스레드를 중지하십시오.

서비스가 항구를 "소유"하는 것을 막을 좋은 이유가 있습니까? 내장 UPS 서비스를보십시오. COM1에 UPS가 첨부되어 있다고 말하면 포트 작별 인사를 할 수 있습니다. 포트를 공유하기위한 강력한 운영 요구 사항이 없다면 똑같이 할 것을 제안합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top