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


windows下實現窗口顯示,如果限定是C/C++語言,並且是原生Windows支持,需要使用GDI或GDI+。一般是在Visual Studio里新建Win32應用程序,而不是Win32 console application(控制台程序),然后使用GDI的一些API,例如CreateWindow、CreateWindowEx來實現窗口的創建、顯示等。

實際上MSDN官方並沒有說只能是Win32應用程序,控制台程序完全可以。個人並不習慣WinMain()函數,更習慣main(int argc, char** argv)。例如OpenCV的highgui模塊,它里面的cvNamedWindow(), cvShowImage(), cvWaitKey()這幾個函數,在windows下的實現,如果不考慮其opengl版本的話,它是基於GDI實現的;而作為一個廣泛使用的圖像處理庫,OpenCV源碼中並沒有WinMain函數。

第1種實現

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

#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上的帖子中的例子進行改造,把各種步驟都拆分到函數中,在主函數中調用,就得到如下代碼:

#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()函數中則手動賦予這兩個參數初值:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM