簡體   English   中英

可以打開窗口的 Win32 控制台應用程序

[英]Win32 Console application that can open windows

我的問題很簡單。 在 Ubuntu 中,通常可以使用或不使用 GUI 部署程序,可能使用 --showGUI 之類的標志。 我本質上想在 Windows 中重新創建此功能,但似乎 Windows 應用程序以 win_main 開頭,而控制台應用程序以 main 開頭。

那么產生這種行為所需的基本結構是什么? 例如,在 Visual Studio 2012 中,我應該從 Windows 應用程序開始,然后隱藏窗口並寫入控制台嗎? 或者我可以從一個空的控制台應用程序開始並使用 windows API 創建一個窗口?

謝謝

(c/c++,順便說一句)

控制台應用程序開始時附加到控制台。 然后它可以創建它認為合適的窗口——本質上與專門為 Windows 子系統編寫的應用程序沒有什么不同。

理論上你可以做相反的事情:為 windows 子系統創建一個應用程序,然后將一個控制台附加到它。 不過,這增加了相當多的額外工作。 標准庫中的啟動代碼通常會做一些工作來將 stdin/cin、stdout/cout、stderr/cerr 附加到控制台。 如果您創建一個 Windows 子系統程序,然后附加一個控制台,您基本上必須重現該代碼以將控制台附加到標准流。

因此,通常最容易從為控制台子系統編寫的程序開始並讓它創建窗口,而不是從為 Windows 子系統編寫的程序開始並讓它創建/附加控制台。

mainWinMain而言:這控制了默認情況下程序將鏈接到哪個子系統。 也就是說,如果您有一個名為main的函數,它將默認為控制台子系統。 如果你有一個名為WinMain的函數,它會默認為 windows 子系統(我不記得如果你同時定義了它會做什么——我建議不要這樣做)。

但是,如果需要,您可以使用鏈接器標志強制選擇子系統,因此您可以使用main作為 Windows 子系統程序的入口點,或使用WinMain作為控制台子系統程序的入口點。 不過,我通常建議不要這樣做。

Windows 應用程序可以同時具有 GUI 和控制台窗口。 只是從來沒有人這樣設置過它們。 你必須自己處理。

這是一些執行此操作的示例代碼:

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

程序的頂部只是一個帶有 WinMain() 入口點的普通 Window 程序。 魔法來自 ReconnectIO() 函數。 如果需要,它將重新連接標准 io 並打開控制台窗口。

當您從命令行啟動程序時,標准輸出將轉到它,當您從桌面啟動時,只打開主窗口。

它確實有一個缺點,那就是當從命令行啟動時,它會立即返回而不是阻塞直到程序退出。 啟動代碼正在這樣做,我還沒有研究如何阻止它。

您可以從有關正在發生的事情的更多信息中查看http://dslweb.nwnexus.com/~ast/dload/guicon.htm

這在 Windows 中真的很難做好。

  1. 如果您創建一個使用 Windows 子系統的程序,則很難知道要附加到哪個控制台。 (如果它是從“運行”菜單或桌面快捷方式啟動而沒有控制台怎么辦?)

  2. 如果您創建一個使用控制台子系統的程序,您總是會得到一個控制台。 (您也可以創建窗口,但是,如果您從現有控制台以外的其他地方啟動,則無論您是否需要,都會出現一個新的控制台窗口。)

Visual Studio 有一個 hack 來解決這個問題。 如果仔細觀察,您會發現同一目錄中有兩個devenv 可執行文件:devenv.exe 是一個 Windows 應用程序,devenv.com 是一個控制台應用程序。

所有快捷方式都直接指向 .exe。 但是,如果您在控制台窗口中鍵入devenv ,並且安裝目錄在您的路徑中,它實際上會啟動 devenv.com,即控制台。 這是因為.com通常在PATHEXT環境變量中列在.exe之前。

如果包含 devenv.com 可以在控制台模式下嚴格處理的命令行選項(如/? ),它會。 否則,它只會為您調用 devenv.exe 並退出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM