简体   繁体   中英

Determine whether Console Application is run from command line or Powershell

如何确定控制台应用程序是从 Powershell 还是从应用程序内部的标准命令行运行?

Something like this might be more reliable than checking the window title:

using System;
using System.Diagnostics;

Process p = Process.GetCurrentProcess();
PerformanceCounter parent = new PerformanceCounter("Process", "Creating Process ID", p.ProcessName);
int ppid = (int)parent.NextValue();

if (Process.GetProcessById(ppid).ProcessName == "powershell") {
  Console.WriteLine("running in PowerShell");
} else {
  Console.WriteLine("not running in PowerShell");
}

[ source ]

One solution is to test the name of the parent process, and to compare it to "cmd" or "powershell". This thread is about finding the parent process. I extended one of the answers to reply to your question. Please investigate whether the provided answer is the most efficient way to get the parent process. This solution demonstrates the possibility, and is not intended to be a production code .

using System;
using System.Diagnostics;

public class TestPowershell {
    public static void Main() {
        string launcher = Process.GetCurrentProcess().Parent().ProcessName;
        if(launcher == "cmd") {
            Console.WriteLine("I was launched by cmd");
        }else if (launcher == "powershell") {
            Console.WriteLine("I was launched by PowerShell");
        }else {
            Console.WriteLine("not sure who launched me. But his name is: " + launcher);
        }
    }
}


// By Michael Hale: https://stackoverflow.com/questions/394816/how-to-get-parent-process-in-net-in-managed-way
public static class ProcessExtensions {
    private static string FindIndexedProcessName(int pid) {
        var processName = Process.GetProcessById(pid).ProcessName;
        var processesByName = Process.GetProcessesByName(processName);
        string processIndexdName = null;

        for (var index = 0; index < processesByName.Length; index++) {
            processIndexdName = index == 0 ? processName : processName + "#" + index;
            var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
            if ((int) processId.NextValue() == pid) {
                return processIndexdName;
            }
        }

        return processIndexdName;
    }

    private static Process FindPidFromIndexedProcessName(string indexedProcessName) {
        var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
        return Process.GetProcessById((int) parentId.NextValue());
    }

    public static Process Parent(this Process process) {
        return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
    }

}

// Define other methods and classes here

A better way (IMO) to determine whether a console application is being run from PowerShell or the standard command line is to P/Invoke GetConsoleProcessList to get the list of processes attached to the console, and use OpenProcess / QueryFullProcessImageName to check the name of these processes.

For example, to determine if an app was run from the PowerShell console*:

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace CheckIfRunningFromPowerShell
{
    class Program
    {
        static void Main(string[] args)
        {
            if (PowerShellUtils.IsCurrentProcessRunningFromPowerShellIse())
            {
                Console.WriteLine("PowerShell, yay!");
            }
            else
            {
                Console.WriteLine("NOPE :(");
            }
        }
    }

    public class PowerShellUtils
    {
        public static bool IsCurrentProcessRunningFromPowerShellIse()
        {
            var processList = new uint[1];
            var count = GetConsoleProcessList(processList, 1);
            if (count <= 0)
            {
                return false;
            }

            processList = new uint[count];
            count = GetConsoleProcessList(processList, (uint)processList.Length);
            for (var pid = 0; pid < count; pid++)
            {
                var buffer = new StringBuilder(260);
                var dwSize = (uint) buffer.Capacity;

                var process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, (int) processList[pid]);
                QueryFullProcessImageName(process, 0, buffer, ref dwSize);

                var exeFileName = buffer.ToString(0, (int) dwSize);

                // Name of EXE is PowerShell_ISE.exe or powershell.exe
                if (exeFileName.IndexOf("PowerShell_ISE.exe", StringComparison.OrdinalIgnoreCase) != -1 ||
                    exeFileName.IndexOf("powershell.exe", StringComparison.OrdinalIgnoreCase) != -1)
                {
                    return true;
                }
            }

            return false;
        }

        [DllImport("kernel32.dll", ExactSpelling=true, EntryPoint="QueryFullProcessImageNameW", CharSet = CharSet.Unicode)]
        internal static extern bool QueryFullProcessImageName(IntPtr hProcess, uint dwFlags, StringBuilder lpExeName, ref uint lpdwSize);

        [DllImport("kernel32.dll", ExactSpelling=true)]
        internal static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern uint GetConsoleProcessList(uint[] processList, uint processCount);

        // ReSharper disable InconsistentNaming
        internal const uint PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
        // ReSharper restore InconsistentNaming
    }
}

You can adapt the code above to check for cmd.exe, etc.


However, if all you're trying to do is to determine whether the console will continue to exist after your program exits (so that you can, for example, prompt the user to hit Enter before the program exits) , then all you have to do is to check if your process is the only one attached to the console. If it is, then the console will be destroyed when your process exits. If there are other processes attached to the console, then the console will continue to exist (because your program won't be the last one).

For example:

using System;
using System.Runtime.InteropServices;

namespace CheckIfConsoleWillBeDestroyedAtTheEnd
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            // ...

            if (ConsoleWillBeDestroyedAtTheEnd())
            {
                Console.WriteLine("Press any key to continue . . .");
                Console.ReadLine();
            }
        }

        private static bool ConsoleWillBeDestroyedAtTheEnd()
        {
            var processList = new uint[1];
            var processCount = GetConsoleProcessList(processList, 1);
            
            return processCount == 1;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern uint GetConsoleProcessList(uint[] processList, uint processCount);
    }
}

(*) Adapted from code found here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM