2010-03-15 46 views
26

Làm thế nào để dỡ bỏ một DLL đã được tải bằng cách sử dụng DllImport trong C#?Dỡ bỏ một DLL được tải bằng DllImport

+0

Nếu bạn định làm như vậy chỉ để tiết kiệm vài KB bộ nhớ in chân của mô-đun đã nạp thì nó không đáng giá. CLR tự làm như vậy bất cứ khi nào appDomain được tải xuống. Tôi sẽ tò mò muốn biết nếu bạn có một số lý do thực sự để thử dỡ dll bản địa. – RBT

Trả lời

21

Cách đáng tin cậy nhất để dỡ bỏ một DLL không được quản lý từ một quá trình được tải bởi một khai báo pinvoke [DllImport] là tự tải nó, một lần nữa, bằng cách ghim LoadLibrary(). Điều đó mang lại cho bạn một xử lý đáng tin cậy cho DLL và hoạt động chính xác ngay cả khi tên mô-đun của DLL không rõ ràng. Nó không có bất kỳ ảnh hưởng nào trong thời gian chạy, ngoài bộ nạp Windows làm tăng số lượng tham chiếu nội bộ trên DLL từ 1 đến 2.

Sau đó bạn có thể ghim FreeLibrary() hai lần để giảm số tham chiếu xuống 0, vượt qua nó IntPtr bạn nhận được từ LoadLibrary(). Điều đó unloads DLL, cũng như bất kỳ DLL phụ thuộc đã được nạp.

Ghi chú rằng bạn sẽ nhận được sự thất bại rất khó chịu khi bạn cố gắng PInvoke bất kỳ chức năng xuất khẩu trên DLL một lần nữa, bất kỳ thời gian sau khi làm điều này. Các marshaller pinvoke là không biết rằng DLL không phải là xung quanh nữa và sẽ gọi chức năng tại địa chỉ nó nghĩ là vẫn còn hợp lệ. Mà bom chương trình của bạn với một ngoại lệ AccessViolation nếu bạn may mắn. Hoặc chạy một bit hoàn toàn ngẫu nhiên của mã nếu bạn không quá may mắn và không gian địa chỉ trước đây bị chiếm đóng bởi DLL đã tái sử dụng bởi một DLL. Bất cứ điều gì có thể xảy ra sau đó, không ai trong số đó tốt.

+1

-1: Ông rõ ràng đang nói về một dll bản địa. – leppie

+4

Bạn vẫn không trả lời câu hỏi của subbu. Ông đã yêu cầu làm thế nào để dỡ bỏ một DLL đã được nạp bởi DllImport, không phải bằng cách tự tải nó thông qua LoadLibrary(). – Ants

+1

@Ants: điểm tốt. Vui lòng điền vào các khoảng trống ... –

7

Điều này sẽ miễn phí một mô-đun được tải trước đó khi bạn gọi hàm P/Gọi.

[DllImport("kernel32", SetLastError=true)] 
static extern bool FreeLibrary(IntPtr hModule); 

public static void UnloadModule(string moduleName) 
{ 
    foreach(ProcessModule mod in Process.GetCurrentProcess().Modules) 
    { 
     if(mod.ModuleName == moduleName) 
     { 
      FreeLibrary(mod.BaseAddress); 
     } 
    } 
} 
+3

Điều đó sẽ * thường * hoạt động. Nhưng tên mô-đun có thể không rõ ràng, nó có thể phát hành sai DLL. –

+0

Hans là đúng. Vì vậy, có lẽ tốt hơn bằng cách sử dụng Path.GetFileName (mod.FileName) thay vì mod.ModuleName? – Peter

+0

Hoặc treo vào con trỏ bạn nhận được từ LoadLibrary và so sánh BaseAddress với điều đó. – yoyo

2

Dựa trên Peters đề nghị này làm việc cho tôi:

[DllImport("kernel32", SetLastError = true)] 
    private static extern bool FreeLibrary(IntPtr hModule); 

    public static void UnloadImportedDll(string DllPath) 
    { 
     foreach (System.Diagnostics.ProcessModule mod in System.Diagnostics.Process.GetCurrentProcess().Modules) 
     { 
      if (mod.FileName == DllPath) 
      { 
       FreeLibrary(mod.BaseAddress); 
      } 
     } 
    } 
-1

Kể từ khi tôi đi qua các thông tin ở đây trong khi tôi đang nhìn xung quanh để biết thông tin tôi con tôi sẽ đóng góp lại những gì tôi đã kết thúc làm để sửa chữa một vấn đề với Sixense SDK trên OSX IN UNITY. Bạn sẽ thấy trong đó một thực hiện tự động tải/dỡ một dylib trên OSX:

https://gist.github.com/amirebrahimi/d7b02c01bcd3ca7da144

-1

Chỉ trong trường hợp nếu bạn là fan hâm mộ của lập trình chức năng sau đó bạn có thể sử dụng LINQ để đạt được những gì @ IllidanS4 đã gợi ý:

[DllImport("kernel32", SetLastError=true)] 
static extern bool FreeLibrary(IntPtr hModule); 

public static void UnloadModule(string moduleName) 
{ 
    var loadedAssemblyModule = 
      Process.GetCurrentProcess().Modules.OfType<ProcessModule>() 
       .FirstOrDefault(x => x.ModuleName == moduleName); 

    if (loadedAssemblyModule != null) 
     FreeLibrary(loadedAssemblyModule.BaseAddress); 
} 
Các vấn đề liên quan