如何在C#中判断我的应用程序是从控制台还是Windows打开的

4

我有一个既包含GUI界面又包含控制台的应用程序。

控制台:它可以在Windows计划任务中执行一些自动化任务,因此需要使用参数来调用。

GUI:用于输入配置参数,相比控制台而言更加友好易用。

所有这些功能都很好。主要是控制台应用程序,如果没有使用参数打开,则控制台将被隐藏,配置表单将会被显示。

问题:如果我从控制台打开,没有使用参数,则控制台将被隐藏,表单将会被显示。如何检测我从哪里或者从什么地方打开了这个应用程序?如果是从Windows中打开的,则应该隐藏控制台,如果是从控制台打开的,则不隐藏控制台。


也许可以将其默认为控制台模式,并需要一个参数来打开GUI,您可以为用户创建一个快捷方式。如果失败,请参见如何检查程序是否从控制台运行? - Alex K.
您可以检查正在运行的进程,找到所需的进程并按照您的意愿应用操作。 - habib
4个回答

5
如果您真的想知道应用程序的“起始位置”,则需要知道您的父进程是什么。为了知道您的父进程,可以阅读以下解决方案:如何以托管方式在.NET中获取父进程 然后,例如,您可以检查您的父进程名称是否为explorer(windows),以将您的应用程序作为GUI打开。
示例代码基于如何以托管方式在.NET中获取父进程提供的解决方案。
namespace ConsoleApp1
{
    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));
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Process.GetCurrentProcess().Parent().ProcessName);
            Console.ReadKey();
        }
    }
}

这段代码将输出以下内容:
  • 在Visual Studio中进行调试:devenv
  • 从Windows启动:explorer
  • 从命令行启动:cmd
  • 从PowerShell控制台启动:powershell
  • ...

@magiva 这个回答是否完全解决了你的问题?如果没有,请告诉我你需要什么或者为什么这个回答还不够完整,我会尝试让它更好。如果这个回答已经解决了你的问题,你可以将问题标记为已解决吗? - asidis
这是一个完美的解决方案,非常优雅地结束了这个项目。现在,我的控制台应用程序将在从资源管理器打开时隐藏控制台,在其他类型下保持打开状态,非常完美。非常感谢! - magiva

1
一种方法是将cli版本和gui版本分开成两个可执行文件(就像7z使用7z.exe命令行工具和7zG Gui版本一样)。
你可以在Visual Studio中有三个项目:
- MyApp.Console(控制台应用程序) - MyApp.WindowsGui(winform/wpf应用程序) - MyApp.Logic(所有逻辑)
控制台和WindowsGui引用您的Logic项目。
这会给你更干净的代码,因为每个“前端”项目只处理它们的目的(处理GUI或控制台内容),而您的Logic可以被两个前端调用。

0

我不太清楚你想要实现什么。据我理解,无论是否有参数,应用程序都将作为控制台应用程序启动。为了防止它消失,您可以利用一个 Boolean 来防止窗口在用户输入配置时关闭。例如(语法可能不是 100% 的 DialogResult):

using System;
using System.Windows.Forms;

// Allows you to access the static objects of Console
//     without having to repeatedly type Console.Something.
using static System.Console;

static bool configured = false;
static bool showForm = false;
static void Main(string[] args) {
    showForm = args.Length < 1;
    if (showForm) {
        WriteLine("The application needs to be configured.");
        using (ConfigForm config = new ConfigForm()) {
            if (config.ShowDialog() == DialogResult.OK) {
                showForm = false;
                configured = true;
                // Set your configured arguments here.
            }
        }
    }

    // Prevents the console from closing.
    while (showForm)
        ReadKey();

    // Do your processing in this condition.
    if (!showForm && configured)
        WriteLine("Thanks for playing. Press any key to exit.");
    else // Retry or exit in this one.
        WriteLine("An error occurred. Press any key to exit.");

    ReadKey();
}

如果您的应用程序设置为控制台应用程序,则默认情况下会启动控制台窗口。现在,如果您需要在不同的时间显示和隐藏控制台,则可以查看this post,其中被接受的答案提供了一种适当的方式来利用Windows API实现此目的,而无需执行一些可疑的逻辑以通过标题或标识找到窗口。
using System.Runtime.InteropServices;

[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

const int SW_HIDE = 0;
const int SW_SHOW = 5;

var handle = GetConsoleWindow();

// Hide
ShowWindow(handle, SW_HIDE);

// Show
ShowWindow(handle, SW_SHOW);

如果这不能解决你的问题,可以在你的帖子中更详细地描述,并附上一些代码以更清晰地说明你的问题。由于我无法评论和提问,所以只能提供基本的解决方案。如果你有任何问题,请随时提出。

我可以理解这个问题似乎很奇怪,但它的工作方式是这样的。 它可以从cmd中不带任何参数地调用,此时我不想隐藏控制台,或者实际上当它关闭时我想重新显示它。 如果我从Windows打开,则只会隐藏控制台,控制台是我需要它在控制台和Windows中同时工作的副产品。我之所以需要它作为一个控制台应用程序,是因为我需要从Windows计划中的服务器调用它的参数。我不确定我是否帮助更好地解释了它。 - magiva

0

这可能会有所帮助:

using System;
using System.Diagnostics;

static class IsRanFromConsole
{
    private static readonly string[] consoleNames = {
        "cmd", "bash", "ash", "dash", "ksh", "zsh", "csh",
        "tcsh", "ch", "eshell", "fish", "psh", "pwsh", "rc",
        "sash", "scsh", "powershell", "tcc"
    };

    private static bool isCache = false;
    private static bool isConsole;

    public static bool IsConsole()
    {
        if (!isCache)
        {
            string parentProc = Process.GetCurrentProcess().Parent().ProcessName;
            isConsole = Array.IndexOf(consoleNames, parentProc) > -1;
        }
        return isConsole;
    }
}

使用方法:

Console.WriteLine(IsRanFromConsole.IsConsole());

在使用.Parent()函数时,您需要添加此代码

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接