[英]Should I call FreeLibrary explicitly after loading unamanaged DLL dynamically
[英]Have to do FreeLibrary 2 times although I have done LoadLibrary only 1 time. Also, after unloading DLL, when trying to load it again, error happens
使用以下 C# 代碼加載和卸載 C++ DLL。
我只加載 DLL 一次,但代碼必須卸載 DLL 2 次。 同樣在卸載 DLL 后,當我再次加載它並調用 DLL 的導出函數時,我收到以下錯誤消息:
嘗試讀取或寫入受保護的內存。 這通常表明其他內存已損壞。
DLL 依賴於其他 DLL。
/// //////////////handle of FDD DLL:
System.IntPtr SystemIntPtr_handle_of_DLL=System.IntPtr.Zero;
private void button4_Click(object sender, EventArgs e)
{
try
{
string string_Dependency_path = ".\\DLL_Dependencies\\";
Call_DLL.SetDllDirectory(string_Dependency_path);
SystemIntPtr_handle_of_DLL = Call_DLL.LoadLibrary("DLL.dll");
if (SystemIntPtr_handle_of_DLL == System.IntPtr.Zero) { throw new Exception("DLL did not load"); }
}
catch (Exception Exception_Object) { MessageBox.Show(Exception_Object.Message); }
}
private void button5_Click(object sender, EventArgs e)
{
try
{
int int_FreeLibrary_counter = 0;
while (Call_DLL.FreeLibrary(SystemIntPtr_handle_of_DLL))
{
int_FreeLibrary_counter++;
}
MessageBox.Show("DLL unloaded. You will have to load it again. (Unloaded" + int_FreeLibrary_counter + " times)");
}
catch (Exception Exception_Object) { MessageBox.Show(Exception_Object.Message); }
}
調用方法:
class Call_DLL
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetDllDirectory(string string_Dependency_path);
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string string_DLL_name);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FreeLibrary(IntPtr IntPtr_handle_of_DLL);
}
我忘了在加載 DLL 之后和卸載 DLL 之前包含以下調用 DLL 導出函數的內容。 認為這些導出函數內部正在發生其他事情是有道理的,這導致了奇怪的行為(即加載 1 次,必須卸載 2 次):
[DllImport(@"DLL.dll", EntryPoint = "getFreq")]
public static extern System.IntPtr getFreq([In, Out, MarshalAs(UnmanagedType.LPStr)] string char_Address, [In, Out, MarshalAs(UnmanagedType.I4)]int int_Num, [In, Out, MarshalAs(UnmanagedType.I4)]int int_Samp);
[DllImport(@"DLL.dll", EntryPoint = "setNat")]
public static extern System.IntPtr setNat([In, Out, MarshalAs(UnmanagedType.LPStr)]string char_Address_File_nat);
[DllImport(@"DLL.dll", EntryPoint = "getMode")]
public static extern System.IntPtr getMode();
您問題的更新提供了足夠的信息來解釋該行為。
LoadLibrary
來加載您的 DLL,該 DLL 占引用之一。DllImport
p/invoke 函數,這又導致調用LoadLibrary
。 這是對圖書館的另一個參考。FreeLibrary
,這會成功兩次,因為有兩次調用LoadLibrary
。 但是現在您遇到了麻煩,因為您已經在 p/invoke 系統的背后,並且它仍然認為它擁有對 DLL 的引用之一。 您在第二次調用FreeLibrary
從中竊取的參考。
我想您缺少的信息是DllImport
p/ DllImport
如何綁定到函數。 您希望他們通過調用GetModuleHandle
獲得模塊句柄。 他們沒有。 他們調用LoadLibrary
。 它們在第一次被調用時執行此操作,並且它們加載的模塊保持加載狀態,直到程序集本身卸載。
最重要的是,您必須遵守規則。 規則規定,對LoadLibrary
每次調用都與對FreeLibrary
的調用相匹配。 您調用LoadLibrary
一次。 所以你也必須只調用一次FreeLibrary
。 停止調用它兩次,一切都很好。
我懷疑您實際上是在嘗試安排一個可以加載和卸載 DLL 的系統。 這可以防止您通過DllImport
使用 p/invoke 。 您必須使用LoadLibrary
和GetProcAddress
來完成這一切。
您對SetDllDirectory
使用看起來有些混亂。 您期望".\\\\DLL_Dependencies\\\\"
與什么相關? 提供完整路徑。
如果您只調用 LoadLibrary 一次,那么您也只需要調用 FreeLibrary 一次。 第二次調用它正在進入未定義的行為。
如果您查看 MSDN 文檔https://msdn.microsoft.com/en-us/library/windows/desktop/ms683152%28v=vs.85%29.aspx如果成功,該函數將返回一個非零值,或者0 如果有錯誤。
如果你想確保它被卸載,那么你可以在調用 FreeLibrary 之后調用 GetModuleHandle 並且它應該返回 null。 https://msdn.microsoft.com/en-us/library/windows/desktop/ms683199(v=vs.85).aspx
更新了您的調用方法類以包括:
[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
然后將您的 button5 單擊處理程序更改為以下內容:
private void button5_Click(object sender, EventArgs e)
{
try
{
int freeCount = 0;
while(Call_DLL.GetModuleHandle("DLL.dll") != System.IntPtr.Zero)
{
Call_DLL.FreeLibrary(SystemIntPtr_handle_of_DLL);
freeCount++;
}
MessageBox.Show("DLL unloaded. You will have to load it again. (Unloaded" + int_FreeLibrary_counter + " times)");
}
catch(Exception Exception_Object)
{
MessageBox.Show(Exception_Object.Message);
}
}
我知道這個問題已經完成,但是在處理類似的問題時,我找到了一種使用 DllImport 動態加載和卸載庫的方法,無一例外。
該方法基於以下利益相關者:
- DllImport 在第一次調用包裝的方法時加載庫。
- 如果一個庫已經加載到堆棧上,則對方法的調用將使用庫的這個實例而不是加載另一個
基於這些考慮,您可以通過以下方式管理您的模塊:
public class MyWrapper
{
int _libraryHandle = 0;
[DllImport("mylibrary.dll")]
public external static void MyMethod();
}
public MyWrapper()
{
_libraryHandle = LoadLibrary("mylibrary.dll");
}
public Dispose()
{
FreeLibrary(_libraryHandle);
}
通過這種方式,每次您將包裝器實例化時,都會在堆棧上加載新的庫,沒有任何異常。
根據https://msdn.microsoft.com/en-us/library/windows/desktop/ms683152%28v=vs.85%29.aspx (第一次谷歌命中) If the function succeeds, the return value is nonzero.
您的卸載循環基本上是說“只要釋放庫有效,就再次釋放它,直到卸載失敗”。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.