23.1.2002
V tomto pokračování se naučíme základům používání registrační databáze pro ukládání konfigurace programu nebo nějakých dalších informací. V doprovodném příkladu budeme mít za úkol zapsat do registrů polohu a velikost okna při ukončení aplikace a při dalším spuštění okno zobrazit v této pozici. Dále si uložíme (ze cvičných důvodů) jako řetězec datum a čas ukončení programu a toto pak zobrazíme při dalším spuštění jako text v okně.
Podobně jako při práci se soubory musíme nejprve požadovaný klíč v registrační databázi otevřít (nebo vytvořit), tím získáme jeho handle. Tento handle pak použijeme v dalších funkcích pro práci s údaji v registrech. Po provedení požadovaných operací handle klíče opět zavřeme.
Pro otevření nebo vytvoření klíče nám slouží funkce RegCreateKeyEx:
LONG RegCreateKeyEx( HKEY hKey, // handle klíče LPCTSTR lpSubKey, // jméno podklíče DWORD Reserved, // rezervováno LPTSTR lpClass, // třída DWORD dwOptions, // volby REGSAM samDesired, // bezpečnost LPSECURITY_ATTRIBUTES lpSecurityAttributes, // děditelnost PHKEY phkResult, // handle klíče LPDWORD lpdwDisposition // způsob "otevření" );
Řekněme si o významu těch důležitých parametrů:
hKey - handle již otevřeného klíče nebo jedna z předdefinovaných hodnot většinou určující větev v registrech, jak ostatně napovídá přehled možných hodnot:
HKEY_CLASSES_ROOT HKEY_CURRENT_CONFIG HKEY_CURRENT_USER HKEY_LOCAL_MACHINE HKEY_USERS Windows NT/2000/XP: HKEY_PERFORMANCE_DATA Windows 95/98/Me: HKEY_DYN_DATA
lpSubkey - jméno podklíče, který chceme otevřít.
samDesired - požadovaný přístup ke klíči. Pokud chceme plný přístup pro zápis a čtení, můžeme jednoduše uvést hodnotu KEY_ALL_ACCESS, nebo pro zvýšení "bezpečnosti" některou z dalších hodnot (jejich seznam s popisem naleznete v dokumentaci Windows SDK), které nějakým způsobem omezují jisté operace.
phkResult - v tomto parametru nám funkce vrátí handle požadovaného klíče.
lpdwDisposition - v tomto parametru nám funkce vrátí hodnotu určující, zda byl otevřen existující klíč nebo vytvořen klíč nový. Může nabývat hodnot REG_CREATED_NEW_KEY nebo REG_OPENED_EXISTING_KEY, jejichž význam je jistě vypovídající.
Pokud funkce skončí úspěšně, návratová hodnota je ERROR_SUCCESS, jejíž název zní možná trochu podivně (česky "chyba_úspěch"), nicméně opravdu značí úspěšné otevření požadovaného klíče v registrech.
Když máme klíč úspěšně otevřen nebo vytvořen, můžeme zapisovat hodnoty funkcí RegSetValueEx:
LONG RegSetValueEx( HKEY hKey, // handle klíče LPCTSTR lpValueName, // název hodnoty DWORD Reserved, // rezervováno DWORD dwType, // typ hodnoty CONST BYTE *lpData, // data DWORD cbData // velikost dat );
Podrobnější popis a význam jednotlivých parametrů naleznete v dokumentaci Windows SDK a nebudu ho zde popisovat, raději si vše ukážeme na příkladě. Vytvořme si funkci, která nám otevře zadaný klíč. Název klíče si v programu vytvoříme pomocí direktivy define:
#define REGISTRY_KEY_NAME "Software\\Radek Chalupa\\Ucime se WinAPI" HKEY registry_OpenKey(LPCTSTR lpKeyName, LPBOOL lpbCreated) { DWORD dwDisp = 0; HKEY hkResult = NULL; if ( RegCreateKeyEx(HKEY_CURRENT_USER, lpKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkResult, &dwDisp) != ERROR_SUCCESS ) return NULL; if ( lpbCreated ) *lpbCreated = ( dwDisp & REG_CREATED_NEW_KEY ); return hkResult; }
Dále si vytvoříme funkce pro zápis číselné hodnoty (typ DWORD) a textových řetězců:
BOOL registry_WriteDWORD(HKEY hKey, LPCTSTR lpValueName, DWORD dwValue) { return ( RegSetValueEx(hKey, lpValueName, 0, REG_DWORD, (CONST BYTE*)&dwValue, sizeof(DWORD)) == ERROR_SUCCESS ); } BOOL registry_WriteString(HKEY hKey, LPCTSTR lpValueName, LPCTSTR lpText) { return ( RegSetValueEx(hKey, lpValueName, 0, REG_SZ, (CONST BYTE*)lpText, (lstrlen(lpText)+1)*sizeof(TCHAR)) == ERROR_SUCCESS ); }
V obou případech zadáme jako parametr hKey handle předtím otevřeného klíče.
Na zavření klíče v registrech si také napíšeme jednoduchou funkci volající API funkci RegCloseKey:
BOOL registry_CloseKey(HKEY hKey) { return ( RegCloseKey(hKey) == ERROR_SUCCESS); }
Nyní se podívejme, jak s využitím těchto funkcí zapíšeme v handleru zprávy WM_CLOSE požadované výše zmíněné informace do registru:
BOOL WriteRegistry() { BOOL bRes = TRUE; HKEY hKey = registry_OpenKey(REGISTRY_KEY_NAME, NULL); if ( !hKey ) return FALSE; if ( !registry_WriteDWORD(hKey, TEXT("WindowPos"), dwWindowPos) ) bRes = FALSE; if ( !registry_WriteDWORD(hKey, TEXT("WindowSize"), dwWindowSize) ) bRes = FALSE; if ( !registry_WriteString(hKey, TEXT("Info"), chInfoText) ) bRes = FALSE; if ( !registry_CloseKey(hKey) ) bRes = FALSE; return bRes; } // Handler zprávy WM_CLOSE void OnClose() { SYSTEMTIME st; GetLocalTime(&st); sprintf(chInfoText, "Předchozí ukončení: %d.%d.%d %.2d:%.2d", st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute); RECT rect; GetWindowRect(g_hWnd, &rect); dwWindowPos = MAKELONG(rect.left, rect.top); dwWindowSize = MAKELONG(rect.right-rect.left, rect.bottom-rect.top); WriteRegistry(); }
Nyní se ještě musíme naučit informace z registru také přečíst. K tomu nám slouží funkce RegQueryValueEx, kterou zde uvedu také bez podrobného popisu parametrů (je v dokumentaci), ale ukážeme si samozřejmě příklad.
LONG RegQueryValueEx( HKEY hKey, // handle klíče LPCTSTR lpValueName, // název hodnoty LPDWORD lpReserved, // rezervováno LPDWORD lpType, // typ bufferu LPBYTE lpData, // datový buffer LPDWORD lpcbData // velikost datového bufferu );
Také pro načítání si připravíme 2 vlastní funkce pro čtení číselné a textové hodnoty:
BOOL registry_ReadDWORD(HKEY hKey, LPCTSTR lpValueName, LPDWORD lpdwResult) { if ( !registry_KeyExists(hKey, lpValueName) ) return FALSE; DWORD dwValue, dwSize, dwType; dwSize = sizeof(DWORD); dwValue = 0; if ( RegQueryValueEx(hKey, lpValueName, NULL, &dwType, (LPBYTE)&dwValue, &dwSize) != ERROR_SUCCESS ) return FALSE; *lpdwResult = dwValue; return TRUE; } BOOL registry_ReadString(HKEY hKey, LPCTSTR lpValueName, LPTSTR lpString) { if ( !registry_KeyExists(hKey, lpValueName) ) return FALSE; DWORD dwSize, dwType; dwSize = sizeof(chInfoText); if ( RegQueryValueEx(hKey, lpValueName, NULL, &dwType, (LPBYTE)lpString, &dwSize) != ERROR_SUCCESS ) return FALSE; return TRUE; }
Jako příklad si ukažme, jak v doprovodném programu načteme informace zapsané při předchozím ukončení a jak zjistíme, zda záznam v registrech neexistuje, tedy že se pravděpodobně jedná o první spuštění programu v tomto uživatelském účtu.
BOOL ReadRegistry() { BOOL bRes = TRUE; BOOL bFirst = FALSE; HKEY hKey = registry_OpenKey(REGISTRY_KEY_NAME, &bFirst); if ( !hKey ) return FALSE; if ( bFirst ) { dwWindowPos = 0x00600080; dwWindowSize = 0x01500200; lstrcpy(chInfoText, TEXT("První spuštění programu")); return TRUE; } if ( !registry_ReadDWORD(hKey, "WindowPos", &dwWindowPos) ) bRes = FALSE; if ( !registry_ReadDWORD(hKey, "WindowSize", &dwWindowSize) ) bRes = FALSE; if ( !registry_ReadString(hKey, "Info", chInfoText) ) bRes = FALSE; if ( !registry_CloseKey(hKey) ) bRes = FALSE; return bRes; }
Tuto funkci zavoláme před vytvořením okna, pokud není nalezen záznam v registrech, nastavíme poloho a rozměry okna na výchozí hodnotu, stejně jako informativní text, jehož výpis v handleru WM_PAINT, je pro čtenáře jistě již rutinní záležitostí.
Jako "domácí úkol" si mohou zájemci program doplnit tak, aby byl odstraněn následující "bug": Pokud totiž okno zavřeme dostatečně vpravo či dole, poté zmenšíme rozlišení obrazovky, při dalším spuštění se nám může stát že zaznamenáme "aut", tedy okno bude celé "zobrazené" mimo viditelnou oblast obrazovky.
Doprovodný projekt je ke stažení zde: win_api_23.zip
Školení
Kontakt
739 219 991
live:radekchalupa_1
Nové články