非托管DLL无法在ASP.NET服务器上加载


68

此问题与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。

任何有关此问题的帮助将不胜感激!

Answers:


21

尝试将dll放在\ System32 \ Inetsrv目录中。这是Windows Server上IIS的工作目录。

如果这不起作用,请尝试将dll放在System32目录中,并将依赖项文件放在Inetsrv目录中。


3
这是一个无需污染system32文件夹的答案:stackoverflow.com/a/4598747/92756
Felix Alcala 2013年

9
相反,您可以通过在web.config上添加<hostingEnvironment shadowCopyBinAssemblies =“ false” />来禁用ShadowCopying,前提是您不修改实时应用程序中的二进制文件。
阿米特(Amit)2015年

我由于制作了一个DLL副本而把它放到System32中,从而使我很生气,因为我忘记了它,然后为我的项目加载了错误版本的Microsoft.Azure.Documents.ServiceInterop.dll,这导致了奇怪的本地queryRanges [ 0] .isMinInclusive尝试连接到数据库时出错。我的修复最后是确保我的本地IIS DefaultAppPool身份是我的本地用户。另请参见:github.com/Azure/azure-documentdb-dotnet/issues/267
Zachstronaut

您刚刚保存了我的一天,+ 1
YS

48

发生这种情况是因为托管dll将卷影复制到.NET Framework目录下的临时位置。有关详细信息,请参见http://msdn.microsoft.com/en-us/library/ms366723.aspx

不幸的是,非托管dll不会被复制,并且ASP.NET进程在需要加载它们时将无法找到它们。

一种简单的解决方案是将非托管dll放在系统路径中的目录中(在命令行中键入“ path”以查看计算机上的路径),以便ASP.NET进程可以找到它们。System32目录始终位于路径中,因此始终将非托管dll放置在该路径中,但是我建议向该路径添加其他文件夹,然后在该路径中添加dll以防止污染System32目录。这种方法的一大缺点是,您必须为应用程序的每个版本重命名非托管dll,并且您很快就会拥有自己的dll地狱。


4
这是比接受的答案更好的答案,因为它解释了为什么要这样做。
汤姆W

根据我的经验,当我创建一个新目录时,将其添加到PATH并将DLL放在其中,在本地运行时发现它很好。但是,在IIS上托管时不是这种情况-仅当我在/ Windows / System32中找到DLL时,才会找到该DLL。在某些情况下,IIS是否可能对路径使用其他变量?
杰克伍德(Jake Wood)

42

作为将dll放在路径中已经存在的文件夹(例如system32)中的一种替代方法,您可以使用以下代码在过程中更改路径值

System.Environment.SetEnvironmentVariable("Path", searchPath + ";" + oldPath)

然后,当LoadLibrary尝试查找非托管DLL时,它还将扫描searchPath。这可能比在System32或其他文件夹中弄乱更好。


4
仅适用于PInvoke。如果使用混合模式程序集,则在运行任何代码之前将DLL链接(并失败)。
Nick Whaley 2014年

11

除了对Matt的回答外,这对于64位服务器2003 / IIS 6终于对我有用:

  1. 确保您的dll / asp.net是相同的版本(32/64位)
  2. 将不受管的dll放在inetsrv目录中(请注意,即使创建了sys32 / inetsrv目录,在64位窗口中,该文件也位于syswow64下)
  3. 将托管的dll保留在/ bin中
  4. 确保两组dll都具有读取/执行权限

+1用于inetsrv在64位系统上的位置。感谢那。
克劳斯·纳吉

6

查看FileMonProcMon并筛选出麻烦的DLL的名称。这将显示您搜索DLL时要扫描的目录,以及您可能遇到的任何权限问题。


1
ProcMon在确定依赖关系以及某个DLL无法加载的内容时非常有用。
奥利弗(Oliver)

4

另一种选择是将本机DLL作为资源嵌入到托管DLL中。这在ASP.NET中更为复杂,因为它需要在运行时写入临时文件夹。 该技术在另一个SO答案中进行了解释


对于可能在PAAS主机(例如Azure网站)中运行或使用的应用程序或库,我不推荐这种方法。
yzorg 2015年

你为什么不推荐这个?我正在考虑将这种方法用于Azure Functions应用,该应用需要从托管程序集运行本机可执行文件。
斯捷潘·贝奈斯

当我编写时,Azure函数不存在。Azure Functions非常小而且有针对性,如果您可以使用它,那就去吧!
yzorg

2

总是值得在您的环境设置中检查路径变量。


8
@Ristogod:答案已经四岁了,您是否对我不满意,因为我没有跟踪确保链接处于活动状态?艰难的人群。
annakata 2012年

3
@annakata您应该从链接中提取重要信息,因此当它失败时,此答案仍然有效;)
Drax

@Drax-我认为重要的信息是检查您的路径变量
annakata

@annakata答案对我来说足够清楚,但可能不足以对所有感兴趣的用户做出自我解释:)
Drax

1

直接在XYZ.dll上的将其部署到的位置上运行DEPENDS。如果那没有发现任何遗漏,请使用平台SDK中的fuslogvw工具跟踪加载程序错误。同样,事件日志有时包含有关加载DLL失败的信息。


您可以链接到我能获得DEPENDS的地方吗?
James McMahon

1

我遇到了同样的问题。我尝试了上述所有选项,将其复制到system32,inetpub,设置路径环境等均无效。通过将非托管dll复制到Web应用程序或Web服务的bin目录中,最终解决了该问题。


1

Аfter整天为这个问题苦苦挣扎,最后我找到了适合我的解决方案。这只是一个测试,但是该方法有效。

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();
                  }
                }
          }
        }
    }
}

0

在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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.