简体   繁体   English

可以打开窗口的 Win32 控制台应用程序

[英]Win32 Console application that can open windows

My question is pretty simple.我的问题很简单。 In Ubuntu, programs can often be deployed with or without a GUI, perhaps using a flag like --showGUI.在 Ubuntu 中,通常可以使用或不使用 GUI 部署程序,可能使用 --showGUI 之类的标志。 I essentially want to recreate this functionality in windows, but it seems that windows applications start with win_main, while console applications start with main.我本质上想在 Windows 中重新创建此功能,但似乎 Windows 应用程序以 win_main 开头,而控制台应用程序以 main 开头。

So what is the basic structure required to produce this behavior?那么产生这种行为所需的基本结构是什么? Eg in in Visual Studio 2012, should I start with a Windows App and then hide the window and write to console?例如,在 Visual Studio 2012 中,我应该从 Windows 应用程序开始,然后隐藏窗口并写入控制台吗? Or can I start with an empty Console Application and create a window with the windows API?或者我可以从一个空的控制台应用程序开始并使用 windows API 创建一个窗口?

Thanks谢谢

(c/c++, btw) (c/c++,顺便说一句)

A console application starts out attached to a console.控制台应用程序开始时附加到控制台。 It can then create windows as it sees fit--essentially no different from an application written specifically for the windows subsystem.然后它可以创建它认为合适的窗口——本质上与专门为 Windows 子系统编写的应用程序没有什么不同。

In theory you can do the opposite: create an application for the windows subsystem, and then attach a console to it.理论上你可以做相反的事情:为 windows 子系统创建一个应用程序,然后将一个控制台附加到它。 This adds a fair amount of extra work though.不过,这增加了相当多的额外工作。 The startup code in the standard library normally does some work to attach stdin/cin, stdout/cout, stderr/cerr to the console.标准库中的启动代码通常会做一些工作来将 stdin/cin、stdout/cout、stderr/cerr 附加到控制台。 If you create a windows subsystem program, then attach a console, you basically have to reproduce that code to attach the console to the standard streams.如果您创建一个 Windows 子系统程序,然后附加一个控制台,您基本上必须重现该代码以将控制台附加到标准流。

As such, it's usually easiest to start with a program written for the console subsystem and have it create windows than start with a program written for the windows subsystem and have it create/attach a console.因此,通常最容易从为控制台子系统编写的程序开始并让它创建窗口,而不是从为 Windows 子系统编写的程序开始并让它创建/附加控制台。

As far as main vs. WinMain goes: this controls which subsystem the program will be linked for by default.mainWinMain而言:这控制了默认情况下程序将链接到哪个子系统。 That is, if you have a function named main , it'll default to the console subsystem.也就是说,如果您有一个名为main的函数,它将默认为控制台子系统。 If you have a function named WinMain , it'll default to the windows subsystem (offhand I don't remember which it'll do if you define both--I'd advise against doing that).如果你有一个名为WinMain的函数,它会默认为 windows 子系统(我不记得如果你同时定义了它会做什么——我建议不要这样做)。

You can, however, force the subsystem choice with a linker flag if you want to, so you can use main as the entry point for a windows-subsystem program, or WinMain as the entry point for a console-subsystem program.但是,如果需要,您可以使用链接器标志强制选择子系统,因此您可以使用main作为 Windows 子系统程序的入口点,或使用WinMain作为控制台子系统程序的入口点。 I'd generally advise against doing either of those as well though.不过,我通常建议不要这样做。

Windows app's can have a GUI and console window at the same time. Windows 应用程序可以同时具有 GUI 和控制台窗口。 It's just that no one ever sets them up that way.只是从来没有人这样设置过它们。 You have to handle it your self.你必须自己处理。

Here is some example code that does this:这是一些执行此操作的示例代码:

#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;
}

The top part of the program is just a normal Window's program with a WinMain() entry point.程序的顶部只是一个带有 WinMain() 入口点的普通 Window 程序。 The magic comes from the ReconnectIO() function.魔法来自 ReconnectIO() 函数。 It will reconnect the standard io's and open a console window if needed.如果需要,它将重新连接标准 io 并打开控制台窗口。

