Windows Application - WNDCLASS etc.

Hi, I am new and been learning to make a basic Window but have a question I like to ask related to function oriented programming.

1) The below code is me trying to figure out how to make everything more cleaner and neater than to have one long code in WinMain. So I tried creating functions for each step process but I’m not sure what function parameters I should have and what should be in the WinMain scope and if I went about doing it the right way?

What would be the correct way to do this so that everything will be compatible and work together if needed?

Also if there are some rules when creating functions to make cleaner code please share some tips. Thanks.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
initWindowClass(hInstance);
SetupWindow(hInstance);
}


==========================================================

void initWindowClass(HINSTANCE hInstance)
{
WNDCLASS wndClass = {};
wndClass.style = CS_DROPSHADOW | CS_HREDRAW | CS_VREDRAW;
wndClass.hInstance = hInstance;
wndClass.hIcon = LoadIcon(0, IDI_APPLICATION);
wndClass.hCursor = LoadCursor(0, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
wndClass.lpszClassName = L”WINDOWCLASS”;
wndClass.lpszMenuName = L”MENU NAME”;
wndClass.lpfnWndProc = WindowProcedure;

RegisterClass(&wndClass);
}


=====================================================================================
/// not sure what is correct to have HWND declare in here or in winmain? ///

void SetupWindow(HINSTANCE hInstance)
{

HWND hWnd = CreateWindow(L”WINDOWCLASS”, L”My Application”, WS_OVERLAPPEDWINDOW, WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 400, 450, 0, 0, hInstance, 0);

}


=====================================================================================
/// not sure how to get HWND from the setupwindow function to this function? ///

void MessageLoop(HWND hWnd)
{

MSG msg;

}
Last edited on
This is more appropriate for the Windows Programming forum, I suggest you move it there by editing the topic. Once moved then we can talk.
Are you using VS? If yes and you create a new project using the Windows Desktop Application template, then all the code required for a minimal Windows program is automatically provided.
Hello. What is your goal in creating a window? Some parameters can be occulted when some are required. For my 2d engine I use many parameters so as to switch in fullscreen mode, Vsync, keyboard input... I guess that only the half of these parameters are required and a simpler application does not need them. Take a look at my function. Maybe useful for you ++
PS : I use Visual Studio.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
HWND xGameEngine::gck_WindowCreate()
{
	WNDCLASS wc;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);		 // no specific icon
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);		 // classical
	wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // redraw available
	wc.hInstance = GetModuleHandle(nullptr);
	wc.lpfnWndProc = gck_WindowEvent;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.lpszMenuName = nullptr;
	wc.hbrBackground = nullptr;
	wc.lpszClassName = L"GECKOO_GAME_ENGINE";
	RegisterClass(&wc);
	nWindowWidth = (LONG)nScreenWidth * (LONG)nPixelWidth;
	nWindowHeight = (LONG)nScreenHeight * (LONG)nPixelHeight;
	// defines window features
	DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
	DWORD dwStyle = WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_THICKFRAME;
	// just an offset on screen when application doesn't work in fullscreen mode
	int nCosmeticOffset = 50;
	nViewW = nWindowWidth;
	nViewH = nWindowHeight;
	ShowCursor(bShowCursor); // hide or show cursor (boolean)
	// fullscreen (boolean)
	if (bFullScreen)
	{
		dwExStyle = 0;
		nCosmeticOffset = 0;
		dwStyle = WS_VISIBLE | WS_POPUP;
		HMONITOR hmon = MonitorFromWindow(gck_hWnd, MONITOR_DEFAULTTONEAREST);
		MONITORINFO mi = { sizeof(mi) };
		if (!GetMonitorInfo(hmon, &mi)) return NULL;
		nWindowWidth = mi.rcMonitor.right;
		nWindowHeight = mi.rcMonitor.bottom;
	}
	gck_UpdateViewport();
	// keep client size as requested
	RECT rWndRect = { 0, 0, nWindowWidth, nWindowHeight };
	AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle);
	int width = rWndRect.right - rWndRect.left;
	int height = rWndRect.bottom - rWndRect.top;
	gck_hWnd = CreateWindowEx(dwExStyle, L"GECKOO_GAME_ENGINE", L"", dwStyle, nCosmeticOffset, nCosmeticOffset, width, height, NULL, NULL, GetModuleHandle(nullptr), this);
	// keyboard mapping
	mapKeys[0x00] = Key::NONE; 
	mapKeys[0x41] = Key::A; mapKeys[0x42] = Key::B; mapKeys[0x43] = Key::C; mapKeys[0x44] = Key::D; mapKeys[0x45] = Key::E; mapKeys[0x46] = Key::F; 
	mapKeys[0x47] = Key::G; mapKeys[0x48] = Key::H; mapKeys[0x49] = Key::I; mapKeys[0x4A] = Key::J; mapKeys[0x4B] = Key::K; mapKeys[0x4C] = Key::L; 
	mapKeys[0x4D] = Key::M; mapKeys[0x4E] = Key::N; mapKeys[0x4F] = Key::O; mapKeys[0x50] = Key::P; mapKeys[0x51] = Key::Q; mapKeys[0x52] = Key::R; 
	mapKeys[0x53] = Key::S; mapKeys[0x54] = Key::T; mapKeys[0x55] = Key::U; mapKeys[0x56] = Key::V; mapKeys[0x57] = Key::W; mapKeys[0x58] = Key::X; 
	mapKeys[0x59] = Key::Y; mapKeys[0x5A] = Key::Z; 
	// F keys
	mapKeys[VK_F1] = Key::F1; mapKeys[VK_F2] = Key::F2; mapKeys[VK_F3] = Key::F3; mapKeys[VK_F4] = Key::F4; mapKeys[VK_F5] = Key::F5; mapKeys[VK_F6] = Key::F6; 
	mapKeys[VK_F7] = Key::F7; mapKeys[VK_F8] = Key::F8; mapKeys[VK_F9] = Key::F9; mapKeys[VK_F10] = Key::F10; mapKeys[VK_F11] = Key::F11; mapKeys[VK_F12] = Key::F12;
	mapKeys[VK_DOWN] = Key::DOWN; mapKeys[VK_LEFT] = Key::LEFT; mapKeys[VK_RIGHT] = Key::RIGHT; mapKeys[VK_UP] = Key::UP; mapKeys[VK_RETURN] = Key::ENTER;
	// special keys
	mapKeys[VK_BACK] = Key::BACK; mapKeys[VK_ESCAPE] = Key::ESCAPE; mapKeys[VK_RETURN] = Key::ENTER; mapKeys[VK_PAUSE] = Key::PAUSE; mapKeys[VK_SCROLL] = Key::SCROLL; 
	mapKeys[VK_TAB] = Key::TAB; mapKeys[VK_DELETE] = Key::DEL; mapKeys[VK_HOME] = Key::HOME; mapKeys[VK_END] = Key::END; mapKeys[VK_PRIOR] = Key::PGUP; 
	mapKeys[VK_NEXT] = Key::PGDN; mapKeys[VK_INSERT] = Key::INS; mapKeys[VK_SHIFT] = Key::SHIFT; mapKeys[VK_CONTROL] = Key::CTRL; mapKeys[VK_SPACE] = Key::SPACE; 
	mapKeys[VK_OEM_PERIOD] = Key::PERIOD; mapKeys[VK_OEM_COMMA] = Key::COMMA;

	mapKeys[0x30] = Key::K0; mapKeys[0x31] = Key::K1; mapKeys[0x32] = Key::K2; mapKeys[0x33] = Key::K3; mapKeys[0x34] = Key::K4;
	mapKeys[0x35] = Key::K5; mapKeys[0x36] = Key::K6; mapKeys[0x37] = Key::K7; mapKeys[0x38] = Key::K8; mapKeys[0x39] = Key::K9;
	// numpad keys
	mapKeys[VK_NUMPAD0] = Key::NP0; mapKeys[VK_NUMPAD1] = Key::NP1; mapKeys[VK_NUMPAD2] = Key::NP2; mapKeys[VK_NUMPAD3] = Key::NP3; mapKeys[VK_NUMPAD4] = Key::NP4;
	mapKeys[VK_NUMPAD5] = Key::NP5; mapKeys[VK_NUMPAD6] = Key::NP6; mapKeys[VK_NUMPAD7] = Key::NP7; mapKeys[VK_NUMPAD8] = Key::NP8; mapKeys[VK_NUMPAD9] = Key::NP9;
	mapKeys[VK_MULTIPLY] = Key::NP_MUL; mapKeys[VK_ADD] = Key::NP_ADD; mapKeys[VK_DIVIDE] = Key::NP_DIV; mapKeys[VK_SUBTRACT] = Key::NP_SUB; mapKeys[VK_DECIMAL] = Key::NP_DECIMAL;

	return gck_hWnd;
}
Last edited on
@copypasta, the MessageLoop function doesn't require any parameters.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// INCLUDES ====================================================================
#include	<windows.h>

