1 Katedra Optoelektroniki i Systemów Elektronicznych 2 Oprogramowanie Systemów Elektronicznych Zagadnienia: Temat wykładu 1. 2. Etapy tworzenia okna 3. 4. 5. 6. 3 Application Programming Interface 4 jest zbiorem funkcji, struktur, komunikatów, makr i reguł programistycznych stanowiących jednolitą i spójną platformę dla aplikacji Windows. Zasoby Win32 można podzielić na następujące kategorie: 1. Window Management interfejs użytkownika, zarządzanie wyświetlaniem okien. 2. Window Controls interfejs użytkownika, zarządzanie kontrolkami graficznymi. 3. Shell Features zarządzanie plikami, bazami danych, portami komunikacyjnymi i urządzeniami bazujące na hierarchicznej strukturze katalogów i obiektów (plików). 4. Graphics Device Interface zarządzanie wyjściem graficznym (monitor, drukarka); rysowanie linii, krzywych, figur zamkniętych, tekstu, map bitowych itp. 5. System Services zarządzanie dostępem do zasobów systemowych komputera, takich jak pamięć, system plików, urządzenia, zasoby komunikacyjne, procesy, wątki, mechanizmy wymiany danych i dzielenia kodu (DDE, DLL). 6. International Features funkcje wspomagające tworzenie aplikacji międzynarodowych niezależnych od wersji językowej systemu. 7. Network Services -zarządzanie komunikacją między komputerami poprzez sieć; zawiera Windows Networking, Windows Sockets, NetBIOS, NetDDE,... Architektura sterowana komunikatami Współpraca aplikacji z systemem Windows bazuje na systemie komunikatów. Każde okno tworzone w programie Windows ma swoją procedurę okna (ang. window procedure). Aby wysłać komunikat do okna, system Windows wywołuje procedurę okna. Procedura okna przetwarza komunikat, a następnie zwraca sterowanie do Windows. Struktura komunikatu Windows - MSG typedef struct tagmsg { // msg HWND hwnd; // Handle to window whose window procedure // receives the message UINT message; // Message number WPARAM wparam; LPARAM lparam; //Specifies additional information about the message. // The exact meaning depends on the value of the message member. DWORD time; // Time at which the message was posted. POINT pt; // Specifies the cursor position, in screen coordinates, // when the message was posted. MSG; 5 6 Kolejka komunikatów System Windows zakłada dla każdego działającego programu kolejkę komunikatów (ang. message queue). W kolejce tej umieszczane są komunikaty do wszystkich okien, które program może utworzyć. W programie znajduje się niewielki fragment kodu zwany pętlą komunikatów (ang. message loop), który zajmuje się pobieraniem komunikatów z kolejki i kierowaniem ich do odpowiedniej procedury okna. Istnieją komunikaty kolejkowane i niekolejkowane. Komunikaty kolejkowane są umieszczane przez Windows w kolejce i pobierane w pętli komunikatów. Do komunikatów kolejkowanych zalicza się komunikaty pochodzące od klawiatury, myszy, timera, WM_PAINT i WM_QUIT. Pozostałe nie są kolejkowane. Komunikaty niekolejkowane są przesyłane bezpośrednio przez system, który wywołuje procedurę okna. Message loop (Pętla komunikatów) while(getmessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); GetMessage()- gets a message from application's message queue. If there is no message, GetMessage() Blocks. TranslateMessage() does some additional processing on keyboard events like generating WM_CHAR messages to go along with WM_KEYDOWN messages. DispatchMessage() - sends the message out to the window that the message was sent to. This could be our main window or it could be another one, or a control. System takes care of sending messege to proper window. 1.1
7 8 Wysyłanie komunikatów The PostMessage function places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message. np. PostMessage(hwnd, WM_CLOSE, 0, 0); //close window Pobieranie komunikatów The GetMessage function retrieves a message from the calling thread's message queue and places it in the specified structure. np. GetMessage(&Msg, NULL, 0, 0); If the function retrieves a message other than WM_QUIT, the return value is greater than zero. If there is an error, the return value is -1. The SendMessage function sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message. np. SendMessage(hwnd, WM_SIZE, 0, 0); //resize window The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure. np. PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); If a message is available, the return value is nonzero. Unlike the GetMessage function, the PeekMessage function does not wait for a message to be placed in the queue before returning. 9 10 Przykładowe komunikaty Windows WM_COMMAND This message is sent when the user selects a command item from a menu, when a control sends a notification message to its parent window, or when an accelerator keystroke is translated. wnotifycode = HIWORD(wParam); // notification code wid = LOWORD(wParam); // item, control, or accelerator identifier hwndctl = (HWND) lparam; // handle of control WM_SIZE This message is sent to a window after its size has changed. fwsizetype = wparam; // resizing flag nwidth = LOWORD(lParam); // width of client area nheight = HIWORD(lParam); // height of client area Predefined macros: #define HIWORD(l) #define LOWORD(l) ((WORD) (((DWORD) (l) >> 16) & 0xFFFF)) ((WORD) (l)) Przykładowe komunikaty Windows WM_PAINT An application sends this message when Windows or another application makes a request to paint a portion of an application's window. hdc = (HDC) wparam; // the device context to draw in WM_CREATE This message is sent when an application requests that a window be created by calling the CreateWindowEx or CreateWindow function. The window procedure of the new window receives this message after the window is created, but before the window becomes visible. The message is sent before the CreateWindowEx or CreateWindow function returns. lpcs = (LPCREATESTRUCT) lparam; // structure with creation data WM_DESTROY This message is sent when a window is being destroyed. It is sent to the window procedure of the window being destroyed after the window is removed from the screen. This message is sent first to the window being destroyed and then to the child windows (if any) as they are destroyed. During the processing of the message, it can be assumed that all child windows still exist. 11 12 Przykładowe komunikaty klawiatury WM_KEYDOWN, WM_KEYUP, WM_CHAR WM_SYSKEYDOWN, WM_SYSKEYUP The WM_CHAR message is posted to the window with the keyboard focus when a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR contains the character code of the key that was pressed. The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when the user holds down the ALT key and then presses another key. It also occurs when no window currently has the keyboard focus. Kod naciśniętego lub zwolnionego klawisza zawiera parametr wparam. W pliku winuser.h zdefiniowano kody klawiszy wirtualnych, np. VK_RETURN, VK_ESCAPE, VK_SPACE, VK_HOME, VK_END, VK_F1, VK_F2,... Etapy generowania komunikatów klawiatury 1. Przechwycenie zdarzenia wciśnięcia lub zwolnienia klawisza przez sterownik klawiatury 2. Przesłanie sygnału o zdarzeniu w postaci komunikatu do systemu, który umieszcza go w kolejce komunikatów systemu. 3. Przesłanie komunikatu klawiatury do kolejki komunikatów programu, który posiada tzw. ognisko wejścia (ang. Input focus). 4. Przesłanie komunikatu klawiatury do odpowiedniej procedury okna za pomocą funkcji DispatchMessage. Kolejkowanie komunikatów klawiatury najpierw w kolejce systemowej, a następnie programowej pozwala na synchronizację ciągu komunikatów klawiatury, w których występują komunikaty przekazujące ognisko innej aplikacji (np. Alt-Tab). Tylko jedna procedura okna wszystkich aplikacji pracujących w systemie może otrzymywać komunikaty klawiatury. Okno, które otrzymuje komunikaty klawiatury ma ognisko. 1.2
13 14 Komunikaty myszy WM_MOUSEMOVE, WM_MOUSEWHEEL WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MBUTTONDBLCLK WM_RBUTTONDOWN, WM_RBUTTONUP, WM_RBUTTONDBLCLK Zmienne wparam i lparam zawierają informacje o położeniu kursora, stanie przycisków myszy i stanie klawiszy shift i control. Wciśnięcie lewego przycisku myszy podczas trzymania klawisza SHIFT. Puszczenie przycisku Etapy generowania komunikatów myszy W pierwszej kolejności procedura okna otrzymuje komunikat WM_NCHITTEST (ang. non-client hit test). Komunikat ten jest przekazywany do domyślnej procedury okna DefWindowProc. Procedura ta m.in. określa położenie kursora w obrębie okna aplikacji: a) Obszar roboczy (client area), b) Obszar nieroboczy (non-client area) (pasek tytułu, pasek menu, paski przewijania). Na podstawie wartości zwracanej przez funkcję DefWindowProc generowany jest odpowiedni komunikat do procedury okna. Komunikaty pochodzące z obszaru nieroboczego różnią się w nazwie występowaniem przedrostka NC (nonclient), np. WM_NCMOUSEMOVE. 15 16 Przechwytywanie myszy Standardowo komunikaty myszy nie są przekazywane przez system do procedury okna, jeśli wskaźnik jest poza oknem. Można jednak wymusić przekazywanie komunikatów myszy do określonej procedury okna dla dowolnej pozycji wskaźnika na ekranie. Do tego celu służy funkcja HWND SetCapture( HWND hwnd // handle of window to receive mouse capture ); Wszystkie komunikaty myszy są wówczas komunikatami obszaru roboczego. Przywrócenie normalnego trybu odbywa się po wywołaniu funkcji ReleaseCapture() lub po przyciśnięciu lewego przycisku myszy w innym oknie. Procedura okna LRESULT CALLBACK WindowProc( HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam ); Parameters: Hwnd - [in] Handle to the window. umsg - [in] Specifies the message. wparam, lparam - [in] Specifies additional message information. The contents of this parameter depend on the value of the umsg parameter. Return Value: The return value is the result of the message processing and depends on the message sent. 17 18 Przykładowy szkielet procedury okna LRESULT CALLBACK WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) { switch (umsg) { case WM_CREATE: // Initialize the window. case WM_PAINT: // Paint the window's client area. case WM_SIZE: // Set the size and position of the window. case WM_DESTROY: // Clean up window-specific data objects. // Process other messages. return DefWindowProc(hwnd, umsg, wparam, lparam); Domyślna procedura okna przeznaczona jest do przetwarzania komunikatów, przesłanych do okna, ale nie przetworzonych. Przechwytywanie komunikatów klawiatury LRESULT CALLBACK WndProc( HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) { switch (umsg) { case WM_CHAR: switch (wparam) { case '0': case f': case F': case WM_KEYDOWN: switch (wparam) { case VK_ESCAPE: PostQuitMessage( 0 ); case VK_F1: // Process other messages. return DefWindowProc( hwnd, umsg, wparam, lparam); //switch (umsg) //WndProc 1.3
19 20 Przechwytywanie komunikatów myszy LRESULT CALLBACK WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) { WPARAM fwkeys; short int xpos, ypos; switch (umsg) { case WM_LBUTTONDOWN: fwkeys = wparam; // key flags xpos = LOWORD(lParam); // horizontal position of cursor ypos = HIWORD(lParam); // vertical position of cursor case WM_MOUSEMOVE: fwkeys = wparam; // key flags xpos = LOWORD(lParam); // horizontal position of cursor ypos = HIWORD(lParam); // vertical position of cursor return DefWindowProc( hwnd, umsg, wparam, lparam); Zagadnienia: 1. 2. Etapy tworzenia okna 3. 4. 5. 6. 21 Etapy tworzenia okna 22 Etapy tworzenia okna Okno aplikacji i klasa okna Okno aplikacji jest tworzone na podstawie klasy okna (ang. window class), która określa procedurę okna. Istnienie klasy okna pozwala na utworzenie wielu okien należących do tej samej klasy, a tym samym używających tej samej procedury okna. Tworzenie okna w programie przebiega następująco: 1. Zadeklarowanie lub zdefiniowanie procedury okna 2. Zainicjowanie pól struktury WNDCLASSEX 3. Rejestracja klasy okna 4. Utworzenie okna na podstawie zarejestrowanej klasy 5. Wyświetlenie okna Przykładowy szablon aplikacji Windows (1/5) #include <windows.h> const char g_szclassname[] = "mywindowclass"; //Step 1: Declaration of the Window Procedure LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); int WIN WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { // HINSTANCE - hinstance // Handle to the programs executable module (the.exe file in memory) // HINSTANCE hprevinstance // Always NULL for Win32 programs. // LPSTR lpcmdline // The command line arguments as a single string. NOT including the // program name. // int ncmdshow // An integer value which may be passed to ShowWindow(). 23 Etapy tworzenia okna 24 Etapy tworzenia okna Przykładowy szablon aplikacji Windows (2/5) WNDCLASSEX wc; HWND hwnd; MSG Msg; //Step 2: Initializing members of Window Class structure wc.cbsize = sizeof(wndclassex); wc.style = 0; // Class Styles (CS_*) wc.lpfnwndproc = WndProc; wc.cbclsextra = 0; //Amount of extra data allocated for this //class in memory. wc.cbwndextra = 0; //Amount of extra data allocated in memory //per window of this type. wc.hinstance = hinstance; //Handle to application instance wc.hicon = LoadIcon(NULL, IDI_APPLICATION); wc.hcursor = LoadCursor(NULL, IDC_ARROW); wc.hbrbackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszmenuname = NULL; //Name of a menu resource to use //for the windows with this class. wc.lpszclassname = g_szclassname; wc.hiconsm = LoadIcon(NULL, IDI_APPLICATION); Przykładowy szablon aplikacji Windows (3/5) //Step 3: Registering the Window Class if(!registerclassex(&wc)) { MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION MB_OK); // Step 4: Creating the Window hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, //window style g_szclassname, //window class name "The title of my window", WS_OVERLAPPEDWINDOW, //window style CW_USEDEFAULT,CW_USEDEFAULT,240,120, //window coordinates NULL //parent window handle NULL //menu handle hinstance, //handle to application instance NULL //pointer to window creation data ); 1.4
25 Etapy tworzenia okna 26 Etapy tworzenia okna Przykładowy szablon aplikacji Windows (4/5) if (hwnd == NULL) { MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION MB_OK); Przykładowy szablon aplikacji Windows (5/5) // Step 6: the Window Procedure LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch(msg) { case WM_PAINT: // WinMain ShowWindow(hwnd, ncmdshow); //send WM_SIZE and WM_SHOWWINDOW UpdateWindow(hwnd); //send WM_PAINT message // The Message Loop while(getmessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); return Msg.wParam; case WM_CLOSE: DestroyWindow(hwnd); case WM_DESTROY: PostQuitMessage(0); return DefWindowProc(hwnd, msg, wparam, lparam); 27 28 Zagadnienia: 1. 2. Etapy tworzenia okna 3. 4. 5. 6. Zasoby programu Zasoby są danymi przechowywanymi razem z programem w pliku EXE. W trakcie uruchamiania programu do pamięci ładowany jest tylko kod programu, a zasoby nie są bezpośrednio dostępne przy użyciu zmiennych zdefiniowanych w kodzie programu. Przed użyciem zasobów należy je jawnie załadować do pamięci z pliku EXE. Rodzaje zasobów: 1. Ikony 2. Kursory 3. Menu 4. 5. Bitmapy 6. Napisy 7. Klawisze skrótu 8. Zasoby zdefiniowane przez użytkownika 29 30 Kompilowanie zasobów Zasoby programu definiuje się w skrypcie zasobów (plik ASCII z rozszerzeniem RC). Skrypt ten zawiera opis zasobów w formacie ASCII lub odwołania do innym plików (ASCII lub binarnych) zawierających opisy zasobów. W plikach zasobów można stosować dyrektywy preprocesora. Kompilator zasobów (RC.EXE) kompiluje skrypt zasobów do postaci binarnej (.RES), który następnie jest dołączany do kodu wykonawczego (.EXE) za pomocą linkera (LINK.EXE). Library files & compiled resource files Plik nagłówkowy "resource.h zawierający identyfikatory zasobów #define IDS_STRING1 1 #define IDI_ICON 101 #define IDR_MENU 1000 #define IDM_FILE_EXIT 1001 #define IDM_HELP_CONTENTS 1002 Plik zasobów #include <windows.h> #define IDM_HELP_ABOUT 1003 #define IDB_BITMAP1 103 Some resource editors and compilers use { in place of BEGIN and in place of END. #include resource.h STRINGTABLE DISCARDABLE BEGIN IDS_STRING1 Application title" END IDI_ICON ICON DISCARDABLE app_one.ico" 1.5
31 32 Plik zasobów cd. BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", IDM_FILE_EXIT END POPUP "&Help" BEGIN MENUITEM "&Contents", IDM_HELP_CONTENTS MENUITEM SEPARATOR MENUITEM "&About...", IDM_HELP_ABOUT END END IDB_BITMAP1 BITMAP DISCARDABLE "bitmap1.bmp Korzystanie z zasobów Bitmapa Kursor Ikona Menu Napis Rodzaj zasobu Tablica akceleratorów Ikona, Kursor, Bitmapa Funkcja ładująca do pamięci Funkcja usuwająca z pamięci LoadAccelerators LoadBitmap LoadCursor LoadIcon LoadMenu LoadString LoadImage DestroyAcceleratorTable DeleteObject DestroyCursor DestroyIcon DestroyMenu - DestroyIcon DestroyCursor DeleteObject 33 34 Korzystanie z zasobów (sygnatury funkcji) Functions: LoadBitmap, LoadCursor, LoadIcon, LoadMenu // 1st parameter - handle of application instance // 2st parameter - resorce name or resource identifier int LoadString( HINSTANCE hinstance, UINT uid, LPTSTR lpbuffer,// address of buffer for resource int nbuffermax // size of buffer ); HANDLE LoadImage( HINSTANCE hinstance, LPCTSTR lpszname, UINT utype, // type of image int cxdesired, // desired width int cydesired, // desired height UINT fuload // load flags ); Metody ładowania zasobów Jeśli parametr hinstance == NULL to funkcje ładują zasoby systemowe, których identyfikatory są zdefiniowane w plikach nagłówkowych. Funkcje mogą ładować zasoby albo z pliku o określonej nazwie, jeśli drugi parametr jest typu LPTSTR i starsze 16 bitów tej zmiennej jest różne od 0, np.: HICON hicon = LoadIcon(hInstance, my_icon ); Najczęściej jednak podaje się jako drugi parametr identyfikator zasobu. Do tego celu wykorzystuje się makro MAKEINTRESOURCE zamień liczbę całkowitą na nazwę zasobu (ang. make an integer into a resource string), np.: HICON hicon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON)); #define MAKEINTRESOURCE(i) (LPTSTR) ((DWORD) ((WORD) (i))) MAKEINTRESOURCE zwraca wartość, w której starsze 16 bitów jest równe 0. 35 36 Metody dołączania menu do okna aplikacji Menu w programie można utworzyć na dwa sposoby: 1. Zdefiniować strukturę menu w pliku zasobów i zainicjować pole nazwy menu w strukturze klasy okna (lpszmenuname) wc.lpszmenuname = MAKEINTRESOURCE(IDR_MYMENU); 2. Zdefiniować strukturę menu w trakcie inicjalizacji okna do którego ma być dołączone menu (przy obsłudze komunikatu WM_CREATE). Struktura menu jest usuwana automatycznie przy usuwaniu okna funkcją DestroyWindow(); Ad. 2. Dynamiczne generowanie struktury MENU case WM_CREATE: { HMENU hmenu, hsubmenu; hmenu = CreateMenu(); hsubmenu = CreatePopupMenu(); AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit"); AppendMenu(hMenu, MF_STRING MF_POPUP, (UINT)hSubMenu, "&File"); hsubmenu = CreatePopupMenu(); AppendMenu(hSubMenu, MF_STRING, IDM_HELP_CONTENTS, "&Contents"); AppendMenu(hSubMenu, MF_SEPARATOR, ID_STUFF_GO, NULL); AppendMenu(hSubMenu, MF_STRING, 0, "&About"); AppendMenu(hMenu, MF_STRING MF_POPUP, (UINT)hSubMenu, "&Help"); SetMenu(hwnd, hmenu); 1.6
37 38 Obsługa zdarzeń menu Zdarzenia menu powodują wysłanie komunikatu WM_COMMAND do okna z którym jest skojarzone MENU. Identyfikator pozycji menu, będącej źródłem komunikatu, przekazywany jest przez parametr wparam. case WM_COMMAND: switch(loword(wparam)) { case IDM_FILE_EXIT: PostMessage(hwnd, WM_CLOSE, 0, 0); case IDM_HELP_CONTENTS: ShowWindow(hwndHelp, SW_SHOWNORMAL); case IDM_HELP_ABOUT: MessageBox(hwnd, About...", Program info", MB_OK); Inne sposoby ładowania menu Menu z pliku zasobów można załadować w trakcie pracy programu korzystając z funkcji HMENU LoadMenu( HINSTANCE hinstance, // handle of application instance LPCTSTR lpmenuname // menu name string or menu identifier ); Funkcja zwraca uchwyt menu, który można przekazać do funkcji CreateWindow, albo przypisać menu do okna, gdy okno już istnieje za pomocą funkcji SetMenu(hWnd, hmenu); Załadowane do pamięci menu można wyświetlić jako menu kontekstowe w miejscu wskaźnika myszy korzystając z funkcji TrackPopupMenu, np.: POINT point; case WM_RBUTTONUP: // <- przechwycenie komunikatu w procedurze okna point.x = LOWORD(lParam); point.y = HIWORD(lParam); ClientToScreen(hwnd, &point); //<- przeliczenie współrzędnych myszy TrackPopupMenu(hMenu,TPM_RIGHTBUTTON,point.x,point.y,0,hwnd,NULL); 39 40 Zagadnienia: 1. 2. Etapy tworzenia okna 3. 4. 5. 6. Tworzenie kontrolek Kontrolki są specjalizowanymi oknami potomnymi aplikacji i tworzymy je za pomocą funkcji CreateWindow lub CreateWindowEx. Jeśli dodajemy kontrolkę predefiniowaną to należy w miejsce parametru lpclassname wstawić nazwę klasy kontrolki: Standard controls: BUTTON przycisk COMBOBOX lista rozwijana z możliwością edycji EDIT pole edycji LISTBOX pole listy SCROLLBAR pasek przewijania STATIC pole tekstowe Common controls, np.: STATUSCLASSNAME pasek statusu TOOLBARCLASSNAME pasek narzędzi PROGRESS_CLASS pasek postępu 41 42 Tworzenie kontrolek Każda kontrolka ma określony zestaw styli, które podajemy w miejsce parametru dwstyle funkcji CreateWindow lub CreateWindowEx. Przykłady: BUTTON class: BS_RADIOBUTTON, BS_PUSHBUTTON, BS_CHECKBOX COMBOBOX class: CBS_UPPERCASE, CBS_HASSTRINGS EDIT class: ES_MULTILINE, ES_NUMBER, ES_PASSWORD LISTBOX class: LBS_SORT, LBS_NOTIFY SCROLLBAR class: SBS_HORZ, SBS_VERT Identyfikatory i uchwyty kontrolek Każde okno potomne ma swój własny uchwyt okna i niepowtarzalny identyfikator. Znając jeden z tych parametrów można uzyskać drugi. Jeśli znany jest uchwyt, a chcemy uzyskać identyfikator korzystamy z funkcji: lub id = GetWindowLong(hWndChild, GWL_ID); id = GetDlgCtrlID(hWndChild); Jeśli znany jest identyfikator, a chcemy uzyskać uchwyt korzystamy z funkcji: hwndchild = GetDlgItem(hWndParent, id); 1.7
43 44 Przekazywanie informacji między oknem macierzystym a kontrolką odbywa się za pośrednictwem komunikatów. Kontrolki typu Standard Controls przesyłają do okna nadrzędnego komunikat WM_COMMAND, a kontrolki typu Common Controls komunikat WM_NOTIFY. Wraz z komunikatem WM_COMMAND kontrolka przesyła do okna nadrzędnego następujące informacje: LOWORD(wParam) Identyfikator kontrolki HIWORD(wParam) Kod powiadomienia lparam uchwyt kontrolki Kod powiadomienia jest dodatkową informacją zawierającą bliższy opis zdarzenia związanego z komunikatem. Przykładowe komunikaty powiadomienia kontrolki typu BUTTON: The BN_PUSHED / BN_UNPUSHED notification message is sent when the push state of a button is set to pushed / unpushed. The BN_SETFOCUS / BN_KILLFOCUS notification message is sent when a button receives / loses the keyboard focus. The BN_CLICKED / BN_DOUBLECLICKED notification message is sent when the user clicks / double-clicks a button. Przykładowe komunikaty powiadomienia kontrolki typu EDIT: EN_SETFOCUS / EN_KILLFOCUS The EN_CHANGE notification message is sent when the user has taken an action that may have altered text in an edit control. The EN_SELCHANGE notification message notifies a rich edit control's parent window that the current selection has changed. 45 46 Kontrolka typu CHECKBOX Przełączanie stanu kontrolki SendMessage(hwndButton, BM_SETSTATE, 1, 0); SendMessage(hwndButton, BM_SETSTATE, 0, 0); Pobranie stanu kontrolki BOOL state = SendMessage(hwndButton, BM_GETSTATE, 0, 0); Pole edycji (EDIT) SetDlgItemText(hwnd, IDC_TEXT, "This is a string"); GetDlgItemText(hwnd, IDC_TEXT, buf, len); BOOL bsuccess; int ntimes = GetDlgItemInt(hwnd, IDC_NUMBER, &bsuccess, FALSE); Kontrolka typu LISTBOX Adding Items int index = SendDlgItemMessage(hwnd, IDC_LIST, LB_ADDSTRING, 0, (LPARAM)"Hi there!"); SendDlgItemMessage(hwnd, IDC_LIST, LB_SETITEMDATA, (WPARAM)index, (LPARAM)nTimes); Funkcja SendDlgItemMessage wysyła komunikat do okna, jeśli znany jest uchwyt okna nadrzędnego i identyfikator kontrolki. Funkcja ta jest połączeniem dwóch funkcji GetDlgItem i SendMessage. 47 48 Kontrolka typu LISTBOX Przechwytywanie zdarzeń case WM_COMMAND: switch(loword(wparam)) { case IDC_LIST: // It's our listbox, check the notification code switch(hiword(wparam)) { case LBN_SELCHANGE: // Selection changed, do stuff here. //... other controls Kontrolka typu LISTBOX Pobieranie danych HWND hlist = GetDlgItem(hwnd, IDC_LIST); // get the number of selected items, int count = SendMessage(hList, LB_GETSELCOUNT, 0, 0); // allocate a buffer based on the number of items, int *buf = GlobalAlloc(GPTR, sizeof(int) * count); // send LB_GETSELITEMS to fill in the array. SendMessage(hList, LB_GETSELITEMS, (WPARAM)count, (LPARAM)buf); //... Do stuff with indexes GlobalFree(buf); 1.8
49 50 Zagadnienia: 1. 2. Etapy tworzenia okna 3. 4. 5. 6. Występują okna dialogowe modalne i niemodalne. Okno modalne jest to okno programu, którego uaktywnienie uniemożliwia przełączenie się do innego okna w programie. Szczególnym rodzajem okna modalnego jest modalne okno systemowe, którego pojawienie się blokuje dostęp do wszystkich okien uruchomionych aktualnie programów. Etapy tworzenia okna dialogowego modalnego: 1. Utworzenie szablonu okna dialogowego i zapisanie go do pliku zasobów (RC), albo do oddzielnego pliku z rozszerzeniem DLG. W drugim przypadku w pliku RC należy dodać wpis rcinclude nazwapliku.dlg 2. Napisanie procedury okna dialogowego 3. Utworzenie okna dialogowego modalnego poprzez wywołanie funkcji DialogBox 4. Przechwycenie wartości zwracanej przez funkcję DialogBox 51 52 Tworzenie okna dialogowego modalnego Przykładowy szablon okna dialogowego: IDD_ABOUT DIALOG DISCARDABLE 0, 0, 239, 66 STYLE DS_MODALFRAME WS_POPUP WS_CAPTION WS_SYSMENU CAPTION "My About Box" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,174,18,50,14 PUSHBUTTON "Cancel",IDCANCEL,174,35,50,14 GROUPBOX "About this program...",idc_static,7,7,225,52 CTEXT "An example program showing how to use\ Dialog Boxes\r\n\r\nby theforger, IDC_STATIC,16,18,144,33 END Identyfikatory kontrolek: IDOK, IDCANCEL Procedura okna dialogowego modalnego BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam) { switch(message) { case WM_INITDIALOG: return TRUE; //always true Return value case WM_COMMAND: switch(loword(wparam)) { case IDOK: EndDialog(hwnd, IDOK); case IDCANCEL: EndDialog(hwnd, IDCANCEL); return FALSE; //function doesn t processes message return TRUE; //function processes message 53 54 Różnice między procedurą okna, a procedurą okna dialogowego modalnego Procedura okna 1. Zwraca wartość LRESULT 2. Wywołuje funkcję DefWindowProc 3. Otrzymuje komunikat inicjujący WM_CREATE 4. Komunikaty przechodzą przez kolejkę komunikatów aplikacji 5. Jest wywoływana przez samą aplikację Procedura okna dialogowego modalnego 1. Zwraca wartość BOOL 2. Zwraca TRUE, jeśli przetwarza komunikat lub FALSE jeśli nie przetwarza komunikatu 3. Otrzymuje komunikat inicjujący WM_INITDIALOG 4. Komunikaty trafiają z pominięciem kolejki komunikatów aplikacji 5. Jest wywoływana przez system Przykład tworzenia okna dialogowego w kodzie programu int ret; case WM_COMMAND: //catch message from menu switch(loword(wparam)) { case ID_HELP_ABOUT: // MENU/HELP/ABOUT int ret = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_ABOUT), hwnd, AboutDlgProc); if (ret == IDOK){ MessageBox(hwnd,"Dialog exited with IDOK.", "Notice", MB_OK MB_ICONINFORMATION); else if(ret == IDCANCEL){ MessageBox(hwnd,"Dialog exited with IDCANCEL.", "Notice", MB_OK MB_ICONINFORMATION); else if(ret == -1){ MessageBox(hwnd, "Dialog failed!", "Error", MB_OK MB_ICONINFORMATION); 1.9
55 56 Niemodalne okna dialogowe (1/3) Niemodalne okno dialogowe tworzy się przy pomocy funkcji CreateDialog HWND CreateDialog( HINSTANCE hinstance, // handle to application instance LPCTSTR lptemplate, // identifies dialog box template name HWND hwndparent, // handle to owner window DLGPROC lpdialogfunc // pointer to dialog box procedure ); W przeciwieństwie do funkcji DialogBox, która kończy działanie dopiero po zlikwidowaniu okna modalnego, funkcja CreateDialog natychmiast kończy działanie i zwraca uchwyt do okna dialogowego. Istotna różnica między oknem modalnym i niemodalnym polega na tym, iż komunikaty przeznaczone dla niemodalnego okna dialogowego przechodzą przez kolejkę komunikatów programu. Niemodalne okna dialogowe (2/3) Pętla komunikatów programu korzystającego z niemodalnych okien dialogowych musi zostać rozbudowana, w celu rozróżnienia komunikatów skierowanych do procedury okna aplikacji i do procedury niemodalnego okna dialogowego. while(getmessage(&msg, NULL, 0, 0) > 0) { if (hdlgmodeless == 0!isDialogMessage(hDlgModeless, &msg)) { TranslateMessage(&Msg); DispatchMessage(&Msg); hdlgmodeless uchwyt niemodalnego okna dialogowego zwrócony przez funkcję CreateDialog Jeśli komunikat jest przeznaczony dla niemodalnego okna dialogowego, to funkcja IsDialogMessage wysyła go do procedury okna obsługującej okno dialogowe i zwraca TRUE, w przeciwnym razie zwraca FALSE. 57 58 Niemodalne okna dialogowe (3/3) Uchwyt niemodalnego okna dialogowego hdlgmodeless może być wykorzystywany przez inne części programu do sprawdzenia, czy istnieje niemodalne okno dialogowe. Na przykład inne okna programu mogą wysyłać komunikaty do okna dialogowego, gdy hdlgmodeless jest różne od zera. Niemodalne okna dialogowe usuwa się wywołując funkcję DestroyWindow, np.: BOOL CALLBACK DlgProc(HWND hdlg, UINT Message, WPARAM wparam, LPARAM lparam) { switch(message) { //... case WM_CLOSE: DestroyWindow(hDlg); hdlgmodeless = 0; return FALSE; //function doesn t processes message return TRUE; //function processes message Zagadnienia: 1. 2. Etapy tworzenia okna 3. 4. 5. 6. 59 60 Multiple Document Interface Tworzenie okna-klienta (MDI Client WINDOW) Okno-klienta tworzone za pomocą funkcji CreateWindow w oparciu o predefiniowaną klasę MDICLIENT. Okno to musi mieć styl WS_CHILD. Tworzenie okna-klienta realizuje się przy obsłudze komunikatu WM_CREATE okna framugi (MDI FRAME WINDOW). Okno-klient pokrywa obszar roboczy framugi i jest odpowiedzialne za obsługę MDI. Skojarzenie okien potomnych z menu okna nadrzędnego 1.10
61 62 Tworzenie okna-klienta (MDI Client WINDOW) CLIENTCREATESTRUCT ccs; // Uchwyt podmenu do którego jest dołączana lista nazw okien potomnych ccs.hwindowmenu = GetSubMenu(GetMenu(hwnd), 2); // Identyfikator menu, począwszy od którego będą skojarzone okna potomne ccs.idfirstchild = ID_MDI_FIRSTCHILD; Stała zdefiniowana w pliku nagłówkowym // Uchwyt okna-klienta HWND g_hmdiclient = NULL; Predefiniowana klasa MDICLENT g_hmdiclient = CreateWindowEx(WS_EX_CLIENTEDGE, "mdiclient", NULL, WS_CHILD WS_CLIPCHILDREN WS_VSCROLL WS_HSCROLL WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwnd, (HMENU)IDC_MAIN_MDI, GetModuleHandle(NULL), (LPVOID)&ccs); Wskaźnik do struktury CLIENTCREATESTRUCT Inicjowanie klasy okna potomnego (MDI Child WINDOW) char g_szchildclassname[] = "MyMDIChild"; WNDCLASSEX wc; wc.cbsize = sizeof(wndclassex); wc.lpfnwndproc = MDIChildWndProc; wc.lpszmenuname = NULL; wc.lpszclassname = g_szchildclassname; wc.hbrbackground = (HBRUSH)(COLOR_3DFACE+1); // initialize other members... If (!RegisterClassEx(&wc)) { MessageBox(0, "Could Not Register Child Window", "Oh Oh...", MB_ICONEXCLAMATION MB_OK); return FALSE; Każdy rodzaj okna potomnego musi mieć zdefiniowaną odpowiednią klasę okna 63 64 Tworzenie okna potomnego (MDI Child WINDOW) Okna potomne (okna dokumentów aplikacji) tworzy się je przez zainicjowanie struktury MDICREATESTRUCT i wysłanie do okna-klienta komunikatu WM_MDICREATE ze wskaźnikiem do tej struktury. MDICREATESTRUCT mcs; HWND hchild; mcs.sztitle = "[Untitled]"; Pointer to mcs.szclass = g_szchildclassname; MDICREATESTRUCT mcs.howner = GetModuleHandle(NULL); mcs.x = mcs.cx = CW_USEDEFAULT; mcs.y = mcs.cy = CW_USEDEFAULT; mcs.style = MDIS_ALLCHILDSTYLES; hchild = (HWND)SendMessage(hMDIClient, WM_MDICREATE, 0, (LONG)&mcs); if(!hchild) { MessageBox(hMDIClient, "MDI Child creation failed.", "Oh Oh...", MB_ICONEXCLAMATION MB_OK); Domyślne procedury okna framugi i okna potomnego Procedury okna framugi i okna potomnego wykorzystują specjalne funkcje obsługi komunikatów przeznaczone dla aplikacji MDI. Procedura framugi wywołuje funkcję DefFrameProc(hwnd, g_hmdiclient, msg, wparam, lparam); Uchwyt okna klienta Procedura okna potomnego wywołuje funkcję DefMDIChildProc(hwnd, msg, wparam, lparam); 65 66 Obsługa komunikatu WM_COMMAND w procedurze okna framugi case WM_COMMAND: switch(loword(wparam)) { //... handle frame window IDs... user has clicked on one of // Handle MDI Window commands the Window menu items if(loword(wparam) >= ID_MDI_FIRSTCHILD) { DefFrameProc(hwnd,g_hMDIClient,msg,wParam,lParam); else { HWND hchild; get active child window hchild =(HWND)SendMessage(g_hMDIClient,WM_MDIGETACTIVE,0,0); if(hchild) { SendMessage(hChild, WM_COMMAND, wparam, lparam); forward the message to the active child window for processing Procedura okna potomnego An application sends the WM_MDIACTIVATE message to a multiple document interface (MDI) client window to instruct the client window to activate a different MDI child window. As the client window processes this message, it sends WM_MDIACTIVATE to the child window being deactivated and to the child window being activated. // Message sent to MDI client wparam = (WPARAM) (HWND) hwndchildact; // child to activate lparam = 0; // not used; must be zero // Message received by MDI child hwndchilddeact = (HWND) wparam; hwndchildact = (HWND) lparam; // child being deactivated // child being activated The WM_MDIACTIVATE message is send first to the child window being deactivated and next to the child window being activated 1.11
67 Pętla komunikatów aplikacji MDI while(getmessage(&msg, NULL, 0, 0) > 0) { if (!TranslateMDISysAccel(g_hMDIClient, &Msg)) { TranslateMessage(&Msg); DispatchMessage(&Msg); Przetwarzanie klawiszy skrótu aplikacji MDI The TranslateMDISysAccel function processes accelerator keystrokes for window menu commands of the multiple document interface (MDI) child windows associated with the specified MDI client window. Ctrl F4 -> zamknięcie okna potomnego Ctrl F6 -> przełączanie między oknami potomnymi 1.12