如何检测是否“按任意键继续”。 。 ”。”将会显示?

发布于 2024-12-05 04:21:46 字数 461 浏览 1 评论 0原文

在 Visual Studio 中运行控制台应用程序时,根据您的设置,它会在程序退出后添加提示:

按任意键继续。 。 .

我已经找到了如何检测我是否在调试器下运行(使用 Debugger.IsAttached ),但这没有帮助。按CTRL-F5启动而不调试将此标志设置为false,但仍然显示提示

我想检测到这一点,因为我想显示自己的消息并等待按键,但不加倍按键检查

我不想破坏我的一般 Visual Studio 设置。如果我可以通过可以签入源代码管理的方式为该项目禁用它,那也可以。

使用什么机制来附加此提示,如何检测它?

或者如何在每个项目中禁用它,并将此更改签入源代码管理?

When running a console application in Visual Studio, depending on your settings, it will add a prompt after the program exits:

Press any key to continue . . .

I have found how to detect if I am running under the debugger(use Debugger.IsAttached), but it isn't helpful. Press CTRL-F5 to Start Without Debugging sets this flag to false, yet still shows the prompt.

I want to detect this because I'd like to display my own message and wait for a keypress, but not double up keypress checks.

I don't want to muck with my general Visual Studio settings. If I can disable it for this project in a way that can be checked into source control, that would also work.

What mechanism is used to append this prompt, and how do I detect it?

Or how do I disable it per-project, and check this change into source control?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

许久 2024-12-12 04:21:46

将以下代码添加到控制台应用程序:

public static class Extensions {
    [DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);

