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);
(另请注意 LoadLibrary
和 FreeLibrary
调用的顺序)。
是否在某处记录了如何 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);
(另请注意 LoadLibrary
和 FreeLibrary
调用的顺序)。