04-Building a Win32API Application

advertisement
Building a Win32API Application
Raw access to the API
Resources
• The resource script (.rc file):
– separate from code and dynamic data
– compiled by a separate "Resource Compiler”
• Resources determine the app’s “look and feel”
• Examples:
– Keyboard Accelerators, Bitmaps, Cursors, Dialog Box values,
Fonts, Icons, Menus, String Tables
• Separation of resources and program code
– separates tasks of programmer & designer
– can change user interface without touching code
Message Processing
User input
(mouse,
keyboard)
interrupt
OS
Msg loop:
GetMsg
DispatchMsg
msg
Message queue
msg
WndProc:
Evaluate msg,
take action
Basic program flow
– RegisterClassEx()
(you provide a pointer to a function (usually called WndProc)
which handles windows messages such as WM_CREATE,
WM_COMMAND, etc.
– CreateWindowEx()
• returns a handle (pointer) to that control.
– When the user clicks on a control you receive the
WM_COMMAND message with the ID of that control.
– Now handle that event. SetWindowText() and GetWindowText()
allow you to set/get the text of any control.
–
–
–
–
ShowWindow() – displays the window
UpdateWindow() – updates the buffer
Start the message loop
OS then calls WndProc() - Process O/S's messages
Message loop
MSG msg; // probably a global vbl
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg); // tell OS to call my WndProc
}
Notes:
1. the name of the window processor does NOT have
to be "WndProc"
2. The two 0's are filter max/min values
About msg loops
• O/S only creates a message queue for threads that
perform operations which require one.
• Must create a window before entering loop
• GetMessage blocks until a filter-matching msg arrives
See http://msdn.microsoft.com/enus/library/windows/desktop/ms644936(v=vs.85).aspx
for a discussion of filter max/minvalues
Windows Messages
• Two 32-bit integer values (DWORDs)
– WPARAM – 2 16-bit WORDs inside a DWORD
– LPARAM – a 32-bit DWORD
• High-order and low order 16 bit WORDs
• Extract each WORD with C macros
– HIWORD(msg) gets high order
– LOWORD(msg) gets low order
• Meanings of HI and LO depend on the
message
Win32API Programs
• WinMain (replaces "main")
– Primary entry point from OS
• Procedure definition
• Init
• Message processing loop
• WinProc (the "callback" function)
– Performs actions to process messages
• Switch statement based on msg#
• Reentered once for each message
– Pixel-by-pixel mouse move
– Mouse-click, etc.
LPARAM & WPARAM
• LPARAM & WPARAM are datatypes
• window message design:
– if the message takes a pointer,
• the pointer is usually passed in the LPARAM,
– if the message takes a handle or an integer,
• it is passed in the WPARAM
– if a message takes both,
• the pointer goes in the LPARAM
• the integer goes in the WPARAM
wParam & lParam
•
•
•
•
Not every message uses these WParam values
Each message uses them differently.
WM_CLOSE message doesn't use either, so ignore both.
WM_COMMAND message uses both,
– WParam contains two values,
• HIWORD (WParam) - notification message (if
applicable)
• LOWORD (WParam) - control or menu id that sent
the message.
• LParam - HWND (window handle) of control which sent the
message
– NULL if the message isn't from a control.
example
• Given, some function "fn", extract the string
from the parameter (which is a pointer)
LRESULT class::fn (WPARAM wParam, LPARAM lParam)
{ //two steps required
LPTSTR lpMessage = (LPTSTR) lParam; // get ptr to string
CString s = lpMessage; // put string into needed type
}
Caution
• Important For WM_LBUTTONDOWN
Do not use the LOWORD or HIWORD macros to
extract the x- and y- coordinates of the cursor
position because these macros return incorrect
results on systems with multiple monitors.
Systems with multiple monitors can have
negative x- and y- coordinates, and LOWORD
and HIWORD treat the coordinates as unsigned
quantities.
Sending messages
• Two ways to inform a handler
– PostMessage
• Enqueues message for handler
• Retrieved with GetMsg
• Immediate return to caller (it's a non-blocking call)
– SendMessage
• Sends message to handler (NO queueing)
• BLOCKS until message received!!
WndProc Processes System Messages
LRESULT CALLBACK WndProc (HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam)
Typical operations:
• If msg is WM_CREATE, call CreateWindow().
• If msg is WM_QUIT, terminate
• If msg is WM_PAINT, then redraw the window
Menus
When a menu item is selected
• Windows sends WM_COMMAND msg
• low word of wParam will be the item ID
• extract with LOWORD(wParam)
• then do
– switch/case using LOWORD(wParam) value
Menus-1
• Double-click your resource file's name (.rc)
• Click on menu/menu-name
• Enter names as needed
Menus-2
• Double-click on "Resource.h"
• Scroll down to your menu items
• Change the numeric values
– Increment to the next available integer
Misc
– Msg class specifies what kind of 'control' this
window is
e.g.; "edit" is a text box and "button" is a button.
– Each control (a window) must have a unique ID
– Use CString vs. STL string
– Remainder of STL is OK in all apps
– Note use of new datatypes, "_T", LPSTR
• Recommended: use _T() macro vs. char or wchar_t
• Use LPTSTR vs. char* or wchar_t*.
• Then set MBCS or _UNICODE & rebuild w/o code
changes.
Character strings
• Unicode
– 16 bit characters
– Allows for international alphabets/symbols
• ANSI (Windows-1252 = ASCII, ISO-8859)
• String declaration types
– "mystring" will use ANSI characters only
– L"mystring" will use Unicode characters only
– _T("mystring")
• Unicode if #define _UNICODE
• ANSI if not
Pointers to strings
•
•
•
•
•
DO NOT use char* or wchar*
Do use TCHAR* or LPTSTR
LPCTSTR is for usage like: const char*
Replace strcpy with _tcscpy
To convert a byte-buffer to character-size
buffer, use: bufflen/sizeof(TCHAR)
• pWnd->GetWIndowText(buffer,
sizeof(buffer) / sizeof(TCHAR))
Callback skeleton for Win32API (for writing text)
LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{ PAINTSTRUCT ps; HDC hDC; TCHAR greeting[ ] = _T ("DJ says, 'Hello World!'");
TCHAR click_greeting[] = _T ("Saw your click"); RECT rect;
switch (message)
{case WM_PAINT:
hDC = BeginPaint (…, &ps);
nHeight = -MulDiv (PointSize, GetDeviceCaps (…, LOGPIXELSY), 72);
GetClientRect (…, &rect);
/* set_font and color where his comment is */
TextOut (…, 5, y_position, greeting, _tcslen (greeting));
EndPaint (…, &ps);
DeleteObject (SelectObject (…, hTmp));
break;
case WM_LBUTTONDOWN:
InvalidateRect (…, NULL, FALSE); // TRUE => erase current content
hDC = BeginPaint (…, &ps);
increment_line_num (…);
set_font (hDC);
TextOut (hDC, 5, y_position, click_greeting, _tcslen (click_greeting));
EndPaint (…, &ps);
break;
case WM_DESTROY: PostQuitMessage (0);
break;
default: return DefWindowProc (…, message, wParam, lParam);
break;
}
return 0; // return to the OS from the callback function
}
subroutines
void increment_line_num (HDC hDC)
{
line_num++;
y_position = (-1)*nHeight * line_num; // allow some space to avoid clipping
// Remember that y-positions are more negative as you go DOWN the screen!!
// i.e.; the top left corner is 0,0
};
Void SetFont ()
{if (line_num==0)
hFont = CreateFont(nHeight, closest_match, escapement, orientation,
FW_DONTCARE,
no_italic, no_ul, no_xout, ANSI_CHARSET,
OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DRAFT_QUALITY, VARIABLE_PITCH,
TEXT("Times")); // TEXT ≡ _T
}
Callback skeleton (for drawing)
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{PAINTSTRUCT ps; RECT rect;
switch (message)
{case WM_LBUTTONDOWN:
iPrevX = LOWORD(lParam); iPrevY = HIWORD(lParam); return 0;
case WM_MOUSEMOVE:
if (wParam & MK_LBUTTON)
{ hdc = GetDC (hwnd);
MoveToEx (…, …, …, …); // update current position. Get old position back in parm 4
LineTo(…, …, …); iPrevX = LOWORD(lParam); iPrevY = HIWORD(lParam);
ReleaseDC (hwnd, hdc);
}
return 0;
case WM_LBUTTONUP:
InvalidateRect (…, …, …);
return 0;
case WM_PAINT:
GetClientRect (hwnd, &rect);
hdc = BeginPaint (hwnd, &ps);
InvalidateRect (…, …, …);
EndPaint (hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
Draw a Circle
case ID_DRAW_CIRCLE:
/* draw a blue-bordered magenta-crosshatched circle */
hDC = GetDC(hWnd);
/* get a DC for painting */
hPen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255)); /* blue pen */
hBrush = CreateHatchBrush(HS_DIAGCROSS, RGB(255, 0, 255));
hOldPen = (HPEN)SelectObject(hDC, hPen); /* select into DC & */
hOldBrush = (HBRUSH)SelectObject(hDC, hBrush); /* save old object */
Ellipse(hDC, 100, 30, 180, 110);
/* draw circle */
SelectObject(hDC, hOldBrush);
/* displace brush */
DeleteObject(hBrush);
/* delete brush */
SelectObject(hDC, hOldPen);
/* same for pen */
DeleteObject(hPen);
ReleaseDC(hWnd, hDC); /* release the DC to end painting */
break;
Draw a Rectangle
case ID_DRAW_RECTANGLE:
/* draw a red-bordered, cyan-filled rectangle */
hDC = GetDC(hWnd);
/* get a DC for painting */
hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0)); /* red pen */
hBrush = CreateSolidBrush(RGB(0, 255, 255)); /* cyan brush */
hOldPen = (HPEN)SelectObject(hDC, hPen); /* select into DC & */
hOldBrush = (HBRUSH)SelectObject(hDC, hBrush); /* save old object */
Rectangle(hDC, 15, 15, 80, 60);
/* draw rectangle */
SelectObject(hDC, hOldBrush);
/* displace new brush */
DeleteObject(hBrush);
/* delete it from DC */
SelectObject(hDC, hOldPen);
/* same for pen */
DeleteObject(hPen);
ReleaseDC(hWnd, hDC);
/* get rid of DC */
break;
Download