【问题标题】:Python script called from ASP.NET runs fine in IIS Express but fails in local IIS从 ASP.NET 调用的 Python 脚本在 IIS Express 中运行良好,但在本地 IIS 中失败
【发布时间】:2016-05-18 16:03:21
【问题描述】:

这可能表面上看起来像 Python ArcGIS ArcPy RuntimeError: NotInitialized 的副本,但这是不同的,因为

  • 使用ArcGIS 10.0 以后有很大区别 我正在使用的 10.3 等版本
  • 指的是安装了多个 Python 版本,而我只 有一个
  • 指的是我已经完成的卸载和重新安装
  • 指的是不同的操作系统(我在 Win 2012 上运行)
  • 指的是在我得到 仅来自 IIS 的错误

    我有一个调用 Python 脚本的 ASP.NET 应用程序。该代码使用 System.Diagnostics.Process 对象调用 Python.exe 并向其传递参数,例如 Python 脚本的位置和其他参数。 该 Process 对象在 C# 中如下所示

        Process proc = new Process();
    proc.StartInfo.Verb = "runas";     
    proc.StartInfo.FileName = pathToPythonExe;
    proc.StartInfo.Arguments = procArgs;
    proc.StartInfo.RedirectStandardError = true;
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.CreateNoWindow = true;
    proc.StartInfo.UseShellExecute = false;
    proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    proc.Start();
    proc.WaitForExit();            
    errorToConsole = proc.StandardError.ReadToEnd();
    proc.WaitForExit();
    messageToConsole = proc.StandardOutput.ReadToEnd();
    proc.WaitForExit();
    

    Path 和 PYTHONPATH 环境变量指向 Python 可执行文件所在的确切位置。

    当我在 Visual Studio 2015 的 IIS Express 中从 ASP.NET C# 应用程序运行 Python 脚本时,一切运行正常。当我通过命令控制台运行 Python 脚本时,一切运行良好。当我从 IDLE 运行 Python 脚本时,一切运行良好。但是,当我将应用程序发布到 IIS 8.5 并运行它时,Python 脚本中出现错误。此外,当我从 Visual Studio 运行应用程序并使用本地 IIS 而不是 IIS Express 时,Python 脚本再次失败。

所以,这里是它起作用的条件的回顾:

  1. 在 IIS Express 中从 ASP.NET C# 应用程序运行 Python 脚本 Visual Studio 2015。
  2. 通过命令控制台运行 Python 脚本。
  3. 从 IDLE 运行 Python 脚本。

以下是它不起作用的条件的回顾:

  1. 在本地 IIS Express 中从 ASP.NET C# 应用程序运行 Python 脚本 在 Visual Studio 2015 中。
  2. 从 IIS 上的 ASP.NET C# 应用程序运行 Python 脚本。

错误的要点是脚本行上的“RuntimeError: NotInitialized”,“import acrpy”。这在下面的“错误 #1”下列出。我看到在 VS(本地 IIS 或 IIS Express)与常规 IIS 中运行它的唯一区别是权限。

当我在 Visual Studio 中运行它时,Visual Studio 应用程序具有管理员权限。 在 IIS 中,身份验证设置为启用 Windows 身份验证。其他一切都被禁用。 以下是应用在 IIS 8.5 中运行以及在带有本地 IIS 的 Visual Studio 中运行时出现的完整错误消息。

错误 #1:

Traceback (most recent call last):
  File "E:\Application Development\PublishServiceDefinition\MapPublisher\MapSdDraftCreator.py", line 1, in <module>
    import arcpy
  File "E:\Program Files (x86)\ArcGIS\Desktop10.3\ArcPy\arcpy\__init__.py", line 21, in <module>
    from arcpy.geoprocessing import gp
  File "E:\Program Files (x86)\ArcGIS\Desktop10.3\ArcPy\arcpy\geoprocessing\__init__.py", line 14, in <module>
    from _base import *
  File "E:\Program Files (x86)\ArcGIS\Desktop10.3\ArcPy\arcpy\geoprocessing\_base.py", line 598, in <module>
    env = GPEnvironments(gp)
  File "E:\Program Files (x86)\ArcGIS\Desktop10.3\ArcPy\arcpy\geoprocessing\_base.py", line 595, in GPEnvironments
    return GPEnvironment(geoprocessor)
  File "E:\Program Files (x86)\ArcGIS\Desktop10.3\ArcPy\arcpy\geoprocessing\_base.py", line 551, in __init__
    self._refresh()
  File "E:\Program Files (x86)\ArcGIS\Desktop10.3\ArcPy\arcpy\geoprocessing\_base.py", line 553, in _refresh
    envset = (set(env for env in self._gp.listEnvironments()))