When you start the program from a command line, standard out will go to it, when you start from the desktop only the main window is opened.当您从命令行启动程序时,标准输出将转到它,当您从桌面启动时,只打开主窗口。

It does have one draw back which is that when started from the command line it return right away instead of blocking until the program exits.它确实有一个缺点,那就是当从命令行启动时,它会立即返回而不是阻塞直到程序退出。 The startup code is doing that and I haven't looked into how to stop it.启动代码正在这样做,我还没有研究如何阻止它。

You can have a look at http://dslweb.nwnexus.com/~ast/dload/guicon.htm from some more info about what is going on.您可以从有关正在发生的事情的更多信息中查看http://dslweb.nwnexus.com/~ast/dload/guicon.htm

This is really hard to do well in Windows.这在 Windows 中真的很难做好。

  1. If you create a program that uses the Windows subsystem, it's difficult to know which console to attach to.如果您创建一个使用 Windows 子系统的程序,则很难知道要附加到哪个控制台。 (What if it was started from the Run menu or a desktop shortcut and there isn't a console?) (如果它是从“运行”菜单或桌面快捷方式启动而没有控制台怎么办?)

  2. If you create a program that uses the Console subsystem, you always get a console.如果您创建一个使用控制台子系统的程序,您总是会得到一个控制台。 (You can also create windows, but, if you launch from somewhere other than an existing console, a new console window is going to flash up whether you wanted it or not.) (您也可以创建窗口,但是,如果您从现有控制台以外的其他地方启动,则无论您是否需要,都会出现一个新的控制台窗口。)

Visual Studio has a hack to get around this. Visual Studio 有一个 hack 来解决这个问题。 If you look closely, you'll see that there are two devenv executables in the same directory: devenv.exe is a Windows app and devenv.com is a console app.如果仔细观察,您会发现同一目录中有两个devenv 可执行文件:devenv.exe 是一个 Windows 应用程序,devenv.com 是一个控制台应用程序。

All of the shortcuts point directly to the .exe.所有快捷方式都直接指向 .exe。 But if you type devenv in a console window, and the installation directory is in your path, it'll actually launch devenv.com, the console one.但是,如果您在控制台窗口中键入devenv ,并且安装目录在您的路径中,它实际上会启动 devenv.com,即控制台。 This is because .com is typically listed before .exe in the PATHEXT environment variable.这是因为.com通常在PATHEXT环境变量中列在.exe之前。

If you include a command line option (like /? ) that devenv.com can handle strictly in console mode, it will.如果包含 devenv.com 可以在控制台模式下严格处理的命令行选项(如/? ),它会。 Otherwise, it will just invoke devenv.exe for you and exit.否则,它只会为您调用 devenv.exe 并退出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Win32 控制台应用程序 - Win32 Console Application 从Windows 10上的Win32 GUI应用程序输出到控制台 - Output to console from a Win32 GUI application on Windows 10 如何将项目win32控制台应用程序转换为C ++ Windows窗体? - How can I convert project win32 console application into C++ Windows Form? 在Win32控制台应用程序中显示弹出窗口 - Display popup in Win32 Console Application 如何从另一个C ++ win32控制台应用程序调用C ++ Win32 DLL - How Can I call a C++ Win32 DLL from another C++ win32 Console Application win32应用程序,Windows窗体应用程序和控制台应用程序有什么区别? - what is the difference between win32 application, windows form application and console application? Windows XP 上的“不是有效的 win32 应用程序” - “is not a valid win32 application” on windows xp 来自一个 Win32 控制台应用程序的多个控制台窗口 - Multiple console windows from one Win32 console app 使用Windows taskchd.msc编程C ++ Win32非控制台应用程序,无法正常工作 - Program C++ Win32 non-console application with Windows taskschd.msc , not working 在 Windows C++ 控制台应用程序(win32)中检测“Shift + 箭头键” - Detect "Shift + Arrow Key" in Windows C++ Console Application (win32)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM