2009-11-30 8 views
1

저는 USB 장치로 작업하고 있습니다. 이 장치는 메시지를 수신하고 언제 또는 얼마나 자주 보내는 지 알 수 없습니다. 드라이버와 함께 제공되는 API는 장치가 메시지를받을 때 콜백을 제공하는 setreceiveCallBack 함수를 지정합니다. 하지만 임의의 시간이나 간격으로 garbagecollected delegate exeption에서 콜백을 수신합니다. 내 문제에 대한 해결책을 찾았지만 해결책 중 어느 것도 내 경우에는 효과가없는 것 같습니다. 다음 내 코드의 가장 큰 부분이다 :콜백에서 garbagecollected 대리인

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

namespace CallBacktesting 
{ 
    public unsafe delegate void callBack(Form1.CANMsg *pmsg); 

    public partial class Form1 : Form 
    { 
     uint handle; 
     static WriteLog log = new WriteLog(); 
     Boolean getCan = false; 
     static int frameCount = 0; 
     static CANMsg newmsg = new CANMsg(); 
     callBack _setCallBack; 
     List<string> write = new List<string>(); 

     public Form1() 
     { 
      InitializeComponent(); 
     } 


     private void buttonOpen_Click(object sender, EventArgs e) 
     { 
       // Open connection 
     } 

     private void buttonClose_Click(object sender, EventArgs e) 
     { 
       // Close connection 
     } 

     private void buttonCallBack_Click(object sender, EventArgs e) 
     { 
      if (!getCan) 
      { 
       int rv; 
       unsafe 
       { 
        callBack _setCallBack = new callBack(call); 
        rv = canusb_setReceiveCallBack(handle, _setCallBack); 
       } 
       label1.Text = rv.ToString(); 
      } 
      else 
      { 
       _setCallBack = null; 
       int rv = canusb_setReceiveCallBack(handle, _setCallBack); 
       GC.KeepAlive(_setCallBack); 
       label1.Text = rv.ToString(); 
      } 
     } 

     public unsafe void call(CANMsg *pmsg) 
     { 
      newmsg = *pmsg; 
      update(); 
     } 

     private void buttonExit_Click(object sender, EventArgs e) 
     { 
      GC.KeepAlive(_setCallBack); 
      Application.Exit(); 
     } 

     [DllImport("canusbdrv.dll", EntryPoint = "canusb_setReceiveCallBack")] 
     public static extern int canusb_setReceiveCallBack(uint handle, callBack callBack); 

     unsafe private void timer_Tick(object sender, EventArgs e) 
     { 
       // update the form with received messages 
     } 

     public void update() 
     { 
      CANMsg msgrec = newmsg; 
      // Build str from messages with all data 
      write.Add(str); 
      log.logWrite(str); 
      frameCount++; 
     } 
    } 

    public class WriteLog 
    { 

     private void OpenFile() 
     {  } 

     public void logWrite(string log) 
     {  } 

     public void logAdd(string log) 
     {  } 

     private void logClose() 
     {  } 
    } 
} 
+0

가독성을 위해 일부 코드를 제거하고 오류를 수정했습니다 (setCallBack 대신 _setCallBack 사용). –

답변

2

을,이를 방지하기 위해 않습니다.

개인 필드에 저장하는 것을 피할 수 있습니다. 각 버튼 클릭이 새로운 콜백을 만들 수 있기 때문에

E.x :


Class Form1 
{ 

callBack _setCallBack; 

private void buttonCallBack_Click(object sender, EventArgs e) 
{ 


       _setCallBack = new callBack(call); 
       rv = canusb_setReceiveCallBack(handle, _setCallBack); 

} 

} 

은 그러나 몇 가지 문제가있을 수 있습니다. 이전 콜백을 참조해야하는 경우 문제가 될 수 있습니다.

리팩터링 코드는 SafeHandle을 사용하여 canusb_Open에서 반환 한 핸들을 저장하는 코드라고 생각합니다.

이 클래스를 디자인 할 것입니다.


class CanUsbSafeHandle : SafeHandle 
{ 
    private EventHandler _receiveCallBack; 
    private readonly object _receiveCallBackLock = new object(); 

    public event EventHandler ReceiveCallBack 
    { 
     add 
     { 
      lock (_receiveCallBackLock) 
      { 
       bool hasListeners = (_receiveCallBack != null); 
       _receiveCallBack += value; 
       //call canusb_setReceiveCallBack only when 1 or more listeners were added 
       //and there were previously no listeners 
       if (!hasListeners && (_receiveCallBack != null)) 
       { 
        canusb_setReceiveCallBack(this, setCallBack); 
       } 
      } 
     } 
     remove 
     { 
      lock (_receiveCallBackLock) 
      { 
       bool hasListeners = (_receiveCallBack != null); 
       _receiveCallBack -= value; 
       //call canusb_setReceiveCallBack only when there are no more listeners. 
       if(hasListeners && (_receiveCallBack == null)) 
       { 
        canusb_setReceiveCallBack(this, null); 
       } 
      } 
     } 
    } 

    public CanUsbSafeHandle() 
     : base(IntPtr.Zero, true) 
    { 
    } 

    public override bool IsInvalid 
    { 
     get { return handle == IntPtr.Zero; } 
    } 

    protected override bool ReleaseHandle() 
    { 
     return canusb_Close(handle); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      lock (_receiveCallBackLock) 
      { 
       _receiveCallBack = null; 
      } 
     } 
     base.Dispose(disposing); 
    } 
} 

그 방법의하여 SafeHandle은 '콜백을받을'대리인의 수명이하여 SafeHandle에서 관리 할 관리합니다.

+0

크레이 건 (Kragen)은 내가 놓친 것을 지적했다. 즉, 'setCallBack'대신 'call'이 전달된다. –

+0

oke 나는 setCallBack을 private 필드로 조정하고 대신에 대신 전달되는 _setCallBack을 복원했습니다. 프로그램은 1 CallBack 만 사용해야하므로 지금 Safehandle 제안을 시도하지 않습니다. (그리고 실제로 이해하고 있는지 잘 모르겠습니다). 지금 테스트 해 보겠습니다. –

+0

대단히 감사합니다. 사적인 분야와 마찬가지로 간단합니다. –

2

이 올바른/오타?인가

callBack setCallBack = new callBack(call); 
rv = canusb_setReceiveCallBack(handle, call); 

당신은 콜백의 인스턴스를 만들 수 있지만 다음 canusb_setReceiveCallBack에 다른 뭔가를 전달하는 표시 - setCallBack 대신에 전달하겠습니까?

또한, 당신이 setCallBack를 선언하는이 라인에하는 것은 지역 변수로, 그래서 당신은 여전히 ​​로컬 아마 쓰레기 수집됩니다 변수를 범위 전달하는, call 대신 setCallBack을 통과 당신이 경우에도 (난 것으로 나타났습니다 GC.KeepAlive(setCallBack); 은 명시 적으로



       callBack setCallBack = new callBack(call); 
       rv = canusb_setReceiveCallBack(handle, call); 

코드에서 어떤 곳이 참조 된 대리자가 없기 때문에 당신이 'canusb_setReceiveCallBack'를 호출 한 후 대리자가 가비지 컬렉션에 사용할 수 있습니다 할 때) 코드에서

+0

둘 다 시도했습니다. setCallBack을 전달해야하는지 확실하지 않거나 funtion 자체를 전달할 수 있습니다. 하지만 아무런 차이가없는 것처럼 보였습니다. 둘 다 예외를 둡니다. 또한 처음에 setCallBack을 선언하여 응용 프로그램의 수명 동안 살아있게했습니다. –