2012-07-23 2 views
1

현재 Microsoft Visual C# 2010 Express에서 코드를 작성하는 동안 CANBus adapter으로 작업하고 있습니다. CANBus 직렬 어댑터를 통해 TEKTRONIX 020-2924-XX DPO DEMO 2 보드에서 메시지를 읽는 별도의 스레드를 실행하는 GUI를 만들려고합니다.CANBus 어댑터의 숨겨진 코드에서 스레드 종료

나는 (setReceiveCallBackThread라고 불리는) 스레드를 종료하는 안전한 방법을 찾아 내려고 노력했다. 그러나이 특정 스레드는 스레드의 코드를 사용할 수 없다는 점에서 특별합니다. 이것은 CANBus API의 일부입니다.

스레드를 안전하게 중지하는 방법에 대해 웹 (특히 스택 오버플로)을 검색했습니다. 중단 방법을 사용하는 것이 최후의 수단이어야한다는 것을 발견했습니다.

그래서 스레드에서 중단을 사용할 수 없다고 결정하면 canplus_setReceiveCallBack 서브 루틴에서 예외 처리를 사용해야합니다. 그러나 문제는 canplus_setReceiveCallBack에 액세스 할 수 없다는 것입니다. 코드가 숨겨져 있습니다. 코드에 액세스 할 수 없기 때문에 이는 고유 한 상황임을 기억하십시오. 콜백 함수의 코드를 볼 수있는 다른 모든 상황과 달리이 서브 루틴의 코드는 볼 수 없습니다.

아래의 코드는 문제를 분석하는 데 필요한 모든 것이어야하며 스레드에서 Abort()를 사용하는 대신에 다른 방법이 필요합니다.

// CANSnifferForm.cs 

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Threading; 
using System.Runtime.InteropServices; 
using System.IO; 
using System.Diagnostics; 

namespace WindowsFormsApplication1 
{ 


    public partial class CANSnifferForm : Form 
    { 

     // per the api document. "This is a blocking call and must be called on a separate thread." 
     // the code previously after setCallback... was never being reached because the call is blocking. 
     // this calls the setCallbackThread function in another thread so processing can continue. 
     Thread setReceiveCallBackThread; 
     bool stop; 

     int can; // Return value of canplus_open 

     uint idFilter; // Filter values entered by user 
     ulong lenFilter; 
     bool stopThread; // Used for stopping thread   

     public CANSnifferForm() 
     { 
      InitializeComponent(); 
      del = new EASYSYNC.CallbackDelegate(callback); 
     } 


     private void callback(ref EASYSYNC.CANMsg msg) 
     { 
      // Populate the dataGridView 
      if(InvokeRequired) 
       BeginInvoke(del, msg); 
      else 
       this.dataGridView1.Rows.Add(msg.id, msg.len, msg.data, msg.timestamp); 
     } 

     private EASYSYNC.CallbackDelegate del; 

     private void StartRestart_Click(object sender, EventArgs e) 
     { 
      this.CANSnifferStatusBox.Clear(); 
      this.CANSnifferStatusBox.AppendText("CAN closed"); 
      this.ProcessStatusBox.Clear(); 
      this.ProcessStatusBox.AppendText("Stopped"); 
      EASYSYNC.CANMsg msg = new EASYSYNC.CANMsg(); 
      msg.id = 1; 
      msg.timestamp = 2; 
      msg.flags = 3; 
      msg.len = 4; 
      msg.data = 5; 

      // Attempt to open CANBus adapter 
      can = EASYSYNC.canplus_Open(IntPtr.Zero, "1000", IntPtr.Zero, IntPtr.Zero, 0); 

      if (can < 0) 
      { 
       // CANBus Adapter not opened 
       this.ErrorBox.Clear(); 
       this.ErrorBox.AppendText("Error opening CAN"); 
       return; 
      } 

      // CANBus Adapter successfully opened 
      this.CANSnifferStatusBox.Clear(); 
      this.CANSnifferStatusBox.AppendText("CAN open"); 

      // Initialize thread 
      setReceiveCallBackThread = new Thread(() => EASYSYNC.canplus_setReceiveCallBack(can, del)); 

      // Attempt for CANBus adapter to listen 
      if (EASYSYNC.canplus_Listen(can) < 0) 
      { 
       // CANBus Adapter not listening 
       this.ErrorBox.Clear(); 
       this.ErrorBox.AppendText("Error setting listen mode\n"); 
       EASYSYNC.canplus_Close(can); 
       this.CANSnifferStatusBox.Clear(); 
       this.CANSnifferStatusBox.AppendText("CAN closed"); 
       return; 
      } 

      // CANBus Adapter successfully listening 
      this.CANSnifferStatusBox.Clear(); 
      this.CANSnifferStatusBox.AppendText("CAN Listening\n"); 

      // Place thread in background. Then start it 
      setReceiveCallBackThread.IsBackground = true; 
      setReceiveCallBackThread.Start(); 
      while(!setReceiveCallBackThread.IsAlive); 

      this.ProcessStatusBox.Clear(); 
      this.ProcessStatusBox.AppendText("Running\n");  


     } 

