我必须创建一个控制台应用程序,需要某些参数。如果缺少或错误,我会打印出错误信息。
现在的问题是:如果有人通过双击资源管理器启动程序,则控制台窗口立即消失。(但是从资源管理器启动的应用程序并不完全无用,您可以将文件拖放到它上面,它也可以工作)
我可以始终等待按键,但如果用户从命令行启动它,我不想要这个。
有没有办法区分这些情况?
请参考http://support.microsoft.com/kb/99115,其中有关于“INFO: 防止控制台窗口消失”的信息。
核心思路是使用GetConsoleScreenBufferInfo函数来确定光标是否移动了初始位置0,0。
下面是基于上述知识库文章的@tomlogic提供的代码示例:
// call in main() before printing to stdout
// returns TRUE if program is in its own console (cursor at 0,0) or
// FALSE if it was launched from an existing console.
// See http://support.microsoft.com/kb/99115
#include <stdio.h>
#include <windows.h>
int separate_console( void)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (!GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE), &csbi))
{
printf( "GetConsoleScreenBufferInfo failed: %lu\n", GetLastError());
return FALSE;
}
// if cursor position is (0,0) then we were launched in a separate console
return ((!csbi.dwCursorPosition.X) && (!csbi.dwCursorPosition.Y));
}
我见过一些与之相关的代码
if (!GetConsoleTitle(NULL, 0) && GetLastError() == ERROR_SUCCESS) {
// Console
} else {
// GUI
}
AttachConsole()
更有帮助。在C++中(仅凭记忆,我不是C++程序员)。if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
// GUI
} else {
// Console, and you have a handle to the console that already exists.
}
更有效。此外,如果你发现自己在图形用户界面(GUI)环境中,并且希望尽可能长时间留在其中,但随后发现发生了一些灾难性事件,需要将其转储到控制台窗口(你不想写一个编辑框窗口来记录它或连接到NT系统日志并弹出MessageBox()
),那么你可以在进程后期使用AllocConsole()
,当GUI方法失败时。
我找到了一个更好的解决方案,使用GetConsoleProcessList来获取当前控制台附加的进程数量。 如果这个进程是唯一附加的进程,那么当进程退出时它将被关闭。
我在这篇文章中找到了它:https://devblogs.microsoft.com/oldnewthing/20160125-00/?p=92922 但是它有一个错误(至少在Windows 10中),因为文档禁止使用null调用此函数。
我的解决方案是:
DWORD procId;
DWORD count = GetConsoleProcessList(&procId, 1);
if (count < 2) ...
我相信cmd.exe
在启动时设置了CMDCMDLINE和CMDEXTVERSION环境变量。因此,如果这些变量被设置了,你的程序很可能是从一个shell启动的。
这并不是绝对可靠的方法,但至少值得一试。
还有一些复杂而可能不太可靠的方法可以确定你的父进程ID,你可能需要进一步研究。
以下是@DanielBenSassoon的优秀答案,适用于C#。在Visual Studio 2019和Windows 10中进行了测试。
// Gets a list of the process IDs attached to this console
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint GetConsoleProcessList(uint[] processList, uint processCount);
public static bool IsFinalProcess()
{
// See: https://devblogs.microsoft.com/oldnewthing/20160125-00/?p=92922
uint[] procIDs = new uint[64];
uint processCount = GetConsoleProcessList(procIDs, 64);
return (processCount < 2);
}
IsFinalProcess
为true时,您可以使用Console.ReadKey(false);
来防止应用程序退出后控制台窗口消失。编辑:我已经成功地使用这个 .bat/.cmd 包装器几年了:
@ECHO OFF
REM Determine if script was executed from an interactive shell
(echo %CMDCMDLINE% | find /i "%~0" >NUL 2>&1) && (set IS_INTERACTIVE=false) || (set IS_INTERACTIVE=true)
<call console application here>
REM Wait for keystroke
if "%IS_INTERACTIVE%" == "false" (
echo.
echo Hit any key to close.
pause >NUL 2>&1
)
这里的优点是,它适用于所有控制台应用程序,无论是您自己的还是别人的(您可能没有可以修改的源代码)。缺点是,您需要一个单独的包装器。
我的2014年的原始答案是:
这个方法非常有效:
@echo off
for %%x in (%cmdcmdline%) do if /i "%%~x"=="/c" goto nonconsole
:console
<do something>
goto exit
:nonconsole
<do something>
pause
:exit
从这个帖子复制过来。我也试过自己评估%cmdcmdline%,但是存在一个关于引号字符(")的问题,这会导致类似if "%cmdcmdline%" == "%ComSpec%" goto [target]这样的语句无法工作。
cls && appname.exe
也可以正常工作。我将编辑此答案以包含我正在使用的代码,供其他人参考。 - tomlogic