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;