必须执行FreeLibrary 2次,尽管我只执行过1次LoadLibrary。另外,在卸载DLL之后,尝试再次加载DLL时,会发生错误

问题描述 投票:-1回答:4

具有以下用于加载和卸载C ++ DLL的C#代码。

我只加载一次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); }
    }

[invoke方法:

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();
c# c++ dll pinvoke loadlibrary
4个回答
5
投票

您的问题的更新提供了足够的信息来解释此行为。

  1. 您调用LoadLibrary加载占引用之一的DLL。
  2. 您调用DllImport p /调用功能,进而调用LoadLibrary。这是对该库的另一个引用。
  3. 然后,当您呼叫FreeLibrary时,此操作成功两次,因为有两次呼叫LoadLibrary

但是现在您遇到了麻烦,因为您已经落后于p / invoke系统的后台,它仍然认为它拥有对DLL的引用之一。您第二次调用FreeLibrary时从中窃取的引用。

我想您所缺少的信息是DllImport p /调用如何绑定到该函数。您希望他们可以通过调用GetModuleHandle获得模块句柄。他们没有。他们称LoadLibrary。他们是在第一次调用它们时执行此操作,并且加载的模块会保持加载状态,直到程序集本身卸载为止。

首先,您要做的就是遵守规则。规则指出,每次对LoadLibrary的调用都应与对FreeLibrary的调用相匹配。您只需拨打一次LoadLibrary。因此,您也必须精确地调用一次FreeLibrary。停止调用两次,一切都很好。

我怀疑您实际上是在尝试安排一个可以加载和卸载DLL的系统。这样可以防止您通过DllImport使用p /调用。您必须使用LoadLibraryGetProcAddress完成所有操作。

您对SetDllDirectory的使用看起来有些混乱。您希望".\\DLL_Dependencies\\"与什么相对?提供完整路径。


0
投票

如果只调用一次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

已将您的invoke方法类更新为包括:

[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);
     }
}

0
投票

我知道这个问题已经完成,但是在解决类似问题时,我发现了一种使用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);
    }

通过这种方式,每次实例化包装器时,都会在堆栈上加载新出现的库,而不会发生任何异常。


-1
投票

根据https://msdn.microsoft.com/en-us/library/windows/desktop/ms683152%28v=vs.85%29.aspx(第一个Google匹配)If the function succeeds, the return value is nonzero.,您的卸载循环基本上说:“只要释放库起作用,就再次释放它,直到卸载失败为止。”

© www.soinside.com 2019 - 2024. All rights reserved.