2011-05-24 56 views
0

我一直在開發使用DLL互操作到外部數據庫應用程序C#應用程序。C#-My應用和Interopability(DLL/COM)與外部應用程序

這種外部應用程序在同一時間與我的C#應用​​程序一起啓動,可只要我的C#應用​​程序正在運行。

現在真正的問題是有關管理,我需要建立與外部應用程序交互的對象。

當我宣佈,可從引用的DLL的這些對象有與文件(即是私有的)操作方法的對象和運行一些查詢(例如,如果通過這個外部應用程序GUI做到了)。這些對象被銷燬「由我」使用Marshal.ReleaseComObject(A_OBJECT)而其他在diferent應用程序域中運行,通過使用AppDomain.CreateDomain("A_DOMAIN"),執行操作和調用AppDomain.Unload("A_DOMAIN"),釋放用於操作的DLL ...

這些變通辦法是由確保此外部應用程序不會「阻止」在這些操作中使用的文件,因此允許從文件夾中刪除或移動它們。

例如

private static ClientClass objApp = new ClientClass(); 

public bool ImportDelimitedFile(
       string fileToImport, 
       string outputFile, 
       string rdfFile)  

{ 
    GENERICIMPORTLib import = new GENERICIMPORTLibClass(); 

    try 
    { 
     import.ImportDelimFile(fileToImport, outputFile, 0, "", rdfFile, 0); 
     return true; 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show(ex.Message); 
     return false; 
    } 
    finally 
    { 
     System.Runtime.InteropServices.Marshal.ReleaseComObject(import); 
     import = null; 
    } 
} 

public int DbNumRecs(string file) 
{ 
    if (!File.Exists(file)) 
    { 
     return -1; 
    } 

    System.AppDomain newDomain = System.AppDomain.CreateDomain(); 
    COMMONIDEACONTROLSLib db = new COMMONIDEACONTROLSLibClass(); 
    try 
    { 
     db = objApp.OpenDatabase(file); 
     int count = (int)db.Count; 

     db.Close(); 
     objApp.CloseDatabase(file); 

     return count; 
    } 
    catch (Exception ex) 
    { 
     return -1; 
    } 
    finally 
    { 
     System.AppDomain.Unload(newDomain); 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
    } 
} 

這兩個「解決方案」都是通過反覆試驗得出的,因爲我沒有任何一種API手冊。這些方案是否正確?你能解釋我的差異嗎?我真的需要使用這兩種解決方案還是應該足夠?

謝謝!

回答

1

您使用的AppDomain是錯誤的。僅僅因爲你在X行之前創建了一個新的AppDomain並不意味着該行X實際上在該AppDomain中執行。

你需要馬歇爾代理類回在您的AppDomain,並在當前使用它。

public sealed class DatabaseProxy : MarshallByRefObject 
{ 
    public int NumberOfRecords() 
    {  
     COMMONIDEACONTROLSLib db = new COMMONIDEACONTROLSLibClass(); 
     try 
     { 
      db = objApp.OpenDatabase(file); 
      int count = (int)db.Count; 

      db.Close(); 
      objApp.CloseDatabase(file); 

      return count; 
     } 
     catch (Exception ex) 
     { 
      return -1; 
     } 
    } 
} 

public int NumberOfRecords() 
{  

    System.AppDomain newDomain = null; 

    try 
    { 
     newDomain = System.AppDomain.CreateDomain(); 
     var proxy = newDomain.CreateInstanceAndUnwrap(
            typeof(DatabaseProxy).Assembly.FullName, 
            typeof(DatabaseProxy).FullName); 
     return proxy.NumberOfRecords(); 
    } 
    finally 
    { 
     System.AppDomain.Unload(newDomain); 
    } 
} 

實際上,你可以創建一個馬歇爾回到COM對象本身,而不是通過你的代理實例吧。這段代碼完全是在這裏寫的,沒有經過測試,所以可能是越野車。

+0

謝謝,我將嘗試@James發佈的解決方案,我認爲它對我的目的更清潔。謝謝 – Libas 2011-05-25 08:17:03

1

第一種解決方案是最好的解決方案。非託管COM使用引用計數方案; IUnknown是底層引用計數接口:http://msdn.microsoft.com/en-us/library/ms680509(VS.85).aspx。當引用計數達到零時,它被釋放。

當您在.NET中創建COM對象時,會在COM對象周圍創建一個包裝器。包裝器維護一個指向底層IUnknown的指針。當發生垃圾收集時,包裝程序將調用底層的IUnknown :: Release()函數在最終化過程中釋放COM對象。正如您注意到的那樣,問題在於有時COM對象會鎖定某些關鍵資源。通過調用Marshal.ReleaseComObject,可以立即調用IUnknown :: Release,而無需等待(或啓動)一般垃圾回收。如果沒有其他對COM對象的引用被保存,它將立即被釋放。當然,在這一點之後.NET包裝將變得無效。

第二種解決方案因GC.Collect()的調用而顯然工作。該解決方案更笨拙,速度更慢,可靠性更低(COM對象可能不一定是垃圾回收:行爲取決於特定的.NET Framework版本)。AppDomain的使用沒有任何貢獻,因爲除了創建一個空域然後卸載它之外,您的代碼實際上並沒有做任何事情。 AppDomain對於隔離加載的.NET Framework程序集很有用。由於涉及非託管COM代碼,AppDomain實際上並不會有用(如果需要隔離,請使用進程隔離)。第二個功能可能可以重寫爲:

public int DbNumRecs(string file) { 
     if (!File.Exists(file)) { 
      return -1; 
     } 
     // don't need to use AppDomain 
     COMMONIDEACONTROLSLib db = null; // don't need to initialize class here 
     try { 
      db = objApp.OpenDatabase(file); 
      return (int)db.Count; 
     } catch (Exception) } // don't need to declare unused ex variable 
      return -1; 
     } finally { 
      try { 
       if (db != null) { 
        db.Close(); 
        Marshal.ReleaseComObject(db); 
       } 
       objApp.CloseDatabase(file); // is this line really needed? 
      } catch (Exception) {} // silently ignore exceptions when closing 
     } 
    } 
+0

感謝您的提示!我已閱讀了很多關於RCW和COM引用計數方案的機制,但爲了真誠,我不確定這是否是最好的解決方案...我將改變我用這個extern工作的所有方法應用程序,測試它並將結果移植到這裏!再次感謝! – Libas 2011-05-25 08:14:55

+0

還有一個問題,發佈順序很重要,對吧? 想象這種情況: db = objApp.OpenDatabase(file); table =(COMDBLib)db.TableDef();我應該在finally子句中: 先做Marshal.ReleaseComObject(table); 然後Marshal.ReleaseComObject(db); 我這樣問,這是由於「table」的引用計數器應該在理論上取決於對「db」的引用,這是由於「table」由「db」初始化的事實... 對? 謝謝。 – Libas 2011-05-25 09:38:36

+0

這取決於圖書館可能會也可能不重要。例如,如果「table」實現在內部持有對「db」變量的引用,那麼在你和「table」釋放它之前,「db」不會被釋放。但實際上,我一般按照你的建議來做,而不用擔心。 – 2011-05-26 16:05:56

相關問題