    [DllImport("kernel32.dll")]
    static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);

    public static Process GetParentProcess(this Process x) {
        return (
            from it in (new ManagementObjectSearcher("root\\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>()
            where (uint)it["ProcessId"]==x.Id
            select Process.GetProcessById((int)(uint)it["ParentProcessId"])
            ).First();
    }

    public static IEnumerable<Process> GetChildProcesses(this Process x) {
        return (
            from it in (new ManagementObjectSearcher("root\\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>()
            where (uint)it["ParentProcessId"]==x.Id
            select Process.GetProcessById((int)(uint)it["ProcessId"])
            );
    }

    public static void Abort(this ProcessThread x) {
        TerminateThread(OpenThread(1, false, (uint)x.Id), 1);
    }
}

然后像这样修改代码:

class Program {
    static void Main(String[] args) {
        // ... (your code might goes here)

        try {
            Process.GetCurrentProcess().GetParentProcess().Threads.Cast<ProcessThread>().Single().Abort();
        }
        catch(InvalidOperationException) {
        }

        Console.Write("Press ONLY key to continue . . . ");
        Console.ReadKey(true);
    }
}

因此,我们期望的一切现在都已完成。我认为这是一个解决方案。它可以在 Windows XP SP3 下运行,我猜它也可以在较新的 Windows 操作系统上运行。在Visual Studio下,应用程序始终是衍生进程。在较旧的 Visual C++ 6.0 中,它是由 IDE 通过调用 VCSPAWN.EXE 生成的;在 Visual Studio 2010 中,您的应用程序在启动而不调试时使用以下命令行运行:

"%comspec%" /c ""您的应用程序文件名" & 暂停"

因此不可能以完全托管的方式达到目标;因为它在应用程序域下。

这里我们使用WMI的托管方式来枚举进程,并封装非托管的WINAPI来终止ProcessThread,因为>ProcessThread 不应该正常中止;它以只读方式提供。

如上所述,应用程序是使用特定的命令行生成的;它将具有单个线程创建单个进程签名,因此我们使用Single()方法来检索该线程并终止它。

当我们在现有命令提示符下启动应用程序时,这与“启动而不调试”的场景相同。此外,当开始调试时,应用程序进程是由devenv.exe创建的。我们知道它有很多线程,并且不会中止任何线程,只是提示并等待按键。这种情况类似于通过双击或从上下文菜单启动应用程序。这样,应用程序进程是由系统外壳创建的,通常是Explorer.exe,它也有很多线程。

事实上,如果我们能够成功中止线程,就意味着我们有权限杀死父进程。但我们不需要需要。我们只需要中止唯一的线程,当进程没有更多线程时,系统会自动终止。通过识别调用进程是 %comspec% 来终止父进程是执行相同操作的另一种方法,但这是一个危险的过程。因为生成应用程序的进程可能有其他线程,这些线程有任意数量的线程创建与 %comspec% 匹配的进程。您可能会不小心杀死进程的关键工作,或者只是增加检查进程是否可以安全杀死的复杂性。因此,我认为单个线程创建单个进程作为父进程的签名,可以安全地终止/中止。

WMI 是现代的,某些 WINAPI 将来可能会被弃用。但这种构图的真正原因是它的简单性。旧的Tool Help Library非常复杂,就像将ProcessThread转换为System.Threading.Thread的方法一样。通过LINQ和扩展方法,我们可以使代码更简单、更语义化。

Add the following code to the console application:

public static class Extensions {
    [DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);

    [DllImport("kernel32.dll")]
    static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);

    public static Process GetParentProcess(this Process x) {
        return (
            from it in (new ManagementObjectSearcher("root\\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>()
            where (uint)it["ProcessId"]==x.Id
            select Process.GetProcessById((int)(uint)it["ParentProcessId"])
            ).First();
    }

    public static IEnumerable<Process> GetChildProcesses(this Process x) {
        return (
            from it in (new ManagementObjectSearcher("root\\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>()
            where (uint)it["ParentProcessId"]==x.Id
            select Process.GetProcessById((int)(uint)it["ProcessId"])
            );
    }

    public static void Abort(this ProcessThread x) {
        TerminateThread(OpenThread(1, false, (uint)x.Id), 1);
    }
}

And then modify your code like this:

class Program {
    static void Main(String[] args) {
        // ... (your code might goes here)

        try {
            Process.GetCurrentProcess().GetParentProcess().Threads.Cast<ProcessThread>().Single().Abort();
        }
        catch(InvalidOperationException) {
        }

        Console.Write("Press ONLY key to continue . . . ");
        Console.ReadKey(true);
    }
}

So, everything we are expecting is done now. I consider this as a workaround solution. It works under Windows XP SP3 and I guess that it would work with newer Windows operating systems. Under Visual Studio, applications are always a spawned process. In older Visual C++ 6.0, it spawned by the IDE by calling VCSPAWN.EXE; in Visual Studio 2010, your application runs with following command line when Start Without Debugging:

"%comspec%" /c ""your application filename" & pause"

So it is impossible to reach the goal in fully managed ways; because it was NOT under the application domain.

Here we use the managed way of WMI to enumerate the processes, and encapsulate the unmanaged WINAPIs to terminate the ProcessThreads, because the ProcessThread is not supposed to be normally aborted; it's provided like something for read-only.

As mentioned above, the application was spawned with the particular command line; it would have a single thread creates a single process signature, so we used the Single() method to retrieve that thread and terminate it.

When we start the application under an existing command prompt, it is just the same scenario of Start Without Debugging. Moreover, when Start Debugging, the application process is created by devenv.exe. It has a lot of threads, we known that and won't abort any thread, just prompt and wait for a key press. This situation is similar to starting application with double-clicking or from context menu. This way, the application process is created by the system shell, usually Explorer.exe and it also has a lots of threads.

In fact, if we can successfully abort the thread it implies that we have the permissions to kill the parent process. But we do NOT need to. We just need to abort the only thread, the process terminates automatically by system when it has no more threads. Killing the parent process by identifying that the calling process is %comspec%, is another way to do the same thing, but it's a dangerous procedure. Because the process spawning the application might have other threads which have any number of threads create a process matches %comspec%. You may kill a critical work of process with carelessness or just growing the complexity of checking whether the process is safe to kill. So I consider a single thread creates a single process as a signature of our parent process which is safe to kill/abort.

WMI is modern, some of WINAPIs might become deprecated in the future. But the real reason of this composition is for its simplicity. The old Tool Help Library is such complicated like the ways to convert ProcessThread to System.Threading.Thread. With LINQ and extension methods, we can make the code simpler and more semantical.

酒解孤独 2024-12-12 04:21:46

听起来这个提示是由 pause 命令提供的。此命令由 Visual Studio 自动添加。

当您在 Visual Studio 之外运行项目时,没有理由“检测”此命令。您可以放心地假设它不会添加到您的程序中。这意味着您可以继续添加所需的任何提示,类似于:

Console.WriteLine("Press any key...");
Console.Read();

查看此问题。

Sounds like this prompt is provided by the pause command. This command is automatically added by Visual Studio.

When you run your project outside of Visual Studio, there is no reason to "detect" this command. You can safely assume that it will not be added to your program. This means you can go ahead and add whatever prompt you want, similar to:

Console.WriteLine("Press any key...");
Console.Read();

See this question.

夏至、离别 2024-12-12 04:21:46

这是应该执行此操作的一段代码:

class Program
{
    static void Main(string[] args)
    {
        // do your stuff

        if (!WasStartedWithPause())
        {
            Console.WriteLine("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
}

public static bool WasStartedWithPause()
{
    // Here, I reuse my answer at http://stackoverflow.com/questions/394816/how-to-get-parent-process-in-net-in-managed-way
    Process parentProcess = ParentProcessUtilities.GetParentProcess();

    // are we started by cmd.exe ?
    if (string.Compare(parentProcess.MainModule.ModuleName, "cmd.exe", StringComparison.OrdinalIgnoreCase) != 0)
        return false;

    // get cmd.exe command line
    string cmdLine = GetProcessCommandLine(parentProcess);

    // was it started with a pause?
    return cmdLine != null & cmdLine.EndsWith("& pause\"");
}

public static string GetProcessCommandLine(Process process)
{
    if (process == null)
        throw new ArgumentNullException("process");

    // use WMI to query command line
    ManagementObjectCollection moc = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId=" + process.Id).Get();
    foreach (ManagementObject mo in moc)
    {
        return (string)mo.Properties["CommandLine"].Value;
    }
    return null;
}

Here is a piece of code that should do it:

class Program
{
    static void Main(string[] args)
    {
        // do your stuff

        if (!WasStartedWithPause())
        {
            Console.WriteLine("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
}

public static bool WasStartedWithPause()
{
    // Here, I reuse my answer at http://stackoverflow.com/questions/394816/how-to-get-parent-process-in-net-in-managed-way
    Process parentProcess = ParentProcessUtilities.GetParentProcess();

    // are we started by cmd.exe ?
    if (string.Compare(parentProcess.MainModule.ModuleName, "cmd.exe", StringComparison.OrdinalIgnoreCase) != 0)
        return false;

    // get cmd.exe command line
    string cmdLine = GetProcessCommandLine(parentProcess);

    // was it started with a pause?
    return cmdLine != null & cmdLine.EndsWith("& pause\"");
}

public static string GetProcessCommandLine(Process process)
{
    if (process == null)
        throw new ArgumentNullException("process");

    // use WMI to query command line
    ManagementObjectCollection moc = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId=" + process.Id).Get();
    foreach (ManagementObject mo in moc)
    {
        return (string)mo.Properties["CommandLine"].Value;
    }
    return null;
}
静待花开 2024-12-12 04:21:46

该消息与您的程序无关。您可以将任何您喜欢的内容添加到程序中,并且它将以与从命令提示符运行它相同的方式执行。

Visual Studio 显示它的目的是向您表明其执行已完成运行,以便您知道它正确结束。如果您想跳过它,您可以尝试“运行而不调试”(或类似的东西;它就在“使用调试器运行”下方)。

That message has nothing to do with your program. You can add to your program anything you like and it will perform in the same way as it would, if you were to run it from the command prompt.

Visual studio displays it for the purpose of showing you that its execution has finished running, so you know it ended correctly. If you wish to skip it you could try to "run without debugging" (or something like that; it's just below "run with debugger").

夏有森光若流苏 2024-12-12 04:21:46

这是由 Visual Studio 添加的“暂停”命令的输出。如果你看到这个,程序就结束了。这就涉及到一个问题,比如应用程序是否有可能检测到它自己已经结束。我认为这不符合逻辑。

This is the output of "pause" command which is added by visual studio. If you see this, the program is ended. Which comes to a question like is it possible for an application to detect that it self is ended. I think this is not logical.

洛阳烟雨空心柳 2024-12-12 04:21:46

不会帮助检测,但如果将其添加到 Main 的末尾,则在附加调试器 (F5) 的情况下运行时会添加相同的提示:

if (Debugger.IsAttached)
{
    Console.Write("Press any key to continue . . . ");
    Console.ReadKey();
}

实际上,它会与 Ctrl + F5 执行相同的操作与 & 一起执行暂停

Won't help to detect, but will add the same prompt when you run with debugger attached (F5) if you add this to the end of Main:

if (Debugger.IsAttached)
{
    Console.Write("Press any key to continue . . . ");
    Console.ReadKey();
}

It will in practice do the same as Ctrl + F5 does with & pause

半衬遮猫 2024-12-12 04:21:46

命令执行时出现此提示
系统(“暂停”)

使用

。即使我也面临这个问题。您可以使用 conio.h 下的函数 _getch() 来等待按键。

因此,您可以使用以下代码:

      cout<<"your message here"
      _getch()

这将等待按键并显示您自己的提示。

This prompt is given when the command
system("pause")

is used.

Even I faced this problem. You can either use the function _getch() under conio.h for waiting for key press.

So you can use the following code:

      cout<<"your message here"
      _getch()

This would wait for the key press and display your own prompt.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文