问题
How do I determine whether a console application is being run from Powershell or the standard command line from within the application?
回答1:
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]
回答2:
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
回答3:
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 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.
来源:https://stackoverflow.com/questions/27022501/determine-whether-console-application-is-run-from-command-line-or-powershell