如何在C#中卸载使用DllImport
加载的DLL?
答案 0 :(得分:22)
从[DllImport] pinvoke声明加载的进程中卸载非托管DLL的最可靠方法是再次通过对LoadLibrary()进行加载来加载它。这为DLL提供了可靠的DLL处理,即使DLL的模块名称不明确也能正常工作。它在运行时没有任何影响,除了Windows加载器将DLL的内部引用计数从1增加到2。
然后你可以将FreeLibrary()两次用来将引用计数减少到0,并将它从LoadLibrary()传递给IntPtr。这会卸载DLL,以及任何已加载的相关DLL。
请注意,当您尝试在DLL上再次任何导出函数时,您将会遇到非常严重的故障,任何时间后执行此操作。 pinvoke marshaller不知道DLL不再存在,并且会在它认为仍然有效的地址处调用该函数。如果幸运的话,哪些程序会使用AccessViolation异常对您的程序进行轰炸。或者运行一个完全随机的代码,如果你不是那么幸运,以前DLL占用的地址空间被另一个DLL重用。那么一切都会发生,没有一件好事。
答案 1 :(得分:10)
当你调用P / Invoke函数时,这应释放先前加载的模块。
[DllImport("kernel32", SetLastError=true)]
static extern bool FreeLibrary(IntPtr hModule);
public static void UnloadModule(string moduleName)
{
foreach(ProcessModule mod in Process.GetCurrentProcess().Modules)
{
if(mod.ModuleName == moduleName)
{
FreeLibrary(mod.BaseAddress);
}
}
}
答案 2 :(得分:3)
根据彼得斯的建议,这对我有用:
[DllImport("kernel32", SetLastError = true)]
private static extern bool FreeLibrary(IntPtr hModule);
public static void UnloadImportedDll(string DllPath)
{
foreach (System.Diagnostics.ProcessModule mod in System.Diagnostics.Process.GetCurrentProcess().Modules)
{
if (mod.FileName == DllPath)
{
FreeLibrary(mod.BaseAddress);
}
}
}
答案 3 :(得分:0)
该晚了,但是我已经做了一个工具来做到这一点。它应该在Unity中运行,但是我认为它可以被其他C#解决方案采用。在这里可用:https://github.com/mcpiroman/UnityNativeTool。
请注意,它本质上是一种hack(但是经常使用,请参阅Harmony),所以我不建议在生产代码中使用它。
答案 4 :(得分:-1)
由于我在浏览信息时遇到了这些信息,因此我想我会回过头来解决OSX IN UNITY上的Sixense SDK问题。您将在那里看到在OSX上动态加载/卸载dylib的实现:
答案 5 :(得分:-2)
如果您是函数式编程的粉丝,那么您可以使用LINQ来实现@IllidanS4所建议的内容:
[DllImport("kernel32", SetLastError=true)]
static extern bool FreeLibrary(IntPtr hModule);
public static void UnloadModule(string moduleName)
{
var loadedAssemblyModule =
Process.GetCurrentProcess().Modules.OfType<ProcessModule>()
.FirstOrDefault(x => x.ModuleName == moduleName);
if (loadedAssemblyModule != null)
FreeLibrary(loadedAssemblyModule.BaseAddress);
}