Search code examples
c#c++dllpinvokeloadlibrary

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


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

Solution

  • 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.