Radek ChalupaČlánkyŠkolení a konzultaceVývoj softwareFreewareKontakt

Práce s registry  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ě.

win-api-23

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.

Otevření klíče

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.

Zápis do registru

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


Komentář k článku

Jméno:


Email:


Zde můžeš napsat komentář k tomuto článku: (max. 1000 znaků)



Copyright © 2019 - Radek Chalupa