4

I'm doing a project that uses python as background script and C# as guy. My problem is that I can't figure out how to cause my GUI to automatically search for the pythonw.exe file in order to run my python scripts.

Currently I'm using this path:

ProcessStartInfo pythonInfo = new ProcessStartInfo(@"C:\\Users\\Omri\\AppData\\Local\\Programs\\Python\\Python35-32\\pythonw.exe");

but I want it to auto detect the path of pythonw.exe (I need to submit the project and it won't run on others computers unless they change the code itself)

Any suggestions may be helpful.

zx485
  • 28,498
  • 28
  • 50
  • 59
Omri
  • 63
  • 1
  • 4

6 Answers6

11

Inspired by @Shashi Bhushan's answer I made this function for getting the Python path reliably;

private static string GetPythonPath(string requiredVersion = "", string maxVersion = "")
{
    string[] possiblePythonLocations = new string[3] {
        @"HKLM\SOFTWARE\Python\PythonCore\",
        @"HKCU\SOFTWARE\Python\PythonCore\",
        @"HKLM\SOFTWARE\Wow6432Node\Python\PythonCore\"
    };

    //Version number, install path
    Dictionary<string, string> pythonLocations = new Dictionary<string, string>();

    foreach (string possibleLocation in possiblePythonLocations)
    {
        string regKey = possibleLocation.Substring(0, 4),
               actualPath = possibleLocation.Substring(5);
        RegistryKey theKey = regKey == "HKLM" ? Registry.LocalMachine : Registry.CurrentUser;
        RegistryKey theValue = theKey.OpenSubKey(actualPath);

        foreach (var v in theValue.GetSubKeyNames())
            if (theValue.OpenSubKey(v) is RegistryKey productKey)
                try
                {
                    string pythonExePath = productKey.OpenSubKey("InstallPath").GetValue("ExecutablePath").ToString();
                            
                    // Comment this in to get (Default) value instead
                    // string pythonExePath = productKey.OpenSubKey("InstallPath").GetValue("").ToString();
                            
                    if (pythonExePath != null && pythonExePath != "")
                    {
                        //Console.WriteLine("Got python version; " + v + " at path; " + pythonExePath);
                        pythonLocations.Add(v.ToString(), pythonExePath);
                    }
                }
                catch
                {
                    //Install path doesn't exist
                }
    }

    if (pythonLocations.Count > 0)
    {
        System.Version desiredVersion = new(requiredVersion == "" ? "0.0.1" : requiredVersion);
        System.Version maxPVersion = new(maxVersion == "" ? "999.999.999" : maxVersion);

        string highestVersion = "", highestVersionPath = "";

        foreach (KeyValuePair<string, string> pVersion in pythonLocations)
        {
            //TODO; if on 64-bit machine, prefer the 64 bit version over 32 and vice versa
            int index = pVersion.Key.IndexOf("-"); //For x-32 and x-64 in version numbers
            string formattedVersion = index > 0 ? pVersion.Key.Substring(0, index) : pVersion.Key;

            System.Version thisVersion = new System.Version(formattedVersion);
            int comparison = desiredVersion.CompareTo(thisVersion),
                maxComparison = maxPVersion.CompareTo(thisVersion);

            if (comparison <= 0)
            {
                //Version is greater or equal
                if (maxComparison >= 0)
                {
                    desiredVersion = thisVersion;

                    highestVersion = pVersion.Key;
                    highestVersionPath = pVersion.Value;
                }
                //else
                //    Console.WriteLine("Version is too high; " + maxComparison.ToString());
            }
            //else
            //    Console.WriteLine("Version (" + pVersion.Key + ") is not within the spectrum.");$
        }

        //Console.WriteLine(highestVersion);
        //Console.WriteLine(highestVersionPath);
        return highestVersionPath;
    }

    return "";
}
unknown6656
  • 2,765
  • 2
  • 36
  • 52
Albert MN.
  • 713
  • 1
  • 16
  • 33
  • FYI that code throws an exception for me "Microsoft.Win32.RegistryKey.OpenSubKey(...) returned null." at the line; string pythonExePath = productKey.OpenSubKey("InstallPath").GetValue("ExecutablePath").ToString(); – Doug Jun 28 '21 at 07:28
  • Please check your code - in my case it throws "Object reference not set to an instance of an object." while executing theValue.GetSubKeyNames() in the foreach loop – Matt Nov 30 '22 at 14:18
6

You can find python installation path by lookup following keys on windows machine.

HKLM\SOFTWARE\Python\PythonCore\versionnumber\InstallPath

HKCU\SOFTWARE\Python\PythonCore\versionnumber\InstallPath

for win64 bit machine

HKLM\SOFTWARE\Wow6432Node\Python\PythonCore\versionnumber\InstallPath

You can refer this post for how to read registry using C#

How to read value of a registry key c#

Community
  • 1
  • 1
Shashi Bhushan
  • 1,292
  • 11
  • 15
2

Find the environment variable name in Windows, for that assembly and use Environment.GetEnvironmentVariable(variableName)

Check out How to add to the pythonpath in windows 7?

Community
  • 1
  • 1
hyankov
  • 4,049
  • 1
  • 29
  • 46
  • How would I know?! It should be the same across all windows installations, if it is done with the same installer. – hyankov Jan 29 '17 at 11:01
  • Apparently not? I've checked. – hyankov Jan 29 '17 at 11:03
  • as far as i know- python is on path. cant understand what are u suggesting me. is there any explanation to var environment? var name? – Omri Jan 29 '17 at 11:14
  • And I can't understand your comment. You are asking how to get the python executable, without hardcoding it. What I am saying is - the path to that executable is usually stored in Windows as environmental variable. I've just given you the function which you can use to retrieve the value... but you have to look into your Windows ENV variables and see what the name for it is. – hyankov Jan 29 '17 at 11:46
2

An example on how to search for Python within the PATH environment variable:

var entries = Environment.GetEnvironmentVariable("path").Split(';');
string python_location = null;

foreach (string entry in entries)
{
    if (entry.ToLower().Contains("python"))
    {
        var breadcrumbs = entry.Split('\\');
        foreach (string breadcrumb in breadcrumbs)
        {
            if (breadcrumb.ToLower().Contains("python"))
            {
                python_location += breadcrumb + '\\';
                break;
            }
            python_location += breadcrumb + '\\';
        }
        break;
    }
}
0

Just change the FileName to "python.exe" if you already set python env path

private void runPython(string cmd, string args)
    {
        ProcessStartInfo start = new ProcessStartInfo();
        start.FileName = "python.exe";
        start.Arguments = string.Format("{0} {1}", cmd, args);
        start.UseShellExecute = false;
        start.RedirectStandardOutput = true;
        using (Process process = Process.Start(start))
        {
            using (StreamReader reader = process.StandardOutput)
            {
                string result = reader.ReadToEnd();
                Console.Write(result);
            }
        }
    }
0

On my machine, with Python 3.11 installed, I can query it by defining this property:

public string PythonInstallPath 
{ 
   get => (string)Microsoft.Win32.Registry.GetValue(
          @"HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\3.11\InstallPath",
          "ExecutablePath", null); 
}

Pythonw.exe is located in the same path, so you can do:

public string PythonWInstallPath 
{ 
   get => System.IO.Path.Combine(System.IO.Path.GetDirectoryName(PythonInstallPath), 
                                 "pythonw.exe"); 
}

There is also a way to look it up in the environment, check this out as an alternative.

Matt
  • 25,467
  • 18
  • 120
  • 187