ASP.Net 中的 DllImport 如何查找 DLL?

How does DllImport in ASP.Net look for the DLL?

是否在某处记录了如何 ASP.Net 为本机 DLL 设置搜索路径?我需要能够在我自己的代码中复制逻辑。

有关更多背景信息:我正在维护一个托管库(比如 Managed.DLL),它包装了一个本机库(比如 Native.DLL),后者又使用另一个本机 DLL(比如 Driver.DLL).到目前为止 Managed.DLL 一直在使用 .Net 的 DllImport 属性从 Native.DLL 导入函数,但现在我必须将其更改为对 LoadLibrary 和 GetProcAddress 的手动编码调用以获得更多控制;特别是,我需要能够调用 FreeLibrary 来卸载 Native.DLL,而当 Native.DLL 已通过 DllImport 加载时我无法执行此操作。

问题来了:虽然仅 DllImport("Native.DLL") 足以定位 Native.DLL 和 Driver.DLL,但调用 LoadLibrary("Native.DLL") 失败并显示 ERROR_FILE_NOT_FOUND 当 Managed.DLL 用于 ASP.Net 应用程序时,因为包含 Managed.DLL 的目录不在本机代码 DLL 的搜索路径上。

我的第一个想法是使用 Assembly.GetExecutingAssembly().Location,然后使用完整路径发出 LoadLibrary 调用,但是 Native.DLL 找不到 Driver.DLL,因为目录包含它们两者仍然不在搜索路径上。

我可以通过使用 Assembly.GetExecutingAssembly().Location 值通过 SetDllDirectory 设置本机 DLL 搜索路径来解决这个问题,但这有两个主要缺点:

1) SetDllDirectory 更改了全局 WinAPI 设置,可能会干扰同一 ASP.Net 工作进程中的其他代码,该工作进程也使用本机代码 DLL;我还验证了使用 DllImport 属性不会干扰此设置,因此现在更改它确实会破坏之前有效的功能。

2) 它仍然无法在 Visual Studio 中调试 ASP.Net 应用程序,因为 VS 将托管资源复制到临时目录中,但将本机 DLL 留在项目构建目录中,因此它们结束up 在调试会话的不同位置(并且每个调试会话都会擦除临时目录,因此手动将本机 DLL 复制到其中也不起作用;我必须将本机 DLL 复制到 IIS 的目录中,以便调试会话找到它们这显然不是可接受的解决方案)。

我真的很想在这里做兼容的事情,但到目前为止还没有找到这是什么,经过几天的无果搜索,任何指点将不胜感激。

回答我自己的问题:

1) 关于复制 Managed.DLL 我遗漏的关键字是 "shadow copying" (James Schubert explains it much better than any official Microsoft documentation I've seen) and the trick is to use Assembly.CodeBase instead of Assembly.Location, because the former gives the the original location of Managed.DLL and the latter the location of the shadow copy (John Sibly and Sneal 分享了很好的代码片段以从 [=12= 中的 URI 中提取目录名称]).

2) 使本机 DLL 的依赖项可用的方法是在需要它们之前使用 LoadLibrary 显式加载它们(并且由于这会增加它们的引用计数,还可以使用 FreeLibrary 释放它们完成后)。

所以,加载顺序是

string dir = Assembly.GetExecutingAssembly().CodeBase;
dir = new Uri(dir).LocalPath;
dir = Path.GetDirectoryName(dir);
IntPtr driver = LoadLibrary(dir + Path.DirectorySeparatorChar + "Driver.DLL");
IntPtr managed = LoadLibrary(dir + Path.DirectorySeparatorChar + "Managed.DLL");

和卸载顺序

FreeLibrary(managed);
FreeLibrary(driver);

(另请注意 LoadLibraryFreeLibrary 调用的顺序)。