簡體   English   中英

盡管我只做了 1 次 LoadLibrary,但必須做 2 次 FreeLibrary。 此外,卸載DLL后,再次嘗試加載時,會發生錯誤

[英]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();

您問題的更新提供了足夠的信息來解釋該行為。

  1. 您調用LoadLibrary來加載您的 DLL,該 DLL 占引用之一。
  2. 您調用DllImport p/invoke 函數,這又導致調用LoadLibrary 這是對圖書館的另一個參考。
  3. 然后當你調用FreeLibrary ,這會成功兩次,因為有兩次調用LoadLibrary

但是現在您遇到了麻煩,因為您已經在 p/invoke 系統的背后,並且它仍然認為它擁有對 DLL 的引用之一。 您在第二次調用FreeLibrary從中竊取的參考。

我想您缺少的信息是DllImport p/ DllImport如何綁定到函數。 您希望他們通過調用GetModuleHandle獲得模塊句柄。 他們沒有。 他們調用LoadLibrary 它們在第一次被調用時執行此操作,並且它們加載的模塊保持加載狀態,直到程序集本身卸載。

最重要的是,您必須遵守規則。 規則規定,對LoadLibrary每次調用都與對FreeLibrary的調用相匹配。 您調用LoadLibrary一次。 所以你也必須只調用一次FreeLibrary 停止調用它兩次,一切都很好。

我懷疑您實際上是在嘗試安排一個可以加載和卸載 DLL 的系統。 這可以防止您通過DllImport使用 p/invoke 。 您必須使用LoadLibraryGetProcAddress來完成這一切。

您對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 在第一次調用包裝的方法時加載庫。
- 如果一個庫已經加載到堆棧上,則對方法的調用將使用庫的這個實例而不是加載另一個

基於這些考慮,您可以通過以下方式管理您的模塊:

  1. 實現包含庫的類
    public class MyWrapper
    {
        int _libraryHandle = 0;

        [DllImport("mylibrary.dll")]
        public external static void MyMethod();
    }
  1. 在構造函數中使用 LoadLibrary 加載庫:
    public MyWrapper()
    {
        _libraryHandle = LoadLibrary("mylibrary.dll");
    }
  1. 在 dispose 方法中使用 FreeLibrary 卸載庫:
    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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM