因此,需要什么基本结构来产生此行为?例如,在Visual Studio 2012中,我应该从Windows应用程序开始,然后隐藏窗口并写入控制台吗?还是我可以从空控制台应用程序开始,并使用Windows API创建窗口?
谢谢
(顺便说一句,是c/c++)
控制台应用程序开始时附加到控制台。然后,它可以随意创建窗口——与专门为Windows子系统编写的应用程序没有什么区别。
理论上,你也可以做相反的事情:创建一个Windows子系统应用程序,然后将控制台附加到它上面。不过这会增加很多额外的工作量。标准库中的启动代码通常会对stdin/cin、stdout/cout以及stderr/cerr进行一些操作,将它们附加到控制台上。如果你创建了一个Windows子系统程序,然后附加了一个控制台,那么你基本上必须重写该代码,将控制台附加到标准流上。
因此,通常最简单的方法是从为控制台子系统编写的程序开始,然后让它创建窗口,而不是从为Windows子系统编写的程序开始,然后创建/附加控制台。
关于main
与WinMain
:这决定了程序默认链接的子系统。也就是说,如果有一个名为main
的函数,它将默认链接到控制台子系统。如果有一个名为WinMain
的函数,它将默认链接到Windows子系统(我不记得如果两个都定义了会发生什么——我建议不要这样做)。
但是,如果需要的话,你可以通过链接器标志强制选择子系统,因此你可以将main
作为Windows子系统程序的入口点,或者将WinMain
作为控制台子系统程序的入口点。不过,我通常也不建议这样做。
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <iostream>
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
bool ReconnectIO(bool OpenNewConsole);
int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
MSG msg;
HWND hwnd;
WNDCLASS wc;
if(!ReconnectIO(false))
printf("Started from command prompt\n");
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.lpszClassName = "Window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpszMenuName = NULL;
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
RegisterClass(&wc);
hwnd = CreateWindow(wc.lpszClassName, "Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 350, 250, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
printf("Entering GetMessage loop\n");
while (GetMessage(&msg, NULL, 0, 0))
{
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
/*******************************************************************************
* NAME:
* ReconnectIO
*
* SYNOPSIS:
* bool ReconnectIO(bool OpenNewConsole);
*
* PARAMETERS:
* OpenNewConsole [I] -- This controls if we open a console window or not.
* True -- if the program was not started from an
* existing console open a new console window.
* False -- Only connect stdio if the program was
* started from an existing console.
*
* FUNCTION:
* This function connects up the stardard IO (stdout, stdin, stderr) to
* the windows console. It will open a new console window if needed
* (see 'OpenNewConsole').
*
* RETURNS:
* true -- A new console window was opened
* false -- Using an existing console window
*
* SEE ALSO:
*
******************************************************************************/
bool ReconnectIO(bool OpenNewConsole)
{
int hConHandle;
long lStdHandle;
FILE *fp;
bool MadeConsole;
MadeConsole=false;
if(!AttachConsole(ATTACH_PARENT_PROCESS))
{
if(!OpenNewConsole)
return false;
MadeConsole=true;
if(!AllocConsole())
return false; // Could throw here
}
// STDOUT to the console
lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen( hConHandle, "w" );
*stdout = *fp;
setvbuf( stdout, NULL, _IONBF, 0 );
// STDIN to the console
lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen( hConHandle, "r" );
*stdin = *fp;
setvbuf( stdin, NULL, _IONBF, 0 );
// STDERR to the console
lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen( hConHandle, "w" );
*stderr = *fp;
setvbuf( stderr, NULL, _IONBF, 0 );
// C++ streams to console
std::ios_base::sync_with_stdio();
return MadeConsole;
}
在Windows中做到这一点真的很难。
如果你创建一个使用Windows子系统的程序,很难知道要连接哪个控制台。(如果它是从“运行”菜单或桌面快捷方式启动的,没有控制台怎么办?)
如果你创建一个使用控制台子系统的程序,你总是会得到一个控制台。(你也可以创建窗口,但是,如果你从除了现有控制台之外的地方启动,一个新的控制台窗口将会闪现出来,无论你是否想要它。)
Visual Studio有一个解决方法。如果你仔细看,就会发现在同一个目录下有两个devenv可执行文件:devenv.exe是一个Windows应用程序,而devenv.com是一个控制台应用程序。
所有的快捷方式都直接指向.exe文件。但是,如果你在控制台窗口中键入devenv,并且安装目录在你的路径中,它实际上会启动控制台版本的devenv.com。这是因为在PATHEXT环境变量中,.com通常列在.exe之前。
如果你包括一个命令行选项(比如/?),devenv.com可以在严格的控制台模式下处理它。否则,它将为你调用devenv.exe并退出。
main
函数。 - chris