RuntimeError: NotInitialized

这是我迄今为止为解决问题所做的工作。

第一次故障排除尝试: 我完全卸载并重新安装了 Desktop 10.3(包括 Python 文件和 arcpy 文件)。那没有帮助。我遇到了同样的错误。

第二次故障排除尝试: 我在另一台 Windows 2012 服务器上安装了 Desktop 10.3(和 Python.exe),并在该机器上设置了 IIS(第二台机器上没有 Visual Studio)。发生了同样的错误。在第二台机器上安装之前,我确保所有 ESRI 产品都已被删除(包括从注册表中删除)。

第三次故障排除尝试: 我在第一行添加了“import arcinfo”,使得 Python 脚本的前两行如下所示:

import arcinfo
import arcpy

这导致了以下错误:

错误 #2:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "e:\program files (x86)\arcgis\desktop10.3\arcpy\arcpy\__init__.py", line 21, in <module>
    from arcpy.geoprocessing import gp
  File "e:\program files (x86)\arcgis\desktop10.3\arcpy\arcpy\geoprocessing\__init__.py", line 14, in <module>
    from _base import *
  File "e:\program files (x86)\arcgis\desktop10.3\arcpy\arcpy\geoprocessing\_base.py", line 598, in <module>
    env = GPEnvironments(gp)
  File "e:\program files (x86)\arcgis\desktop10.3\arcpy\arcpy\geoprocessing\_base.py", line 595, in GPEnvironments
    return GPEnvironment(geoprocessor)
  File "e:\program files (x86)\arcgis\desktop10.3\arcpy\arcpy\geoprocessing\_base.py", line 551, in __init__
    self._refresh()
  File "e:\program files (x86)\arcgis\desktop10.3\arcpy\arcpy\geoprocessing\_base.py", line 553, in _refresh
    envset = (set(env for env in self._gp.listEnvironments()))
RuntimeError: NotInitialized

Traceback (most recent call last):
  File "E:\Application Development\PublishServiceDefinition\MapPublisher\MapSdDraftCreator.py", line 1, in <module>
    import arcinfo
  File "E:\Program Files (x86)\ArcGIS\Desktop10.3\ArcPy\arcinfo.py", line 18, in <module>
    gp.setProduct("ArcInfo")
RuntimeError: ERROR 999999: Error executing function.

第四次故障排除尝试: 由于 Windows 身份验证使用本地 IIS_IUSRS 帐户,因此我完全控制该帐户以 • 包含 Python.exe 文件的文件夹, • ArcGIS\Desktop10.3 文件夹 • 包含 Python 脚本的文件夹 • Python 脚本访问的任何文件夹

我从 Python 脚本中删除了“import arcinfo”。结果和我第一次从 IIS 尝试的结果一样。

第 5 次故障排除尝试: 由于该错误似乎引用了环境变量,我认为本地 IIS_IUSRS 组可能出于安全原因不允许其进程中的任何线程访问环境变量。那只是一个猜测。在我看来,微软可能会做一些事情。 因此,我在 IIS 中获取了应用程序来模拟具有本地管理员权限的帐户。当我再次运行 ASP.NET 应用程序时,它似乎甚至没有尝试读取 Python 脚本。包装 Python 脚本的 C# 进程捕获 StandardError 和 StandardOutput。使用模拟时没有捕获任何内容。此外,调试器停留在 Start() 和 WaitForExit() 方法上的时间可以忽略不计,就好像 Process 对象中的执行线程甚至没有尝试读取 Python 脚本一样。 如果这是文件夹安全权限的问题,那么当 IIS_IUSRS 没有足够的权限时,模拟帐户就会出现错误。但是,我没有收到任何错误。看起来使用模拟帐户运行的进程甚至没有尝试做任何事情。

