Have to do FreeLibrary 2 times although I have don

2019-09-19 12:52发布

Have the following C# code for loading and unloading a C++ DLL.

I load DLL only once, but code has to unload DLL 2 times. Also after unloading DLL, when I load it again, and I call DLL's exported function, I get the following error message:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

DLL depends on other DLLs.

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

The invoke method:

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

EDIT

I forgot to include the following which call DLL exported functions, after loading DLL and before unloading DLL. It makes sense to think that something else is going on inside these exported functions which is causing strange behavior (i.e. loading 1 time, having to unload 2 times):

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

3条回答
唯我独甜
2楼-- · 2019-09-19 13:06

The update to your question provides enough information to explain the behaviour.

  1. You call LoadLibrary to load your DLL which accounts for one of the references.
  2. You call DllImport p/invoke functions which in turn lead to a call to LoadLibrary. That's the other reference to the library.
  3. Then when you call FreeLibrary, this succeeds twice since there were two calls to LoadLibrary.

But now you are in trouble because you've gone behind the back of the p/invoke system and it still believes that it owns one of the references to the DLL. A reference that you stole from it with your second call to FreeLibrary.

I guess that the information you are missing is how the DllImport p/invokes bind to the function. You are hoping that they will obtain a module handle by calling GetModuleHandle. They don't. They call LoadLibrary. They do this the first time they are called and the module they load stays loaded until the assembly itself unloads.

What you have to do, above all else, is follow the rules. The rules state that each call to LoadLibrary is to be matched by a call to FreeLibrary. You call LoadLibrary once. So you must call FreeLibrary exactly once also. Stop calling it twice and all is well.

I suspect that you are actually trying to arrange a system whereby you can load and unload DLLs. This prevents you from using p/invoke via DllImport. You'll have to do it all with LoadLibrary and GetProcAddress.

Your usage of SetDllDirectory looks somewhat messed up. What do you expect ".\\DLL_Dependencies\\" to be relative to? Supply a full path.

查看更多
放我归山
3楼-- · 2019-09-19 13:19

If you only call LoadLibrary once then you only need to call FreeLibrary once as well. Calling it a second time is moving into undefined behavior.

If you look at the MSDN docs https://msdn.microsoft.com/en-us/library/windows/desktop/ms683152%28v=vs.85%29.aspx the function returns a non zero value if it succeeds, or 0 if there is an error.

If you want to be sure that its unloaded then you can call GetModuleHandle on it after the call to FreeLibrary and it should return null. https://msdn.microsoft.com/en-us/library/windows/desktop/ms683199(v=vs.85).aspx

Updated your invoke method class to include:

[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);

Then change your button5 click handler to the following:

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);
     }
}
查看更多
仙女界的扛把子
4楼-- · 2019-09-19 13:20

According to https://msdn.microsoft.com/en-us/library/windows/desktop/ms683152%28v=vs.85%29.aspx (the first google hit) If the function succeeds, the return value is nonzero. Your unload loop basically says "as long as freeing the library worked, free it again, until the unload fails".

查看更多
登录 后发表回答