Start the first Program
Brief Summary
This topic covers day001 to day002.
We would first introduce the basical use of windows programming and create a window.
Then we will get to show something on the Screen.
Two basic functions
WinMain
WinMain is the conventional name used for the application entry point. You can use WinMain to initialize the application, display its main window etc.
int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
);About the _In_, It’s a SAL annotation. You can use it to describe how a funtion uses its parameters. For _In_, this indicates that this parameter is used for input. Correspondingly , there is a _Out_ indicates that it is used for output.
HINSTANCE means a handle to the instance of the application. It can alternatively be obtained from the GetModuleHandle API function. So there are two parameters, one is hInstance, meaning a handle to the current instance, we have to pass this to winMain so that we can get to do something to it.
hPrevInstance ,meaning the previous instance, is always NULL.
lpCmdLine is the command line for the application, excluding the program name. To retrieve the entire command line, use the GetCommandLine function.
nCmdShow Controls how the window is to be shown.
Window procedures
In addition to WinMain function, every Windows desktop application must also have a window-procedure function. This function is typically named WndProc.In our program, we named it MainWindowCallback.
LRESULT CALLBACK
MainWindowCallback(
HWND Window,
UINT Message,
WPARAM Wparam,
LPARAM LParam);In this function you write code to handle messages that the application receives from Windows when events occur.For example, if a user clicks an OK button in your application, Windows will send a message to you and you can write code inside your WndProc function that does whatever work is appropriate. This is called handling an event. You only handle the events that are relevant for your application.You may find full explanation here.
Thus ,now we have two functions to write:
LRESULT CALLBACK
MainWindowCallback(
HWND Window,
UINT Message,
WPARAM Wparam,
LPARAM LParam)
{
//deals with events
}
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//deals with some information about the window
}Add funtionality to the WinMain function
WNDCLASS
In the winMain, we populate a structure of type WNDCLASS(You may also use WNDCLASSEX).
typedef struct tagWNDCLASS {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS, *PWNDCLASS;A typical WNDCLASS structure will looks like this:
WNDCLASS WindowClass = {};
WindowClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
WindowClass.lpfnWndProc = MainWindowCallback;
WindowClass.hInstance = hInstance;
WindowClass.lpszClassName = "HandmadeHeroWindowClass";The first line above takes the default initialization of WINDCLASS.
You must register the WINDCLASS with Windows so that it knows about your window and how to send message to it. So now here comes the RegisterClass ( RegisterClassEx )function.
So here comes a typical way of dealing with this:
if (!RegisterClassEx(&WindowClass))
{
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
} Now we can create a Window. Use the CreateWindowEx function.
HWND WindowHandle = CreateWindowEx(
0,
WindowClass.lpszClassName,
"Handmade Hero",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
hInstance,
0);This function returns an HWND, which is a handle to a window. A handle is somewhat like a pointer that Windows uses to keep track of a window.
At this point, the window has been created, we try to Handle the messages.
if (WindowHandle) {
for (;;) {
MSG Message;
BOOL MessageResult = GetMessage(&Message, 0, 0, 0);
if (MessageResult > 0)
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
else
{
break;
}
}
}Casey defined a infinite loop. But in my opinion, doing this like the following may be more concise:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} For more information about this loop, you can see MSG, GetMessage, TranslateMessage, and DispatchMessage.
Add funtionality to the MainWindowCallback function
Recall that the MainWindowCallback function (WindowProc callback function)is used to handle the messages the application receives.We implement it as a switch statement.
We define the function like this:
LRESULT CALLBACK
MainWindowCallback(HWND Window,
UINT Message,
WPARAM Wparam,
LPARAM LParam)
{
//switch statement
}As its literal meaning, we have to focus on the Message parameter.This is what we have just got from the GetMessage().
One important message to handle is the WM_PAINT message.This application receives this message when part of its displayed window must be updated. to handle a WM_PAINT message, first call BeginPaint,then handle all the logic to lay out the text, buttons and other controls in the window. After painting, call EndPaint.
case WM_PAINT:
{
PAINTSTRUCT Paint;
HDC DeviceContext = BeginPaint(Window, &Paint);
//Some operations
EndPaint(Window, &Paint);
}break;HDC is a handle to a device context, which is a data structure that Windows uses to enable your application to communicate with the graphics subsystem.
If we want to show something on screen, we have to use the function PatBlt.This function paints the specified rectangle using the brush.Then the code is like this:
case WM_PAINT:
{
PAINTSTRUCT Paint;
HDC DeviceContext = BeginPaint(Window, &Paint);
int X = Paint.rcPaint.left;
int Y = Paint.rcPaint.top;
int Height = Paint.rcPaint.bottom - Paint.rcPaint.top;
int Width = Paint.rcPaint.right - Paint.rcPaint.left;
static DWORD Operation = WHITENESS;
PatBlt(DeviceContext,X,Y,Width,Height, Operation);
if (Operation == WHITENESS)
{
Operation = BLACKNESS;
}
else
{
Operation = WHITENESS;
}
EndPaint(Window, &Paint);
}break;An application typically handles many other messages, for example ,WM_CREATE when the window is created.WM_DESTROYwhen a window is being destroyed.
One another important thing is we have to define a default operation for our switch statement:
default:
Result = DefWindowProc(Window,Message,Wparam,LParam);
break;Notice that you have to pass all other things to DefWindowProc.If you omit DefWindowProc then you are saying “For all messages I did not handle above, do nothing.” Which means that a lot of messages like “Please draw the buttons” get handled as “do nothing.” Result: No buttons.(details here).
By now we have reached the day 002. You may find the day002.cpp in history folder, and thank you for your reading.