Radek Chalupa   vývoj software, konzultace a školení programování
 DomůČlánkyUkázky kóduKonzultace a školeníVývoj softwareFreewareKontakt

Výběr a načtení souboru.  22.1.2002

V tomto pokračování si ukážeme něco málo ze základů práce se soubory. Naučíme se použít standardní dialog pro otevření souboru a dále si ukážeme ten nejjednodušší způsob načtení souboru (textu) například do víceřádkového editačního pole. Výsledek dnešního ukázkového příkladu vidíte na obrázku:

win-api-22

K vyvolání standardního systémového dialogu pro otevření (nebo uložení) souboru složí funkce GetOpenFileName:

BOOL GetOpenFileName(
  LPOPENFILENAME lpofn   // inicializační data
);

Jak je vidět, veškeré parametry funkce jsou ve struktuře OPENFILENAME:

typedef struct tagOFN { 
  DWORD         lStructSize; 
  HWND          hwndOwner; 
  HINSTANCE     hInstance; 
  LPCTSTR       lpstrFilter; 
  LPTSTR        lpstrCustomFilter; 
  DWORD         nMaxCustFilter; 
  DWORD         nFilterIndex; 
  LPTSTR        lpstrFile; 
  DWORD         nMaxFile; 
  LPTSTR        lpstrFileTitle; 
  DWORD         nMaxFileTitle; 
  LPCTSTR       lpstrInitialDir; 
  LPCTSTR       lpstrTitle; 
  DWORD         Flags; 
  WORD          nFileOffset; 
  WORD          nFileExtension; 
  LPCTSTR       lpstrDefExt; 
  LPARAM        lCustData; 
  LPOFNHOOKPROC lpfnHook; 
  LPCTSTR       lpTemplateName; 
#if (_WIN32_WINNT >= 0x0500)
  void *        pvReserved;
  DWORD         dwReserved;
  DWORD         FlagsEx;
#endif // (_WIN32_WINNT >= 0x0500)
} OPENFILENAME, *LPOPENFILENAME;

Prozatím se nebudeme zabývat podrobnějším vysvětlením všech prvků této struktury. Nejlépe si ukázat jednoduchý příklad. Důležité je pamatovat na "vynulování" struktury před plněním jednotlivých prvků, abychom ty které nás nezajímají, mohli ignorovat a měli jistotu, že jsou "nulové". Dále je nutné runě naplnit prvek lStructSize na velikost struktury (pomocí sizeof). Nyní již k příkladu.  V dnešní ukázce je aplikace založená na dialogu se 2 edit-boxy, z nichž do jednoho vypíšeme plný název vybraného souboru, do druhého (víceřádkového) pak obsah tohoto (textového) souboru. Dialog otevření souboru vyvoláme na talčítko "Vybrat soubor" (IDC_OPEN_FILE). Pro vybrání souboru si vytvoříme následující funkci:

BOOL OpenDialog(LPTSTR lpFile, HWND hwndOwner)
{
  OPENFILENAME ofn;
  TCHAR chFile[_MAX_PATH];
  lstrcpy(chFile, "");
  ZeroMemory(&ofn, sizeof(OPENFILENAME));
  ofn.lStructSize = sizeof(OPENFILENAME);
  ofn.lpstrFile = chFile;
  ofn.hwndOwner = hwndOwner;
  ofn.nMaxFile = sizeof(chFile);
  ofn.lpstrFilter = 
     TEXT("Textové soubory (*.txt)\0*.txt\0Všechny soubory (*.*)\0*.*\0");
  ofn.nFilterIndex = 1;
  ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  if ( !GetOpenFileName(&ofn) )
    return FALSE;
  lstrcpy(lpFile, chFile);
  return TRUE;
}

Pokud uživatel stiskne tlačítko "OK", funkce zkopíruje plný název vybraného souboru do parametru lpFile.

Nyní tedy máme jméno souboru a můžeme přistoupit k jeho otevření a načtení. Pro otevření (nebo vytvoření nového) souboru se používá funkce CreateFile:

HANDLE CreateFile(
  LPCTSTR lpFileName,                         // jméno souboru
  DWORD dwDesiredAccess,                      // přístup
  DWORD dwShareMode,                          // sdílení
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // atributy zabezpečení
  DWORD dwCreationDisposition,                // způsob vytvoření
  DWORD dwFlagsAndAttributes,                 // atributy souboru
  HANDLE hTemplateFile                        // soubor "šablony"
);

Podrobněji se k této funkci a jejím možnostem vrátíme později, zde si ukážeme ten nejjednodušší způsob použití pro otevření existujícího souboru. Funkce vrátí handle otevřeného souboru nebo v případě neúspěchu hodnotu INVALID_HANDLE_VALUE. Podívejme se nyní na celou vlastní funkci, která načte zadaný soubor a jeho obsah nastaví jako text okna zadaného jako parametr:

BOOL LoadFile(LPCTSTR lpFileName, HWND hWndTo)
{
  HANDLE hFile;
  DWORD dwSize;
  DWORD dw;
  LPBYTE lpBuffer = NULL;
  hFile =  CreateFile(lpFileName, GENERIC_READ, 0,
    NULL, OPEN_EXISTING, 0, NULL);
  if ( hFile == INVALID_HANDLE_VALUE )
    return FALSE;
  dwSize = GetFileSize(hFile, NULL);
  lpBuffer = (LPBYTE)HeapAlloc(GetProcessHeap(),
    HEAP_GENERATE_EXCEPTIONS, dwSize+1);
  if ( !ReadFile(hFile, lpBuffer, dwSize, &dw, NULL) )
  {
    CloseHandle(hFile);
    HeapFree(GetProcessHeap(), 0, lpBuffer);
    return FALSE;
  }
  CloseHandle(hFile);
  lpBuffer[dwSize] = 0;
  SetWindowText(hWndTo, (LPCTSTR)lpBuffer);
  if ( !HeapFree(GetProcessHeap(), 0, lpBuffer) )
    return FALSE;
  return TRUE;
}

Jak je zřejmé, celý obsah souboru načítáme v jednom čtení. Musíme tedy nejprve zjistit velikost souboru pomocí funkce GetFileSize. Buffer si naalokujeme na tuto velikost plus jeden byte navíc pro uložení nulového znaku ukončujícího řetězec, neboť k tomuto bufferu budeme přistupovat jako k céčkovskému řetězci, použijeme ho jako parametr funkce SetWindowText. Pro alokaci bufferu by samozřejmě bylo možné použít standardní céčkovské funkce malloc/free. Na tomto příkladě jsem ale také ukázal jednoduchý způsob alokace paměti pomocí Win32 funkcí, konkrétně HeapAlloc/HeapFree. Vlastní načtení souboru pak provádíme funkcí ReadFile:

BOOL ReadFile(
  HANDLE hFile,                // handle otevřeného souboru
  LPVOID lpBuffer,             // datový buffer
  DWORD nNumberOfBytesToRead,  // počet bytů k načtení
  LPDWORD lpNumberOfBytesRead, // počet načtených bytů
  LPOVERLAPPED lpOverlapped    // "overlapped" buffer
);

Tato funkce nám vrátí logickou hodnotu indikující úspěch načtení a současně naplní parametr lpNumberOfBytesRead počtem skutečně načtených bytů, který může být menší než počet bytů požadovaných, pokud například narazíme na konec souboru.

Doprovodný projekt je ke stažení zde: win_api_22.zip


Komentář k článku

Jméno:


Email:


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



Copyright © 2019 - Radek Chalupa