1. 程式人生 > 程式設計 >C#呼叫python.exe使用arcpy方式

C#呼叫python.exe使用arcpy方式

背景

環境:ArcGis10.2.2。C#開發程式一直以來以呼叫Desktop的python環境(32位)來做資料處理分析。但是資料量大時,出現了記憶體資源不夠的情況。因此決定換成使用64位python環境。

遇到問題

C#通過Process.Start()去呼叫64位python.exe,在Debug模式下毫無問題,但是直接執行exe就報錯Process finished with exit code -1073741819 (0xC0000005)。指向異常。

分析問題

後來發現是由於arcpy模組導致的,去掉這個模組的內容就能執行,import arcpy就執行不起來。既然使用arcpy做資料處理,如果連import arcpy都不行,那還做個屁啊。於是開始尋找程式Debug模式下和Run模式下的區別。

程式中使用ProcessStartInfo類啟動的python.exe的程序,那問題基本就出自這裡了。附上檢測程式碼:

var start = new ProcessStartInfo
{
 WorkingDirectory = Environment.CurrentDirectory,FileName = sInterpreterPath,UseShellExecute = false,ErrorDialog = true,CreateNoWindow = true,RedirectStandardOutput = true,RedirectStandardInput = true,Arguments = sParam
};
using (Process process = Process.Start(start))
{
 var a = start.Environment;
 var b = a.Keys.ToList();
 b.Sort();
 var sss = "";
 foreach (var it in b)
 {
 sss = $"{sss}\n{it}------->{a[it]}";
 }
 sss = sss.Trim();
 using (StreamReader reader = process.StandardOutput)
 {
 var sResult = "";
 while (!reader.EndOfStream)
 {
 sResult = $"{sResult} \n {reader.ReadLine()}";
 }
 sResult = sResult.Trim();
 MessageBox.Show(sResult);
 }
 MessageBox.Show("ExitCode is " + process.ExitCode);
}

於是就對比了Debug模式下與Run模式下的程序環境變數。

C#呼叫python.exe使用arcpy方式

明顯可見兩個環境的__COMPAT_LAYER值就是不一樣的。查了一下__COMPAT_LAYER是版本相容相關引數,由於我是32位程式呼叫64位python.exe,因此懷疑是這個引數導致的問題。RunAsAdmin是以管理員執行,而Installer的解釋是安裝工具。

解決問題

上面分析出可能是__COMPAT_LAYER值不同才導致的問題,那麼就能對症下藥了,現在把Run下的值也設定為RunAsAdmin。加上下例程式碼:

start.EnvironmentVariables["__COMPAT_LAYER"] = "RunAsAdmin";

start.Environment["__COMPAT_LAYER"] = "RunAsAdmin";

再次執行,居然成功了。

下面附上C#呼叫64為Python.exe處理指令碼程式碼:

/// <summary>
/// 執行Python指令碼
/// </summary>
/// <param name="sScriptPath">指令碼路徑</param>
/// <param name="lstParam">引數列表</param>
/// <returns>是否成功</returns>
public bool RunScript(string sScriptPath,List<string> lstParam)
{
 var bResult = false;
 try
 {
 if (!File.Exists(sScriptPath))
 throw new Exception($"檔案{sScriptPath}不存在!");
 var sInterpreterPath = @"E:\ArcGIS\Python27\ArcGISx6410.2\python.exe";
 var sParam = $"{sScriptPath}";
 if (null != lstParam && 0 < lstParam.Count)
 {
 var sArgument = "\"" + string.Join("\" \"",lstParam) + "\"";
 sParam = $"\"{sParam}\" {sArgument}";
 }
 var start = new ProcessStartInfo
 {
 WorkingDirectory = Environment.CurrentDirectory,Arguments = sParam
 };
 start.EnvironmentVariables["__COMPAT_LAYER"] = "RunAsAdmin";
 start.Environment["__COMPAT_LAYER"] = "RunAsAdmin";
 using (Process process = Process.Start(start))
 {
 using (StreamReader reader = process.StandardOutput)
 {
 var sResult = "";
 while (!reader.EndOfStream)
 {
  sResult = $"{sResult} \n {reader.ReadLine()}";
 }
 sResult = sResult.Trim();
 MessageBox.Show(sResult);
 }
 MessageBox.Show("ExitCode is " + process.ExitCode);
 }
 }
 catch (Exception ex)
 {
 MessageBox.Show(ex.ToString());
 }
 return bResult;
}

補充知識:C#從登錄檔中獲取ArcPy的python.exe安裝位置

為何要獲取該位置?

在C#中呼叫命令執行Python指令碼的時候,Python直譯器是必不可少的工具。ArcGIS 10.2.2安裝時預設安裝Python,但不同使用者可能將Python安裝到不同位置,比如,本人就將其安裝到D盤而非預設的C盤。

那麼,當我們的系統給其他使用者使用時,勢必需要找到Python直譯器即python.exe檔案位置,才能正常執行工具呼叫。

當然,你可以將檔案位置寫入到環境變數,這位就無需獲取全路徑了。本文不考慮此種情形。

如何獲取該位置?

對比多臺電腦發現,Python安裝後,會在登錄檔中位置“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components”下自動建立一個鍵“9A6767D28A88AEB44AD0AE3AA51002C0”。

該鍵下有一個值,對應的資料即為python.exe的完整路徑。我們只需要讀到這個資料,即可獲取python.exe位置。

C#呼叫python.exe使用arcpy方式

C#程式碼如下:

  /// <summary>
  /// Python.exe路徑在登錄檔中的安裝位置(安裝ArcGIS自帶Python環境時自動建立)
  /// </summary>
  private static readonly string RegistryPythonDefaultKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\";
  /// <summary>
  /// Python.exe路徑在登錄檔中的鍵名(安裝ArcGIS自帶Python環境時自動建立)
  /// </summary>
  private static readonly string RegistryPythonTargetKey = "9A6767D28A88AEB44AD0AE3AA51002C0";
  /// <summary>
  /// 獲取Python.exe安裝路徑
  /// </summary>
  /// <returns></returns>
  private static string GetPythonPath()
  {
   var sPythonPath = "";
   try
   {
    var registryKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine,Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); //判斷機器位數
    var targetSubKey = registryKey.OpenSubKey(Path.Combine(RegistryPythonDefaultKey,RegistryPythonTargetKey));
    var lstName = targetSubKey.GetValueNames();
    foreach (var sName in lstName)
    {
     var sValue = targetSubKey.GetValue(sName) + string.Empty;
     if (!sValue.EndsWith("python.exe",StringComparison.OrdinalIgnoreCase) || !File.Exists(sValue))
     {
      continue;
     }
     sPythonPath = sValue;
     break;
    }
   }
   catch (Exception ex)
   {
    SysConfig.Model.LogServices.WriteExceptionLog(ex,"GetPythonPath");
   }
   return sPythonPath;
  }

需要注意的地方?

開啟登錄檔的時候,需要判斷機器位數,32位與64位登錄檔位置有所差異,如下:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components

以上這篇C#呼叫python.exe使用arcpy方式就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。