非托管 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 上添加 来禁用 ShadowCopying,前提是您没有在实时应用程序中修改二进制文件。 复制我的 DLL 并将其放入 System32 中,因为我忘记了它而被烧毁了,然后我为我的加载了错误版本的 Microsoft.Azure.Documents.ServiceInterop.dll尝试连接到数据库时导致奇怪的本地 queryRanges[0].isMinInclusive 错误的项目。我的修复最终是确保我的本地 IIS DefaultAppPool 身份是我的本地用户。另见:github.com/Azure/azure-documentdb-dotnet/issues/267 你拯救了我的一天,+1【参考方案3】:

查看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 之间的巨大性能差异

.NET 无法在同一目录中找到非托管 DLL

无法在 ASP.NET vNext 中启动我的静态文件托管服务器

从 C# 调用 C++ dll。 “无法封送'返回值':托管/非托管类型组合无效。”

SMTP 服务器无法从托管 c#asp.net 发送电子邮件