如何创建执行以下操作的Windows应用程序:
在没有命令行参数的情况下调用时,它是一个常规的GUI应用程序
指定可选的"--help"命令行参数会导致应用程序将使用文本写入stdout然后终止
它必须是单个可执行文件.通过使控制台应用程序执行第二个可执行文件没有作弊.
假设主应用程序代码是用C/C++编写的
如果在指定"--help"时没有创建GUI窗口,则奖励积分.(即,短暂的窗口没有闪烁)
根据我的经验,控制台应用程序的标准visual studio模板没有GUI功能,而普通的win32模板不会将其stdout发送到父cmd shell.
微软设计的控制台和GUI应用程序是互斥的.这种短视意味着没有完美的解决方案.最流行的方法是有两个可执行文件(例如.cscript/wscript,java/javaw,devenv.com/devenv.exe等),但是你已经表明你认为这是"作弊".
您有两个选项 - 制作"控制台可执行文件"或"gui可执行文件",然后使用代码尝试提供其他行为.
GUI可执行文件
cmd.exe
将假设您的程序没有控制台I/O,因此不会等待它在继续之前终止,这在交互模式(即不是批处理)意味着显示下一个(" C:\>
")提示并从键盘读取.因此,即使您使用AttachConsole,您的输出也会与输出混合,cmd
如果您尝试输入,情况会变得更糟.这基本上是一个非首发.
控制台可执行
与信仰相反,没有什么可以阻止控制台可执行文件显示GUI,但有两个问题.
第一个是如果你从没有参数的命令行运行它(所以你想要GUI),
cmd
它仍然会在继续之前等待它终止,这样特定的控制台在这段时间内将无法使用.这可以通过启动相同可执行文件的第二个进程来解决(您认为这是作弊吗?),将DETACHED_PROCESS标志传递给CreateProcess()并立即退出.然后,新进程可以检测到它没有控制台并显示GUI.
这是用于说明此方法的C代码:
#include#include int main(int argc, char *argv[]) { if (GetStdHandle(STD_OUTPUT_HANDLE) == 0) // no console, we must be the child process { MessageBox(0, "Hello GUI world!", "", 0); } else if (argc > 1) // we have command line args { printf("Hello console world!\n"); } else // no command line args but a console - launch child process { DWORD dwCreationFlags = CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS; STARTUPINFO startinfo; PROCESS_INFORMATION procinfo; ZeroMemory(&startinfo, sizeof(startinfo)); startinfo.cb = sizeof(startinfo); if (!CreateProcess(NULL, argv[0], NULL, NULL, FALSE, dwCreationFlags, NULL, NULL, &startinfo, &procinfo)) MessageBox(0, "CreateProcess() failed :(", "", 0); } exit(0); }
我用cygwin的gcc -YMMV和MSVC编译了它.
第二个问题是,从资源管理器运行时,您的程序将在一瞬间显示一个控制台窗口.没有编程方式,因为控制台是在应用程序启动时由Windows创建的,然后才开始执行.您可以做的唯一事情是,在安装程序中,使用SW_HIDE的"show command"(即0)创建程序的快捷方式.除非您故意在程序中使用STARTUPINFO的wShowWindow字段,否则这只会影响控制台,所以不要这样做.
我通过黑客攻击cygwin的"mkshortcut.exe"来测试这个.如何在您选择的安装程序中完成它取决于您.
用户当然可以通过在资源管理器中查找可执行文件并双击它,绕过控制台隐藏快捷方式并查看控制台窗口的简短黑色闪光来运行程序.你无能为力.
您可以使用AllocConsole()
WinApi函数为GUI应用程序分配控制台.您也可以尝试附加到父进程的控制台,AttachConsole()
如果它已经有一个,这是有意义的.与重定向的完整代码stdout
,并stderr
以该控制台将是这样的:
if(AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole()){ freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); }
我在Pidgin源代码中找到了这种方法(参见WinMain()
pidgin/win32/winpidgin.c)