非托管 DLL 无法在 ASP.NET 服务器上加载
Posted
技术标签:
【中文标题】非托管 DLL 无法在 ASP.NET 服务器上加载【英文标题】:Unmanaged DLLs fail to load on ASP.NET server 【发布时间】:2010-09-25 13:38:29 【问题描述】:这个问题与一个 ASP.NET 网站有关,最初是在 VS 2005 中开发的,现在在 VS 2008 中开发。
本网站使用两个非托管外部 DLL,它们不是 .NET,我没有编译它们的源代码,必须按原样使用它们。
该网站在 Visual Studio 中运行良好,可以正确定位和访问这些外部 DLL。但是,当网站发布在网络服务器(运行 IIS6 和 ASP.NET 2.0)而不是开发 PC 上时,它无法定位和访问这些外部 DLL,并且我收到以下错误:
Unable to load DLL 'XYZ.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
外部 DLL 以及包装它们的托管 DLL 以及网站的所有其他 DLL 都位于网站的 bin 目录中。
搜索这个问题发现许多其他人似乎在从 ASP.NET 网站访问外部非 .NET DLL 时遇到同样的问题,但我还没有找到可行的解决方案。
我尝试了以下方法:
运行 DEPENDS 以检查依赖关系以确定前三个 都是在System32目录中的路径,最后是在.NET 2 框架。 我将这两个 DLL 及其依赖项放入 System32并重新启动服务器,但网站仍然存在 无法加载这些外部 DLL。 授予 ASPNET、IIS_WPG 和 IUSR(针对该服务器)的全部权限 网站bin目录并重新启动,但网站仍然无法 加载这些外部 DLL。 将外部 DLL 作为现有项目添加到项目并设置 他们的“复制到输出”属性为“始终复制”,以及网站 仍然找不到 DLL。 还将他们的“构建操作”属性设置为“嵌入式资源”和 网站仍然找不到 DLL。任何有关此问题的帮助将不胜感激!
【问题讨论】:
【参考方案1】:在您已将其部署到的位置直接运行 DEPENDS on XYZ.dll。如果这没有显示任何缺失,请使用平台 SDK 中的 fuslogvw 工具来跟踪加载程序错误。此外,事件日志有时包含有关加载 DLL 失败的信息。
【讨论】:
你能链接到我可以得到 DEPENDS 的地方吗?【参考方案2】:尝试将 dll 放在 \System32\Inetsrv 目录中。这是 Windows Server 上 IIS 的工作目录。
如果这不起作用,请尝试将 dll 放在 System32 目录中,并将依赖文件放在 Inetsrv 目录中。
【讨论】:
这是一个无需污染system32文件夹的答案:***.com/a/4598747/92756 相反,您可以通过在 web.config 上添加查看FileMon 或ProcMon 并过滤有问题的DLL 的名称。这将向您显示在搜索 DLL 时扫描了哪些目录,以及您可能遇到的任何权限问题。
【讨论】:
ProcMon 在确定依赖关系以及某个 DLL 不会加载的内容方面非常有用。【参考方案4】:在您的环境设置中也总是值得 checking the path 变量。
【讨论】:
@Ristogod:答案是四岁,你对我投了反对票,因为我没有跟踪以确保链接处于活动状态?艰难的人群。 @annakata 你应该从链接中提取重要信息,所以当链接失败时,这个答案仍然有效;) @Drax - 我认为重要的信息是检查你的路径变量 @annakata 答案对我来说已经足够清楚了,但对于所有感兴趣的用户来说可能不是那么容易解释:)【参考方案5】:除了马特的回答之外,这对我来说最终适用于 64 位服务器 2003 / IIS 6:
-
确保您的 dll / asp.net 版本相同(32 / 64 位)
将非托管 dll 放入 inetsrv 目录(请注意,在 64 位 Windows 中,这是在 syswow64 下,即使已创建 sys32/inetsrv 目录)
将托管 dll 留在 /bin 中
确保两组 dll 都具有读取/执行权限
【讨论】:
+1 表示 inetsrv 在 64 位系统上的位置。谢谢你。【参考方案6】:发生这种情况是因为托管 dll 将影子复制到 .NET Framework 目录下的临时位置。详情请见http://msdn.microsoft.com/en-us/library/ms366723.aspx。
不幸的是,非托管 dll 不会被复制,并且 ASP.NET 进程在需要加载它们时将无法找到它们。
一个简单的解决方案是将非托管 dll 放在系统路径中的目录中(在命令行中键入“path”以查看计算机上的路径),以便 ASP.NET 进程可以找到它们. System32 目录 always 在路径中,因此将非托管 dll 放在那里总是可以的,但我建议在路径中添加一些其他文件夹,然后在其中添加 dll 以防止污染 System32 目录。此方法的一大缺点是您必须为应用程序的每个版本重命名非托管 dll,并且您可以快速拥有自己的 dll 地狱。
【讨论】:
这是一个比接受的答案更好的答案,因为它解释了您为什么要这样做。 根据我的经验,当我创建一个新目录时,将其添加到PATH并将DLL放在那里,在本地运行时发现它很好。但是,托管在 IIS 上时情况并非如此——只有当我在 /Windows/System32 中找到 DLL 时才能找到该 DLL。在某些情况下,IIS 是否可能对路径使用不同的变量?【参考方案7】:作为将 dll 放入路径中已经存在的文件夹(如 system32)的替代方法,您可以使用以下代码更改进程中的路径值
System.Environment.SetEnvironmentVariable("Path", searchPath + ";" + oldPath)
然后,当 LoadLibrary 尝试查找非托管 DLL 时,它也会扫描 searchPath。这可能比在 System32 或其他文件夹中弄得一团糟。
【讨论】:
仅适用于 PInvoke。如果使用混合模式程序集,则 DLL 会在任何代码运行之前链接(并失败)。【参考方案8】:另一种选择是将本机 DLL 作为资源嵌入到托管 DLL 中。这在 ASP.NET 中更为复杂,因为它需要在运行时写入临时文件夹。 The technique is explained in another SO answer.
【讨论】:
对于可能在 PAAS 主机(如 Azure 网站)中运行或使用的应用程序或库,我不推荐这种方法。 你为什么不推荐这个?我正在考虑将此方法用于需要从托管程序集运行本机可执行文件的 Azure Functions 应用程序。 Azure 函数在我编写时并不存在。 Azure Functions 是如此的小而有针对性,如果你让它工作,那就去吧!【参考方案9】:我遇到了同样的问题。我尝试了上述所有选项,复制到 system32、inetpub、设置路径环境等都没有奏效。 这个问题最终通过将非托管 dll 复制到 web 应用程序或 web 服务的 bin 目录来解决。
【讨论】:
【参考方案10】:А在为这个问题苦苦挣扎了一整天后,我终于找到了适合我的解决方案。这只是一个测试,但方法是有效的。
namespace TestDetNet
static class NativeMethods
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
public partial class _Default : System.Web.UI.Page
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int GetRandom();
protected System.Web.UI.WebControls.Label Label1;
protected void Page_Load(object sender, EventArgs e)
Label1.Text = "Hell'ou";
Label1.Font.Italic = true;
protected void Button1_Click(object sender, EventArgs e)
if (File.Exists(System.Web.HttpContext.Current.Server.MapPath("html/bin")+"\\DelphiLibrary.dll"))
IntPtr pDll = NativeMethods.LoadLibrary(System.Web.HttpContext.Current.Server.MapPath("html/bin")+"\\DelphiLibrary.dll");
if (pDll == IntPtr.Zero) Label1.Text = "pDll is zero";
else
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "GetRandom");
if (pAddressOfFunctionToCall == IntPtr.Zero) Label1.Text += "IntPtr is zero";
else
GetRandom _getRandom = (GetRandom)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall,typeof(GetRandom));
int theResult = _getRandom();
bool result = NativeMethods.FreeLibrary(pDll);
Label1.Text = theResult.ToString();
【讨论】:
【参考方案11】:在 Application_start 上使用这个: (根据需要自定义 /bin/x64 和 bin/dll/x64 文件夹)
String _path = String.Concat(System.Environment.GetEnvironmentVariable("PATH")
,";"
, System.Web.Hosting.HostingEnvironment.MapPath("~/bin/x64")
,";"
, System.Web.Hosting.HostingEnvironment.MapPath("~/bin/dll/x64")
,";"
);
System.Environment.SetEnvironmentVariable("PATH", _path, EnvironmentVariableTarget.Process);
【讨论】:
以上是关于非托管 DLL 无法在 ASP.NET 服务器上加载的主要内容,如果未能解决你的问题,请参考以下文章
DLL 无法访问 IIS 内部用于 ASP.NET 应用程序的数据库
C# 加载非托管 DLL:IIS 上的控制台应用程序和 webapp 之间的巨大性能差异
无法在 ASP.NET vNext 中启动我的静态文件托管服务器