具有以下用于加载和卸载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();
您的问题的更新提供了足够的信息来解释此行为。
LoadLibrary
加载占引用之一的DLL。DllImport
p /调用功能,进而调用LoadLibrary
。这是对该库的另一个引用。FreeLibrary
时,此操作成功两次,因为有两次呼叫LoadLibrary
。但是现在您遇到了麻烦,因为您已经落后于p / invoke系统的后台,它仍然认为它拥有对DLL的引用之一。您第二次调用FreeLibrary
时从中窃取的引用。
我想您所缺少的信息是DllImport
p /调用如何绑定到该函数。您希望他们可以通过调用GetModuleHandle
获得模块句柄。他们没有。他们称LoadLibrary
。他们是在第一次调用它们时执行此操作,并且加载的模块会保持加载状态,直到程序集本身卸载为止。
首先,您要做的就是遵守规则。规则指出,每次对LoadLibrary
的调用都应与对FreeLibrary
的调用相匹配。您只需拨打一次LoadLibrary
。因此,您也必须精确地调用一次FreeLibrary
。停止调用两次,一切都很好。
我怀疑您实际上是在尝试安排一个可以加载和卸载DLL的系统。这样可以防止您通过DllImport
使用p /调用。您必须使用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
已将您的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);
}
}
我知道这个问题已经完成,但是在解决类似问题时,我发现了一种使用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(第一个Google匹配)If the function succeeds, the return value is nonzero.
,您的卸载循环基本上说:“只要释放库起作用,就再次释放它,直到卸载失败为止。”