第 6 次故障排除尝试: 由于已安装的 Python.exe 是 32 位版本的 Python,因此我前往 ASP.NET 应用程序使用的应用程序池,并确保在高级设置中启用了 32 位应用程序设置。这不会改变打开或关闭 IIS 模拟的结果。

我看到的唯一其他奇怪的事情是,Visual Studio 中的 ASP.NET 项目属性显示它针对 .NET Framework 4.5,而应用程序池设置为版本 IIS 管理器显示应用程序池设置为.NET CLR 4.0 版。当我尝试在 IIS 管理器中更改 .NET Framework 版本时,它只提供了 v4.03 和 v2.05 两种选择。我不知道这有多重要。

编辑(2016 年 5 月 18 日下午 4:06): 我又做了一些测试。我查看了三种不同条件下的一些环境变量。我使用以下三个设置在 Visual Studio 中运行 ASP.NET 应用程序:

  1. 本地 IIS,已禁用模拟
  2. 本地 IIS,启用模拟,身份 = x12345
  3. IIS Express

以下是这些测试的一些结果。

本地 IIS,已禁用模拟:

Environment.UserDomainName = "IIS APPPOOL"
Environment.UserName = .NET v4.5

本地 IIS,启用模拟,身份 = x12345:

Environment.UserDomainName = x12345
Environment.UserName = ABC

IIS Express:

Environment.UserDomainName = x12345
Environment.UserName = ABC

请注意,在启用模拟的情况下运行与使用 IIS Express 运行的相同身份会导致相同的环境变量。 这告诉我,也许我应该专注于在启用 ASP.NET 模拟的同时使其工作,因为正如我之前提到的,使用 IIS Express 运行始终是成功的。尝试尽可能地模仿那些已知的成功条件似乎对我有益。 对我来说,将精力集中在尝试找出与 IIS Express 相同的环境用户模拟 ASP.NET 无法运行脚本的原因似乎是合理的。评论和建议将不胜感激。

编辑(2016 年 5 月 19 日下午 1:40):

我尝试在“IIS Admin Service”属性和 W3SVC 属性中启用“允许服务与桌面交互”选项。这没有帮助。

我的下一个尝试是创建一个调用 Python 可执行文件并返回结果的 Web 服务。我将让当前的 ASP.NET 应用程序调用 Web 服务,然后返回结果。 除非有人知道为什么这不起作用,或者知道如何让当前的 ASP.NET 应用程序按需要运行,否则我将继续使用 Web 服务策略。

编辑(2016 年 5 月 25 日)

经过将近 3 周的工作,我终于可以完成这项工作了。

虽然我没有确切知道我做了什么来让它工作,但这里遵循我所做的两个主要事情。

我使用托管在 IIS 中的 basicHttpBinding 创建了一个 WCF 服务。 wsHttpBinding 对我不起作用,因为当我尝试为 Windows 身份验证配置它并设置 mode="transport" 时,我收到一条错误消息,指出需要 ssl。

更重要的似乎是我将 WCF 服务的应用程序池的进程标识从 ApplicationPoolIdentity 更改为具有提升权限的帐户。

现在,当我在任务管理器中查看 w3wp.exe 用户名时,我发现它正在使用该帐户的名称运行。

我的下一步是开始按顺序撤消所有以前的配置并测试哪些配置是不必要的。例如,我在包含使用的 Python 脚本和可执行文件的文件夹的安全访问中添加了应用程序池帐户。 现在我认为这可能是不必要的,因为 w3wp 可以通过其新帐户访问 Python 脚本。我将开始删除我之前添加的其他帐户。

事后看来,我认为创建 Web 服务在技术上可能是不必要的,因为我可能刚刚能够更改运行 ASP.NET 应用程序的应用程序池的进程标识。但是,这可能存在安全问题,因为用户可以直接访问 ASP.NET 应用程序,并且 Web 服务只能间接访问,即使此应用程序是 Intranet 应用程序并且没有面向外部的接口。

任何关于我如何让它发挥作用的见解(建议和/或建设性批评)将不胜感激。