     private void FilterData_SelectedIndexChanged(object sender, EventArgs e) 
     { 

     } 


     private void Form1_Load(object sender, EventArgs e) 
     { 

     } 
     private void Stop_Click(object sender, EventArgs e) 
     { 
      setReceiveCallBackThread.Abort(); // Stop thread 
      while (setReceiveCallBackThread.IsAlive == true) 
      { 

      } 

      this.ProcessStatusBox.Clear(); 
      this.ProcessStatusBox.AppendText("Stopped"); 


      // Attempt to flush CANBus Adapter 
      if (EASYSYNC.canplus_Flush(can) < 0) 
      { 
       // CANBus not flushing 
       this.ErrorBox.Clear(); 
       this.ErrorBox.AppendText("Error flushing CAN"); 
       EASYSYNC.canplus_Close(can); // Close CANBus Adapter 
       this.CANSnifferStatusBox.Clear(); 
       this.CANSnifferStatusBox.AppendText("CAN closed"); 
       return; 
      } 
      // Attempt to reset CANBus Adapter 
      if (EASYSYNC.canplus_Reset(can) < 0) 
      { 
       // CANBus not resetting 
       this.ErrorBox.Clear(); 
       this.ErrorBox.AppendText("Error resetting CAN"); 
       EASYSYNC.canplus_Close(can); // Close CANBus Adapter 
       this.CANSnifferStatusBox.Clear(); 
       this.CANSnifferStatusBox.AppendText("CAN closed"); 
       return; 
      } 

      this.CANSnifferStatusBox.Clear(); 
      this.CANSnifferStatusBox.AppendText("CAN closed"); 
      this.ErrorBox.Clear(); 
     } 

     private void FilterID_KeyDown(object sender, EventArgs e) 
     { 

     } 

     private void FilterLength_KeyDown(object sender, EventArgs e) 
     { 

     } 


     private void FilterID_MaskInputRejected(object sender, MaskInputRejectedEventArgs e) 
     { 
      if (FilterIDBox.MaskFull) 
      { 

      } 
      else if (e.Position == FilterIDBox.Mask.Length) 
      { 

      } 
      else 
      { 

      } 
     } 

     private void FilterLength_MaskInputRejected(object sender, MaskInputRejectedEventArgs e) 
     { 
      if (FilterLengthBox.MaskFull) 
      { 

      } 
      else if (e.Position == FilterLengthBox.Mask.Length) 
      { 

      } 
      else 
      { 

      } 

     } 
    } 
} 
+0

내 원래의 대답은 정확 편집을 확인하십시오

setReceiveCallBackThread = new Thread(() => { EASYSYNC.canplus_setReceiveCallBack(can, del); Trace.WriteLine("EASYSYNC thread terminated"); }); 

귀하의 Stop_Click 방법으로 스레드를 중지 할 수 있습니다. – Tergiver

+0

그래, 이런 종류의 코드는 다루기가 매우 어려운 카테고리에 들어 맞습니다. .NET에서 호출 할 수있는 모든 종류의 산업용 버스 인터페이스는 실제로 원시 코드로 구현됩니다. COM은 상용구입니다. 이러한 코드를 중단하면 중단없이 관리되는 코드 만 일관된 방식으로 중단 될 수 있습니다. 기본 Winapi 호출 WaitForSingleObjectEx(), bAlertable 인수를 사용합니다. 모두가 비 Ex 버전을 사용하거나 FALSE를 전달합니다. 이것이 가능하다면 포기해야 할 것입니다. –

+0

실제로 WaitFor에서 블로킹 중이며 중지 할 수없는 경우, 'Thread.IsBackgroundThread' 속성을 true로 설정하면 응용 프로그램이 종료 될 때 종료됩니다. – Tergiver

답변

0

내 원래 대답이 잘못되었습니다. API 가이드에 따라

들어오는 모든 메시지에 대해 콜백을 수신하는 함수를 정의합니다. 이것은 차단 호출이며 별도의 스레드에서 호출되어야합니다. 이 콜백 함수의 등록을 취소하려면, cbfn을 NULL로 사용하여 canplus_setReceiveCallback을 호출 할 수 있습니다.

문서에서 차단 된 통화가 종료되는 시점을 알려주지 않으므로 NULL로 canplus_setReceiveCallback을 호출 할 때 종료되는 것으로 추정됩니다.

쉽게 테스트 할 방법이 없지만 원래 호출이 반환 될 때 텍스트를 출력하여이를 확인할 수 있습니다. ,

EASYSYNC.canplus_setReceiveCallBack(can, null); 
+0

컴퓨터에서 CANBU와 Demoboard를 물리적으로 단절하지 않으면 스레드가 멈추지 않는 것처럼 보입니다. 심지어 스레드가 완료되면 코드가 진행될 것인지 확인하는 논리를 사용했습니다. 나는 내가 할 수있는 일이별로 없다고 생각한다. 하지만 평소대로 당신에게 감사드립니다. 귀하의 답변은 매우 유용합니다 !! – EmbedThis