windows下控制檯程序實現窗口顯示

windows下實現窗口顯示,若是限定是C/C++語言,而且是原生Windows支持,須要使用GDI或GDI+。通常是在Visual Studio裏新建Win32應用程序,而不是Win32 console application(控制檯程序),而後使用GDI的一些API,例如CreateWindow、CreateWindowEx來實現窗口的建立、顯示等。c++

實際上MSDN官方並無說只能是Win32應用程序,控制檯程序徹底能夠。我的並不習慣WinMain()函數,更習慣main(int argc, char** argv)。例如OpenCV的highgui模塊,它裏面的cvNamedWindow(), cvShowImage(), cvWaitKey()這幾個函數,在windows下的實現,若是不考慮其opengl版本的話,它是基於GDI實現的;而做爲一個普遍使用的圖像處理庫,OpenCV源碼中並無WinMain函數。windows

第1種實現

下面是用純C實現的「調用GDI的API接口,實現控制檯類型程序中顯示窗口」的樣例程序,改編自How do you create a Win32 window from Console Application?app

#include <stdio.h>
#include <windows.h>

LRESULT __stdcall WindowProcedure(HWND window, unsigned int msg, WPARAM wp, LPARAM lp)
{
    switch(msg)
    {
        case WM_DESTROY:
            printf("\ndestroying window\n");
            PostQuitMessage(0) ;
            return 0L ;

        case WM_LBUTTONDOWN:
            printf("\nmouse left button down at (%d, %d)\n", LOWORD(lp), HIWORD(lp));

        // fall thru
        default:
            printf(".");
            return DefWindowProc( window, msg, wp, lp ) ;
    }
}

typedef struct MyRect{
    int x, y, width, height;
} MyRect;

int main()
{
    printf("hello world!\n");

    const char* const myclass = "myclass" ;

    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    /* Win 3.x */
    wc.style = CS_DBLCLKS;
    wc.lpfnWndProc = WindowProcedure;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandle(0);
    wc.hIcon = LoadIcon(0, IDI_APPLICATION);
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = 0;
    wc.lpszClassName = myclass;
    /* Win 4.0 */
    wc.hIconSm = LoadIcon(0, IDI_APPLICATION);

    if(RegisterClassEx(&wc))
    {
        MyRect rect;
        rect.x = 300;
        rect.y = 300;
        rect.width = 640;
        rect.height = 480;

        HWND window = CreateWindowEx( 0, myclass, "title",
                   WS_OVERLAPPEDWINDOW, rect.x, rect.y,
                   rect.width, rect.height, 0, 0, GetModuleHandle(0), 0 ) ;

        if(window)
        {
            ShowWindow( window, SW_SHOWDEFAULT ) ;
            MSG msg ;
            while( GetMessage( &msg, 0, 0, 0 ) ) DispatchMessage(&msg);
        }
    }
}

第2種實現

實際上,在Visual Studio中經過「菜單欄」->"文件"->「新建項目」->"Win32項目"(或"Win32控制檯項目"進入後再選「Windows應用程序」),建立的「Hello World」工程提供的代碼也僅僅是「顯示一個窗口」,只不過入口函數是WinMain,其內容以下:函數

// gdi_show_png.cpp : 定義應用程序的入口點。
//

#include "stdafx.h"
#include "gdi_show_png.h"

#define MAX_LOADSTRING 100

// 全局變量: 
HINSTANCE hInst;                                // 當前實例
WCHAR szTitle[MAX_LOADSTRING];                  // 標題欄文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口類名

// 此代碼模塊中包含的函數的前向聲明: 
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此放置代碼。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_GDI_SHOW_PNG, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 執行應用程序初始化: 
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GDI_SHOW_PNG));

    MSG msg;

    // 主消息循環: 
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  函數: MyRegisterClass()
//
//  目的: 註冊窗口類。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GDI_SHOW_PNG));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_GDI_SHOW_PNG);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函數: InitInstance(HINSTANCE, int)
//
//   目的: 保存實例句柄並建立主窗口
//
//   註釋: 
//
//        在此函數中,咱們在全局變量中保存實例句柄並
//        建立和顯示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 將實例句柄存儲在全局變量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函數: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    處理主窗口的消息。
//
//  WM_COMMAND  - 處理應用程序菜單
//  WM_PAINT    - 繪製主窗口
//  WM_DESTROY  - 發送退出消息並返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜單選擇: 
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此處添加使用 hdc 的任何繪圖代碼...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// 「關於」框的消息處理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

而若是忽略WinProc函數(各類事件處理代碼),不妨把前面從gamedev上的帖子中的例子進行改造,把各類步驟都拆分到函數中,在主函數中調用,就獲得以下代碼:ui

#include <stdio.h>
#include <windows.h>

LRESULT __stdcall WindowProcedure(HWND window, unsigned int msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
    case WM_DESTROY:
        printf("\ndestroying window\n");
        PostQuitMessage(0);
        return 0L;

    case WM_LBUTTONDOWN:
        printf("\nmouse left button down at (%d, %d)\n", LOWORD(lp), HIWORD(lp));

        // fall thru
    default:
        printf(".");
        return DefWindowProc(window, msg, wp, lp);
    }
}

const char* szWindowClass = "myclass";

typedef struct MyRect {
    int x, y, width, height;
} MyRect;

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wc;

    wc.cbSize = sizeof(WNDCLASSEX);
    /* Win 3.x */
    wc.style = CS_DBLCLKS;
    wc.lpfnWndProc = WindowProcedure;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(0, IDI_APPLICATION);
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = 0;
    wc.lpszClassName = szWindowClass;
    /* Win 4.0 */
    wc.hIconSm = LoadIcon(0, IDI_APPLICATION);

    return RegisterClassEx(&wc);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    MyRect rect;
    rect.x = 300;
    rect.y = 300;
    rect.width = 640;
    rect.height = 480;

    HWND hwnd = CreateWindowEx(0, szWindowClass, "title",
        WS_OVERLAPPEDWINDOW, rect.x, rect.y,
        rect.width, rect.height, 0, 0, GetModuleHandle(0), 0);

    if (!hwnd)
    {
        return FALSE;
    }
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    return TRUE;
}


int main()
{
    printf("hello world!\n");

    HINSTANCE hInstance = GetModuleHandle(0);
    int nCmdShow = SW_SHOWDEFAULT;

    MyRegisterClass(hInstance);
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }
        
    MSG msg;
    while (GetMessage(&msg, 0, 0, 0))
    {
        DispatchMessage(&msg);
    }
}

對比一下發現,其實main()WinMain()分別做爲入口的差異,僅僅在於hInstance函數和nCmdShow參數有所不一樣:WinMain()函數中是這兩個參數做爲參數傳入,而main()函數中則手動賦予這兩個參數初值:spa

HINSTANCE hInstance = GetModuleHandle(0);
int nCmdShow = SW_SHOWDEFAULT;.net

相關文章
相關標籤/搜索