2017-04-18 50 views
0

我在使用DllImport訪問它的函數時打了一個第三方C++ dll的調用問題,這個dll是我用類包裝的。我怎樣才能構建一個try-catch-finally塊來最終處理錯誤?

該DLL需要在使用之前打開一個會話,該會話在執行操作時返回用於引用該會話的整數句柄。完成後,必須使用相同的句柄關閉會話。所以我做了這樣的事情:

public void DoWork(string input) 
{ 
    int apiHandle = DllWrapper.StartSession(); 

    try 
    { 
     // do work using the apiHandle 
    } 
    catch(ApplicationException ex) 
    { 
     // log the error 
    } 
    finally 
    { 
     DllWrapper.CloseSession(apiHandle); 
    } 
} 

我的問題是,CloseSession()有時會導致DLL的問題拋出Error運行線程時:

System.AggregateException:一個或多個發生錯誤。 ---> System.AccessViolationException:試圖讀取或寫入受保護的 內存。這通常表明其他內存已損壞。

我不知道我能做些什麼來阻止這個錯誤,因爲它似乎是通過以線程方式使用Dll而產生的 - 它應該是線程安全的。但是由於我的CloseSession()函數除了調用該Dll的關閉函數外什麼都不做,因此我沒有太多擺動空間來「修復」任何東西。

但最終結果是會話無法正常關閉。所以當這個過程再次嘗試時,它會遇到一個開放的會話,並不斷拋出新的錯誤。該會議絕對被關閉。

我不知道如何設計更強大的錯誤處理語句,以確保會話始終關閉?

+4

我想問題是,錯誤應該如何「處理」?如果關閉會話會從第三方代碼中引發錯誤,並且我們無法繼續關閉會話,那麼下一步是什麼?我的意思是,你可以在'finally'裏放置另一個'try/catch',但是你會如何迴應這個錯誤?如果第三方工具不起作用,可以做些什麼? – David

+4

如果這是由線程引起的,那麼也許可以通過在StartSession周圍使用[lock(){}](https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx)語句來解決此問題()和CloseSession()方法調用。 – Serge

+0

@David第三方工具失敗是間歇性的 - 當你第二次嘗試時,它通常起作用。我的實際代碼目前是一個非常糟糕的遞歸和控制流程,試圖確保始終發生。我很討厭添加更多,但如果必須,我必須:( –

回答

1

我會改變包裝來包括處理外部資源,也包裝句柄。即而不是通過句柄來表示一個會話,你可以用一個包裝器對象來表示它。

此外,將調用包裝到lock -statements(如@Serge建議)中,可以完全防止多線程問題。請注意,鎖對象是靜態的,所以所有的DllWrapper都使用同一個鎖對象。

public class DllWrapper : IDisposable 
{ 
    private static object _lockObject = new object(); 

    private int _apiHandle; 
    private bool _isOpen; 

    public void StartSession() 
    { 
     lock (_lockObject) { 
      _apiHandle = ...; // TODO: open the session 
     } 
     _isOpen = true; 
    } 

    public void CloseSession() 
    { 
     const int MaxTries = 10; 

     for (int i = 0; _isOpen && i < MaxTries; i++) { 
      try { 
       lock (_lockObject) { 
        // TODO: close the session 
       } 
       _isOpen = false; 
      } catch { 
      } 
     } 
    } 

    public void Dispose() 
    { 
     CloseSession(); 
    } 
} 

請注意,方法現在是實例方法。

現在可以保證會議的閉幕與using語句:

using (var session = new DllWrapper()) { 
    try { 
     session.StartSession(); 
     // TODO: work with the session 
    } catch(ApplicationException ex) { 
     // TODO: log the error 
     // This is for exceptions not related to closing the session. If such exceptions 
     // cannot occur, you can drop the try-catch completely. 
    }  
} // Closes the session automatically by calling `Dispose()`. 

可以提高通過調用這個類Session和方法OpenClose命名。這個類的用戶不需要知道它是一個包裝器。這只是一個實現細節。此外,這些方法的命名現在是對稱的,並且不需要重複名稱Session

通過封裝所有會話相關的東西,包括錯誤處理,從錯誤情況中恢復和資源處置,你可以大大減少代碼中的混亂。 Session類現在是一個高級抽象。舊的DllWrapper位於低級和高級之間的中間位置。

+0

謝謝你。我非常喜歡將它作爲解決問題並同時整理代碼的手段。這種方法的初始原型似乎已經減慢了很多處理速度,我不知道爲什麼。我正在調查 - 如果我能解決問題,我會很樂意接受。 –