【问题讨论】:

  • 在 IIS Express 上进行调试时,与开发桌面应用程序几乎没有区别。但这只是错误的路径,因为 IIS 运行方式不同。除非 Python 的东西可以通过其供应商明确地与 IIS 一起使用,否则您将不得不破解源代码。
  • ASP.NET 中从 IIS Express 调用 Python 的 System.Diagnostics.Process 对象与从本地 IIS 调用 Python 的 ASP.NET 中的 System.Diagnostics.Process 对象相同。更改供应商源代码是一个红鲱鱼。需要做的是改变本地 IIS 的配置、安全访问、帐户权限和环境变量,以充分匹配它在其他条件下的工作方式。问题是如何以及具体需要以不同方式进行配置。
  • 存在会话等根本区别。因此,您作为该产品的用户而非开发人员的所有尝试很可能会失败,因为您不知道实际上需要哪些资源以及这些差异如何影响您。我不会在这些事情上浪费时间。
  • 如果我创建一个 Web 服务来运行 Python 脚本并从 ASP.NET 调用该 Web 服务会怎样?
  • 如果你确定 Python 代码是这样工作的,那么我想你可以试试。

标签: c# python asp.net iis arcgis


【解决方案1】:

我对此进行了更多研究,现在我明白了我的解决方案为何有效。此信息回答了我最初的问题。以下是我的研究发现的有关该主题的有用和相关信息。

  • 信息 #1

在计算机上安装 ArcGIS Desktop 10.3 后,它会创建一个目录来存储重要信息。该目录的默认位置取决于启动 ArcGIS Desktop 应用程序安装的用户的登录帐户名称。例如,如果登录帐户是 MS\AHejlsberg,则默认安装的 UNC 路径将为
C:\Users\AHejlsberg.MS\AppData\Roaming\ESRI\Desktop10.3\
该目录将包含基本文件,例如默认位于此处的 .sde 数据库连接,
C:\Users\AHejlsberg.MS\AppData\Roaming\ESRI\Desktop10.3\ArcCatalog\

  • 信息 #2

当调用 .NET System.Diagnostics.Process 对象的 Start() 方法时,Process 对象将使用与运行 ASP.NET 应用程序的应用程序池中标识的 w3wp.exe 工作进程相同的帐户运行。 因此,如果 ASP.NET 应用程序在 DefaultAppPool 下运行,并且该应用程序池具有标识 ApplicationPoolIdentity,则 Windows 的本地安全机构将使用 DefaultAppPool 的用户 SID 和组 SID 创建一个新的虚拟帐户。 IIS_IUSRS 安全主体在其访问令牌中。
新的 .NET Process 对象将在该新虚拟帐户的安全上下文中运行。因此,如果新的 Process 对象包装了 Python.exe 文件,则该可执行文件将像由应用程序池标识中标识的同一帐户调用一样运行。
再举一个例子,如果 ASP.NET 应用程序在某个应用程序池下运行,该应用程序池将身份设置为某个特定用户(例如域帐户),那么从 ASP.NET 应用程序生成的 .NET Process 对象将在安全上下文下运行该用户的。在这种情况下,Python.exe 将像由特定用户调用一样运行,并设置为应用程序池的标识。
即使 ASP.NET 应用程序启用了模拟,这样经过身份验证的 HTTP 上下文标识和返回的 WindowsIdentity.GetUser() 标识恰好相同,新的 .NET Process 对象仍不会在该 ASP.NET 应用程序标识下运行如果应用程序池的标识不同。

注意:程序员可能会过分假设,如果启用 ASP.NET 模拟以作为某个帐户运行并且应用程序池的标识设置为同一帐户,那么 ASP.NET 应用程序和新生成的 . NET Process 对象将以完全相同的安全上下文运行,实际上,ASP.NET 应用程序进程中尝试访问安全对象的线程将具有模拟令牌,而代表新访问安全对象的线程生成的 .NET Process 对象将只有一个主访问令牌。

  • 信息 #3