// FUNCTION PROTOTYPES =========================================================
BOOL             InitApplication(HINSTANCE);
BOOL             InitInstance(HINSTANCE, int);
int              MessageLoop();
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// GLOBALS =====================================================================
// (sometimes a global makes things easier than passing as a local parameter, YMMV)
const WCHAR szWinName[]  = L"WINMOD";
const WCHAR szAppTitle[] = L"Modular WinAPI Minimal Application";

// Visual Studio gets all warning whingy if WinMain ain't SALed up the wazoo.
// 
// SAL: https://docs.microsoft.com/en-us/cpp/code-quality/understanding-sal?view=msvc-170
//
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
                    _In_ PWSTR szCmdLine, _In_ int nWinMode)
{
   if (InitApplication(hInstance) == FALSE)
   {
      return E_FAIL;
   }

   if (InitInstance(hInstance, nWinMode) == FALSE)
   {
      return E_FAIL;
   }

   return MessageLoop();
}

// initializes the window class data and registers the main window class
BOOL InitApplication(HINSTANCE hInstance)
{
   WNDCLASSW wc;

   wc.style         = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc   = WndProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = hInstance;
   wc.hIcon         = (HICON)   LoadImageW(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
   wc.hCursor       = (HCURSOR) LoadImageW(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
   wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = szWinName;

   if (RegisterClassW(&wc) == 0)
   {
      MessageBoxW(NULL, L"Can't Register the Window Class!", szWinName, MB_OK | MB_ICONERROR);
      return FALSE;
   }
   else
   {
      return TRUE;
   }
}

// initializes the instance data and creates a new window for each instance
BOOL InitInstance(HINSTANCE hInstance, int nWinMode)
{
   HWND hwnd = CreateWindowW(szWinName, szAppTitle,
                            WS_OVERLAPPEDWINDOW,
                            CW_USEDEFAULT, CW_USEDEFAULT,
                            CW_USEDEFAULT, CW_USEDEFAULT,
                            NULL, NULL, hInstance, NULL);

   if (hwnd == NULL)
   {
      MessageBoxW(NULL, L"Can't Create the Main Window!", szWinName, MB_OK | MB_ICONERROR);
      return FALSE;
   }

   ShowWindow(hwnd, nWinMode);
   UpdateWindow(hwnd);

   return TRUE;
}

// starts and runs the application's message loop
int MessageLoop()
{
   MSG msg;

   while (GetMessageW(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessageW(&msg);
   }

   return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   const WCHAR szAboutLeft[]  = L"This is a minimal modular WinAPI application.\nYou've pressed the left mouse button!";
   const WCHAR szAboutRight[] = L"This is a minimal modular WinAPI application.\nYou've pressed the right mouse button!";

   switch (message)
   {
   case WM_LBUTTONDOWN:
      MessageBeep(MB_ICONEXCLAMATION);
      MessageBoxW(hwnd, szAboutLeft, L"About", MB_OK | MB_ICONINFORMATION);
      return 0;

   case WM_RBUTTONDOWN:
      MessageBeep(MB_ICONASTERISK);
      MessageBoxW(hwnd, szAboutRight, L"About", MB_OK | MB_ICONINFORMATION);
      return 0;

   case WM_DESTROY:
      PostQuitMessage(0);
      return 0;
   }

   return DefWindowProcW(hwnd, message, wParam, lParam);
}
Using the modular approach makes splitting a single source file into multiple header/source files easy to do:
modular.h:
1
2
3
4
5
6
7
8
9
10
11
#ifndef MODULAR_H
#define MODULAR_H

#include <windows.h>

BOOL             InitApplication(HINSTANCE);
BOOL             InitInstance(HINSTANCE, int);
int              MessageLoop();
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

#endif 

modular.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "modular.h"

const WCHAR szWinName[]  = L"WINMOD";
const WCHAR szAppTitle[] = L"Modular WinAPI Minimal Application";

BOOL InitApplication(HINSTANCE hInstance)
{
   WNDCLASSW wc;

   wc.style         = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc   = WndProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = hInstance;
   wc.hIcon         = (HICON)   LoadImageW(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
   wc.hCursor       = (HCURSOR) LoadImageW(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
   wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = szWinName;

   if (RegisterClassW(&wc) == 0)
   {
      MessageBoxW(NULL, L"Can't Register the Window Class!", szWinName, MB_OK | MB_ICONERROR);
      return FALSE;
   }
   else
   {
      return TRUE;
   }
}

BOOL InitInstance(HINSTANCE hInstance, int nWinMode)
{
   HWND hwnd = CreateWindowW(szWinName, szAppTitle,
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             NULL, NULL, hInstance, NULL);

   if (hwnd == NULL)
   {
      MessageBoxW(NULL, L"Can't Create the Main Window!", szWinName, MB_OK | MB_ICONERROR);
      return FALSE;
   }

   ShowWindow(hwnd, nWinMode);
   UpdateWindow(hwnd);

   return TRUE;
}

int MessageLoop()
{
   MSG msg;

   while (GetMessageW(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessageW(&msg);
   }

   return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   const WCHAR szAboutLeft[]  = L"This is a minimal modular WinAPI application.\nYou've pressed the left mouse button!";
   const WCHAR szAboutRight[] = L"This is a minimal modular WinAPI application.\nYou've pressed the right mouse button!";

   switch (message)
   {
   case WM_LBUTTONDOWN:
      MessageBeep(MB_ICONEXCLAMATION);
      MessageBoxW(hwnd, szAboutLeft, L"About", MB_OK | MB_ICONINFORMATION);
      return 0;

   case WM_RBUTTONDOWN:
      MessageBeep(MB_ICONASTERISK);
      MessageBoxW(hwnd, szAboutRight, L"About", MB_OK | MB_ICONINFORMATION);
      return 0;

   case WM_DESTROY:
      PostQuitMessage(0);
      return 0;
   }

   return DefWindowProcW(hwnd, message, wParam, lParam);
}

winmain.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "modular.h"

int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
                    _In_ PWSTR szCmdLine, _In_ int nWinMode)
{
   if (InitApplication(hInstance) == FALSE)
   {
      return E_FAIL;
   }

   if (InitInstance(hInstance, nWinMode) == FALSE)
   {
      return E_FAIL;
   }

   return MessageLoop();
}
Another approach for modularizing WinAPI Desktop code is to use "message crackers." Replaces the switch cases with functions.
msgcrk.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef  MSGCRK_H
#define  MSGCRK_H

#include <windows.h>
#include <windowsx.h>

// handles left mouse button presses
void OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);

// handles right mouse button presses
void OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);

// handles WM_DESTROY messages
void OnDestroy(HWND hwnd);

#endif 

msgcrk.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "msgcrk.h"

// handles WM_LBUTTONDOWN messages
void OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
   WCHAR szAboutLeft[] = L"This is a minimal modular WinAPI application.\nYou've pressed the left mouse button!";

   MessageBeep(MB_ICONEXCLAMATION);
   MessageBoxW(hwnd, szAboutLeft, L"About", MB_OK | MB_ICONINFORMATION);
}

// handles WM_RBUTTONDOWN messages
void OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
   WCHAR szAboutRight[] = L"This is a minimal modular WinAPI application.\nYou've pressed the right mouse button!";

   MessageBeep(MB_ICONASTERISK);
   MessageBoxW(hwnd, szAboutRight, L"About", MB_OK | MB_ICONINFORMATION);
}

// handles WM_DESTROY messages
void OnDestroy(HWND hwnd)
{
   PostQuitMessage(0);
}

The revised modular.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include "modular.h"
#include "msgcrk.h"

const WCHAR szWinName[]  = L"WINMOD";
const WCHAR szAppTitle[] = L"Modular WinAPI Minimal Application";

BOOL InitApplication(HINSTANCE hInstance)
{
   WNDCLASSW wc;

   wc.style         = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc   = WndProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = hInstance;
   wc.hIcon         = (HICON)   LoadImageW(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
   wc.hCursor       = (HCURSOR) LoadImageW(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
   wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = szWinName;

   if (RegisterClassW(&wc) == 0)
   {
      MessageBoxW(NULL, L"Can't Register the Window Class!", szWinName, MB_OK | MB_ICONERROR);
      return FALSE;
   }
   else
   {
      return TRUE;
   }
}

BOOL InitInstance(HINSTANCE hInstance, int nWinMode)
{
   HWND hwnd = CreateWindowW(szWinName, szAppTitle,
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             NULL, NULL, hInstance, NULL);

   if (hwnd == NULL)
   {
      MessageBoxW(NULL, L"Can't Create the Main Window!", szWinName, MB_OK | MB_ICONERROR);
      return FALSE;
   }

   ShowWindow(hwnd, nWinMode);
   UpdateWindow(hwnd);

   return TRUE;
}

int MessageLoop()
{
   MSG msg;

   while (GetMessageW(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessageW(&msg);
   }

   return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   switch (message)
   {
      HANDLE_MSG(hwnd, WM_LBUTTONDOWN, OnLButtonDown);
      HANDLE_MSG(hwnd, WM_RBUTTONDOWN, OnRButtonDown);
      HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
   }

   return DefWindowProcW(hwnd, message, wParam, lParam);
}

modular.h and winmain.cpp remain the same.

FYI, this approach is similar to how MFC does windows messaging.

No matter what approach someone adopts having generic "boilerplate" WinAPI Desktop code you drop into a new project and modify gives IMO better flexibility than letting VS generate the base code. It adds in potentially unneeded files.
@Geckoo, your exuberance and enthusiasm for your custom library is clearly evident, and I can appreciate that, but come on! Posting that very incomplete and very complex code is like using an entire ICBM arsenal to swat a gnat. That is not what the OP was asking or looking for.

Learning how to program basic GUI apps using the WinAPI Desktop model is complicated enough already.
I mentioned MFC earlier, here's a minimalist MFC app similar to the WinAPI Desktop versions:
MFCApp.hpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#pragma once

// compile for Windows 7 or higher
// #define WINVER       0x0601
// #define _WIN32_WINNT 0x0601

// compiling for Windows 10 (recommended)
#define WINVER       0x0A00
#define _WIN32_WINNT 0x0A00

#include <afxwin.h>

// application class
class CTheApp : public CWinApp
{
public:
   BOOL InitInstance();
};

// main window class
class CMainWnd : public CFrameWnd
{
public:
   CMainWnd();

protected:
   afx_msg void OnPaint();
   afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
   afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
   
   DECLARE_MESSAGE_MAP()
};

MFCApp.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include "MFCApp.hpp"

// instantiate the application
CTheApp theApp;

// construct a window
CMainWnd::CMainWnd()
{
   Create(NULL, L"A Minimal MFC Application");
}

// initalize the application
BOOL CTheApp::InitInstance()
{
   m_pMainWnd = new CMainWnd;

   m_pMainWnd->ShowWindow(m_nCmdShow);
   m_pMainWnd->UpdateWindow();

   return TRUE;
}

// application's message map
BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd)
   ON_WM_PAINT()
   ON_WM_LBUTTONDOWN()
   ON_WM_RBUTTONUP()
END_MESSAGE_MAP()

void CMainWnd::OnPaint()
{
   CRect    rect;

   GetClientRect(&rect);

   CPaintDC dc(this);

   dc.DrawTextW(L"Hello, MFC!", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}

void CMainWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
   CString MsgCoord;
   MsgCoord.Format(L"Left Button at P(%d, %d)", point.x, point.y);
   MessageBoxW(MsgCoord);
}

void CMainWnd::OnRButtonUp(UINT nFlags, CPoint point)
{
   MessageBoxW(L"Right Mouse Button Up");
}

MFC hides a lot of the nitty-gritty implementation details seen in WinAPI code in framework C++ classes an MFC app inherits from.
If you choose a dialog based MFC from the wizard and use CFormView you even have a GUI designer to create your user interface.
Hmmm I see some have suggested MFC which I will try later down the road. Right now I am still learning basics first with the Window api using functional programming style.

My main question is how can I make functions for long code and make it neater in WinMain. As my example code I have created functions for every new step. But I have a hard time understanding what function parameters to declare so that everything will work with each other.

Also for HANDLE, HWND and the WNDCLASS structure should they be declared in the WinMain scope or in created functions? What are the rules to this?


I appreciate everyone’s input.
Last edited on
> how can I make functions for long code and make it neater in WinMain.

Here is such a minimalist program.

It has a main(), not a WinMain(); so create it as a console application.
This makes std::cout (used for displaying debug messages) available.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <string>
#include <windows.h>
#include <iostream>

inline HINSTANCE hinstance() { return ::GetModuleHandle( nullptr ) ; }

bool register_window_class( const std::string& name, WNDPROC window_proc )
{
	WNDCLASSA wc {} ;
	wc.style = CS_DROPSHADOW | CS_HREDRAW | CS_VREDRAW ;
	wc.lpszClassName = name.c_str() ;
	wc.lpfnWndProc = window_proc ;
	wc.hInstance = hinstance() ;
	wc.hIcon = ::LoadIcon( 0, IDI_APPLICATION );
	wc.hCursor = LoadCursor( 0, IDC_ARROW );
	wc.hbrBackground = HBRUSH( COLOR_WINDOW+1) ;
	return ::RegisterClassA( std::addressof( wc ) ) ;
}

LRESULT CALLBACK window_proc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
		case WM_CREATE:
		{
			std::cout << "recd WM_CREATE message for window " << hwnd << '\n' ;
			return 0 ;
		}

		case WM_CLOSE: 
		{
			std::cout << "recd WM_CLOSE message\n" ;
			return ::DestroyWindow( hwnd ) ? 0 : 1 ;
		}

        case WM_DESTROY: 
		{
			std::cout << "recd WM_DESTROY message. posting a WM_QUIT message\n" ;
			::PostQuitMessage( 0 ) ;
			return 0 ;
		}

        default: return DefWindowProc( hwnd, msg, wParam, lParam );
    }

}

HWND create_window( const std::string& wclass, const std::string& title, int width = CW_USEDEFAULT, int height = CW_USEDEFAULT )
{
	const auto hwnd =  ::CreateWindowA( wclass.c_str(), title.c_str(), WS_OVERLAPPEDWINDOW,
			       				        CW_USEDEFAULT, CW_USEDEFAULT, width, height,
							            nullptr, nullptr, hinstance(), 0 ) ;
	if( hwnd ) { ::ShowWindow( hwnd, SW_SHOWNORMAL ) ; ::UpdateWindow( hwnd ) ; }
	return hwnd ;
}

int message_loop()
{
	::MSG msg ;
	while( ::GetMessage( std::addressof(msg), nullptr, 0, 0 ) )
	{
		::TranslateMessage( std::addressof( msg ) ) ;
		::DispatchMessage( std::addressof( msg ) ) ;
	}
	std::cout << "recd WM_QUIT message\n" ;
	return int( msg.wParam );
}


int main()
{
	const std::string wclass_name = "my_window_class" ;

	if( register_window_class( wclass_name, window_proc ) )
	{
		std::cout << "registered window class\n" ;
		const HWND hwnd = create_window( wclass_name, "my first window" ) ;
		std::cout << "hwnd == " << hwnd << '\n' ;
		if( hwnd ) return message_loop() ;
	}   

	return 1 ;
}
@Geckoo, your exuberance and enthusiasm for your custom library is clearly evident, and I can appreciate that, but come on! Posting that very incomplete and very complex code is like using an entire ICBM arsenal to swat a gnat. That is not what the OP was asking or looking for.

Learning how to program basic GUI apps using the WinAPI Desktop model is complicated enough already.


Right. I am sorry for the inconvenience. If you start creating a Windows application (using Visual Studio), you can embed needed headers - so you can create a window quickly. I show a simpler window using some basic parameters. It is similar to the first one that George P posted, but more concise in order to simplify your reading. I hope that it can help you. It display a grey background. Take a look at the links inside the code so as to get all informations about these parameters. Useful ++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <windows.h>

const char g_szClassName[] = "window";
// window process and its message queue
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEXW wc;
    HWND hwnd;
    MSG Msg;
    // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassexw
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(128, 128, 128)); // background color
    wc.lpszMenuName = NULL;
    wc.lpszClassName = (LPCWSTR)g_szClassName;
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    // something goes wrong here!
    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"Window Registration Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }
    // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw
    hwnd = CreateWindowExW(
        WS_EX_CLIENTEDGE,
        (LPCWSTR)g_szClassName,
        L"myWindow",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 320, 200,
        NULL,
        NULL,
        hInstance,
        NULL);
    // something goes wrong here!
    if (hwnd == NULL)
    {
        MessageBox(NULL, L"Window Creation Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage
    while (GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }

    return Msg.wParam;
}
Last edited on
Yes - but the OP question was about breaking this down into separate functions - specifically winmain() - and what params should be used and what (if any) global variables should be used for the main window, instance etc.


@Geckoo (and @copypasta), why do you use WinAPI functions that have been superseded? LoadIcon and LoadCursor are not recommended, LoadImage is what should be used now.

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadiconw

See my code examples for usage.

There is no guarantee superseded WinAPI functions won't be deprecated and possibly removed in later WinAPI standards.

Also recommended now is explicitly using Unicode exclusively with new Windows apps. If a WinAPI data type can be either ANSI or Unicode use the Unicode (W) version. MessageBoxW instead of MessageBox. WCHAR/whar_t instead of CHAR/char.

If you want to use LoadIcon and LoadCursor use LoadIconW and LoadCursorW.

M'ok, there are instances where using ANSI works and might be a better choice, such as reading a text file. By using the A version and a C string char array the code self-documents what is happening.

And should ANSI vs. Unicode issues crop up using the explicit version of a WinAPI data type or function is a relatively easy fix that is easy to spot.

For that matter MS might decide creating apps using the ancient yet very useful WinAPI Desktop C-based model is no longer supported and now using C#/.NET framework is how things are done.

Charles Petzold's "Programming Windows, Fifth Edition" (published 1998) is all about the old and familiar WinAPI Desktop C based model geared to Win9X and early WinNT. His sixth edition, published in 2013, is Win8 using C# and XAML.
Topic archived. No new replies allowed.