Radek Chalupa   konzultace a školení programování, vývoj software na zakázku

Vyhledávání souborů - zjištění obsahu složky.

1.2.2002

V tomto článku si ukážeme, jak získat seznam souborů a podsložek, obsažených v zadané složce. Výsledkem bude program, který vyvolá již probíranou funkci pro výběr složky, upravenou tak, že během procházení jednotlivými složkami uživatelem bude do ListBoxu na hlavním dialogu zobrazován seznam všech souborů v této složce (s odfiltrováním podsložek) s uvedením jejich velikosti a data poslední změny, jak je vidět na následujícím obrázku:

win-api-find-file

Získání obsahu složky nám umožňují funkce FindFirstFile a FindNextFile. Princip použití je následující: nejprve zavoláme funkci FindFirstFile, které zadáme vzor, kterému musí vyhovovat nalezené objekty (soubory či složky). Funkce nám v případě úspěchu naplní strukturu WIN32_FIND_DATA a vrátí handle. Tyto hodnoty pak použijeme v opakovaném hledání dalších objektů pomocí funkce FindNextFile. Nakonec musíme zavřít použitý handle. Struktura WIN32_FIND_DATA obsahuje různé informace o nalezeném objektu. V našem případě použijeme jméno souboru (cFilename) a prvek dwFileAttributes, pomocí kterého zjistíme, zda objektem je složka, které chceme ze seznamu vyloučit. Celý postup je vidět v následujícím výpisu:

void PridejSoubor(HWND hListBox, LPWIN32_FIND_DATA lpWFD)
{
  TCHAR szText[MAX_PATH+60];
  TCHAR szVelikost[25];
  TCHAR szCas[25];
  FILETIME fileTime;
  SYSTEMTIME sysTime;
  StrCpy(szText, lpWFD->cFileName);
  StrFormatByteSizeA(lpWFD->nFileSizeLow, szVelikost, sizeof(szVelikost));
  FileTimeToLocalFileTime(&lpWFD->ftLastAccessTime, &fileTime);
  FileTimeToSystemTime(&fileTime, &sysTime);
  _stprintf(szCas, _T("změněno dne: %d.%d.%d"),
    sysTime.wDay, sysTime.wMonth, sysTime.wYear);
  StrCat(szText, _T(" ( velikost: "));
  StrCat(szText, szVelikost);
  StrCat(szText, _T(", "));
  StrCat(szText, szCas);
  StrCat(szText, _T(" )"));
  SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)szText);
}

void PridejZeSlozky(HWND hWnd, LPTSTR lpSlozka)
{
  TCHAR szSoubor[MAX_PATH];
  StrCpy(szSoubor, lpSlozka);
  StrCat(szSoubor, _T("\\*.*"));
  HWND hListBox = GetDlgItem(hWnd, IDC_LISTBOX);
  // Nejprve odstraníme případné staré položky ListBoxu
  SendMessage(hListBox, LB_RESETCONTENT, 0, 0);
  WIN32_FIND_DATA wfd;
  ZeroMemory(&wfd, sizeof(wfd));
  // Nalezneme 1. objekt
  HANDLE hFind = FindFirstFile(szSoubor, &wfd);
  if ( hFind == INVALID_HANDLE_VALUE )
    return;
  // Vyloučit složky
  if ( !(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
    PridejSoubor(hListBox, &wfd);
  // Procházíme všemi objekty ve složce
  while ( FindNextFile(hFind, &wfd) )
  {
    if ( !(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
      PridejSoubor(hListBox, &wfd);
  }
  // Zavřít handle vyhledávání
  FindClose(hFind);
}

Ve funkci PridejSoubor je dále vidět, jak lze pomocí funkce StrFormatByteSize získat textové vyjádření zadané velikosti (v bytech), přičemž funkce sama zvolí použití jednotky (kB, MB..) podle aktuální velikosti. Dále je zde ukázán převod a výpis časového údaje v souboru (typ FILETIME) na místní čas.

Nyní si ukážeme, jak zařídit, aby uvedená funkce byla volaná vždy při změně složky v dialogu výběru složky. Ve funkci BrowseCallbackProc využijeme parametr lpData, který představuje prvek lParam struktury BROWSEINFO, kterou plníme před voláním funkce SHBrowseForFolder. Využijeme ho pro předání handle dialogu, potřebné při volání funkce PridejZeSlozky, kterou si tak můžeme zavolat přímo zevnitř této callback funkce, jak je vidět na jejím výpisu:

int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
  TCHAR szDir[MAX_PATH];
  switch ( uMsg )
  {
    case BFFM_INITIALIZED:
      if ( chStartBrowseDir != NULL )
        SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)chStartBrowseDir);
      break;
    case BFFM_SELCHANGED:
      if ( SHGetPathFromIDList((LPITEMIDLIST)lParam, szDir))
      {
        SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)szDir);
        PridejZeSlozky((HWND)lpData, szDir);
      }
      break;
  }
  return 0;
}

Pro úplnost uvedeme také její volání:

BOOL VyberSlozku(LPTSTR lpVybranaSlozka, LPCTSTR lpStart, LPCTSTR lpNadpis, HWND hVlastnik)
{
  if ( lpStart != NULL )
    lstrcpy(chStartBrowseDir, lpStart);
  BOOL bResult = TRUE;
  LPITEMIDLIST pidl  = NULL;
  BROWSEINFO bi;
  ZeroMemory(&bi, sizeof(bi));
  LPMALLOC pMalloc = NULL;
  SHGetMalloc(&pMalloc);
  if ( IsWindow(hVlastnik) )
  {
    bi.hwndOwner = hVlastnik;
    bi.lParam = (LPARAM)hVlastnik;
  }
  else
    bi.hwndOwner = NULL;
  bi.pszDisplayName = 0;
  bi.pidlRoot = 0;
  bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
  bi.lpfn = BrowseCallbackProc;
  if ( lpNadpis == NULL )
    bi.lpszTitle = _T("Vyberte požadovanou složku");
  else
    bi.lpszTitle = lpNadpis;
  pidl= SHBrowseForFolder(&bi);
  if ( pidl )
  {
    if ( !SHGetPathFromIDList(pidl, lpVybranaSlozka) )
      bResult = FALSE;
    pMalloc->Free(pidl);
  }
  else
    bResult = FALSE;
  pMalloc->Release();
  return bResult;
}

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