如果 Python 运行时需要与随 ArcGIS Desktop 一起安装的 ESRI ArcPy 一起使用,则运行时需要能够访问我在上面的信息 #1 部分中提到的 AppData\Roaming\ESRI 子文件夹。当我的 Python 脚本调用“import ArcPy”命令时,情况就是如此。
Windows 中的帐户具有与其关联的环境变量列表。从该帐户创建的任何进程都将引用该环境变量列表。
这些变量之一将被称为“AppData”。
Python 运行时将在与触发 Python 可执行文件的帐户的身份关联的环境变量列表中查找 AppData 变量。
该 AppData 变量需要对 ArcGIS Destkop 安装其 ESRI 子文件夹的位置进行赋值。将 AppData 变量赋值为所需 UNC 路径的一种方法是运行触发 Python 可执行文件的进程,其身份与用于安装 ArcGIS Desktop 的帐户相同。
因此,例如,如果 ArcGIS Desktop 是使用名为 MS\AHejlsberg 的帐户安装的,那么从 ASP.NET 生成的将调用 Python 可执行文件的 .NET 进程也需要以 MS\AHejlsberg 帐户的身份运行.将承载 ASP.NET 应用程序池的应用程序池的标识设置为该帐户将实现让 ASP.NET 应用程序使用该所需帐户创建 .NET Process 对象的目标。

人们可以像这样检索运行 ASP.NET 应用程序的身份的环境变量列表:

var envVars = Environment.GetEnvironmentVariables();

如果为 MS\AHejlsberg 启用了 ASP.NET 模拟,则 AppData 变量将如下所示:

// ["APPDATA"] = "C:\\Users\\AHejlsberg.MS\\AppData\\Roaming"

一个人可以检索环境变量列表来识别从 ASP.NET 应用程序生成的进程的身份,如下所示:

ProcessStartInfo procInfo = proc.StartInfo;

如果托管该 ASP.NET 应用程序的应用程序池将其标识设置为 ApplicationPoolIdentity,并且应用程序池的名称恰好是“.NET v4.5”,那么 AppData 将如下所示:

// Environment = {System.Collections.Specialized.StringDictionary.GenericAdapter}
    // Count = 43        
        // [19] = {[APPDATA, C:\Windows\system32\config\systemprofile\AppData\Roaming]}

另一方面,如果托管该 ASP.NET 应用程序的应用程序池将其标识设置为 MS\AHejlsberg,则 AppData 将如下所示:

// Environment = {System.Collections.Specialized.StringDictionary.GenericAdapter}
    // Count = 49     
        // [21] = {[APPDATA, C:\Users\AHejlsberg.MS\AppData\Roaming]}

现在,Python 运行时将能够定位 ArcGIS Desktop 安装的子文件夹。

如果程序员无法将应用程序池的身份设置为特定用户帐户(如上图所示的域帐户),则另一种选择是将所有 ESRI 子文件夹复制到对应用程序池的标识,例如,
C:\Windows\system32\config\systemprofile\AppData\Roaming
如上所示,应用程序池的名称为“.NET 4.5”,标识为 ApplicationPoolIdentity。
我想这应该可行;但我没有测试过。可能还需要更改其他变量设置。

请注意我的错误回溯的倒数第二行:
envset = (set(env for env in self._gp.listEnvironments()))

我们可以看到 Python 运行时显然在尝试访问环境变量。

当我将应用程序池标识设置为 ApplicationPoolIdentity 时,从 ASP.NET 生成的 .NET Process 对象在 Windows 创建的虚拟帐户的标识下运行,因此 Python 运行时正在寻找错误的 AppData UNC 路径。这导致了我们在回溯的最后一行看到的 RunTime not initialized 错误。

当我将应用程序池的标识设置为与安装 ArcGIS Destkop 的同一帐户的相同标识时,从 ASP.NET 应用程序生成的 .NET Process 对象能够引用 AppData 变量来评估路径Python 运行时可以成功地用于“Import ArcPy”语句。

这解决了 Python 运行时在 IIS Express 上成功但在本地 IIS 上失败的谜团。

【讨论】:

  • 费了很大力气把所有的细节都记下来了。
  • 最后两段是解决我的 Python 问题的关键,因为我遇到了 IIS 无法找到 Python 模块的相同问题。出色的工作和文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-09
  • 2017-04-09
  • 2014-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多