Есть два способа позволить пользователю просматривать папки. Более тяжелый
путь — используя API-функцию SHBrowseForFolder . Есть
простой путь, использовать VCL-функцию SelectFolder.
SelectFolder документирована в help к BCB. Эта страница объясняет, как использовать
функцию SHBrowseForFolder в проекте C++Builder . Рисунок 1 показывает диалоговое
окно, которое появляется при вызове SHBrowseForFolder.
SHBrowseForFolder - часть API интерфейса оболочки. Прототип для функции расположен в SHLOBJ.H. Подобно большинству функций API, SHBrowseForFolder берет большую структуру как параметр. Название типа структуры — BROWSEINFO. Вы заполняете эту структуру, чтобы указать, как будет вести себя диалог. После того, как пользователь закрывает диалог, SHBrowseForFolder возвращает указатель на идентификатор (PIDL), который указывает, какая папка была выбрана.
Один запутывающий аспект SHBrowseForFolder — то, что она не возвращает путь каталога к папке, которую пользователь выбрал. Можно было предположить, что структура BROWSEINFO будет содержать строку пути каталога, но увы. Как упомянуто ранее, SHBrowseForFolder возвращает PIDL. API содержит функцию по имени SHGetPathFromIDList, которая может преобразовать PIDL в строку пути каталога.
Код ниже объясняет, как использовать SHBrowseForFolder.
#include <shlobj.h> void __fastcall TForm1::Button1Click(TObject *Sender) { BROWSEINFO info; char szDir[MAX_PATH]; char szDisplayName[MAX_PATH]; LPITEMIDLIST pidl; LPMALLOC pShellMalloc; // SHBrowseForFolder возвращает PIDL. Память для PIDL
// распределяется оболочкой. В итоге, мы должны очистить // эту память, так как мы должны получить указатель на объект
// shell malloc COМ который будет позже освобожден PIDL. if(SHGetMalloc(&pShellMalloc) == NO_ERROR) { //если мы получили оболочку malloc объекта,
// тогда продолжите, инициализируя структуру BROWSEINFO memset(&info, 0x00,sizeof(info)); info.hwndOwner = Handle; // Owner window info.pidlRoot = 0; // root folder info.pszDisplayName = szDisplayName; // return display name info.lpszTitle = "Browse Title"; // label caption info.ulFlags = BIF_RETURNONLYFSDIRS; // config flags info.lpfn = 0; // callback function // вызовем диалог pidl = SHBrowseForFolder(&info); // pidl будет null если пользователь нажмет cancel в диалоге выбора
// pidl не будет null если пользователь выбрал папку if(pidl) { // попытаемся преобразовать pidl в строку // при успешном преобразовании возвращаемое значение — true
if(SHGetPathFromIDList(pidl, szDir)) { // set one caption to the directory path Label1->Caption = szDir; } // set another caption based on the display name Label2->Caption = info.pszDisplayName; // Освобождим pidl. // затем вызовем Relasee для сигнализирования, что мы больше // не нуждаемся в shell malloc object pShellMalloc->Free(pidl); } pShellMalloc->Release(); } }
SHBrowseForFolder возвращает объект
PIDL. PIDL распределяется программой распределения задачи оболочки. Когда Вы
закончили использовать PIDL, вы должны освободить. Чтобы должным образом освободить
объект PIDL, Вы должны получить ссылку к программе распределения задачи оболочки,
вызывая функцию SHGetMalloc. Вы можете тогда использовать программу распределения
задачи, чтобы освободить PIDL.
SHGetMalloc возвращает указатель на IMalloc COM-объект.
IMalloc COM-интерфейс документирован в справке OLE Reference MS help, который
идет с C++Builder. Если Вы не работали с COM, то думайте о IMalloc объекте COM,
как об обычном объекте C++, который имеет методы, которые Вы можете вызвать.
Метод, в котором мы нуждаемся - Free. Он освобождает
объект PIDL. Когда Вы закончили использование task alloctor, вызовите метод
Release объекта IMalloc. Псевдокод ниже изображает
отношения между методами COM и кодом C++, с которым мы более знакомы
COM code C++ psuedo-equivalent LPMALLOC pShellMalloc; // think of pShellMalloc as an object that SHGetMalloc(&pShellMalloc); // handles new and delete for OS shell tasks LPITEMIDLIST pidl; Tpidl *pidl; pidl = SHBrowseForFolder(...); pidl = new Tpidl(); pShellMalloc->Free(pidl); delete pidl; pShellMalloc->Release();
Структура BROWSEINFO позволяет Вам управлять видом и поведением диалога SHBrowseForFolder. Структура BROWSEINFO описана ниже.
typedef struct { HWND hwndOwner; LPCITEMIDLIST pidlRoot; LPSTR pszDisplayName; LPCSTR lpszTitle; UINT ulFlags; BFFCALLBACK lpfn; LPARAM lParam; int iImage; } BROWSEINFO;
hwndOwner: Определяет дескриптор окна владельца диалога. Если Вы указываете владельца, диалог будет вести себя модально относительно окна владельца. Пользователь не будет способен взаимодействовать с вашей программой, в то время как диалог отображен. Также, когда Вы определяете владельца, панель задач не будет отображать отдельный значок для диалога просмотра. Если Вы устанавливаете hwndOwner в NULL, то диалог просмотра ведет себя подобно отдельному окну, которое — не является частью вашей программы. Пользователи все еще будут способны взаимодействовать с вашей программой, и диалог просмотра будет иметь его собственный значок в панели задач. .
pidlRoot: Определяет корень PIDL, или каталог, диалога. pidlRoot действует подобно основной папке для диалога. Пользователь не может выйти вне указанной папки. Например, скажем, то, что Вы хотите, чтобы ваши пользователи выбрали папку на диске C: . Вы можете получить PIDL для диска C:\ , и затем назначаете этот PIDL на pidlRoot член BROWSEINFO. Диалог просмотра не позволит пользователю выбирать вне корневого каталога диска C:.
pszDisplayName: Диалог пишет заголовок выбранной папки в pszDisplayName BROWSEINFO. Вы должны указать на буфер, который может содержать не менее MAX_PATH символов. Обратите внимание, что pszDisplayName — не та же самая вещь как путь к папке.
lpszTitle: Этот параметр позволяет Вам определять текст, который диалог отобразит выше дерева каталога (см. Рисунок 1).
ulFlags: Контролирует тип папки, которую пользователь может выбрать. Возможные значения следующие:
BIF_BROWSEFORCOMPUTER Browses only for computers (network neighborhood). BIF_BROWSEFORPRINTER Browses for network printers (somewhat useless). BIF_DONTGOBELOWDOMAIN Prevents display of network folders below domain level. BIF_RETURNFSANCESTORS Returns file system items (drives and directories). BIF_RETURNONLYFSDIRS Returns file system ancestors (so what are those??). BIF_STATUSTEXT Displays a label on the browse dialog.
Будьте внимательны, когда пытаетесь управлять SHBrowseFolder, изменяя ulFlags. Вы можете не получить результаты, которые Вы ожидаете. Например, если Вы определяете флажок BIF_BROWSEFORPRINTER, Вы могли бы ожидать, что пользователь будет способен выбрать локальный принтер, который связан непосредственно с компьютером. Это, оказывается, ложно. BIF_BROWSEFORPRINTER только позволяет пользователю просматривать сетевые принтера.
lpfn: Указатель на процедуру обратного вызова(callback). Пример показан ниже.
lParam: Значение, используемое в callback routine.
iImage: Папка, которая выбрана SHBrowseForFolder, будет иметь связанную иконку некоторого вида. Когда SHBrowseForFolder закрывается, iImage содержит целочисленное значение. Это значение — индекс выбранной папки в system imagelist. Если Вы не знаете о system imagelist, см. Отображение иконок, используемых Windows
Два из BROWSEINFO членов принадлежат некоторому мистическому обратному вызову.
Так, о чем идет речь ? Процедура обратного вызова существует, и Вы можете настроить
поведение диалога SHBrowseForFolder. Например, если Вы не понимаете, почему
ulFlags параметр, кажется, не ведет себя правильно, Вы можете взять под свой
контроль диалог самостоятельно, используя обратный вызов. Обратный вызов позволяет
Вам включать и отключать кнопку OK диалога. Вы можете также управлять диалогом
в определенной папке или устанавливать текст состояния.
Вот — пример кода, который демонстрирует, как использовать функцию обратного вызова диалога. Этот код устанавливает текст строки состояния диалога, и инициализирует начальный каталог диалога. Код также отслеживает текущий выбор в диалоге и отображает путь папки в Label.
int __stdcall BrowseProc(HWND hwnd,UINT uMsg, LPARAM lParam, LPARAM lpData ) { char szDir[MAX_PATH]; switch(uMsg) { case BFFM_INITIALIZED: SendMessage(hwnd, BFFM_SETSTATUSTEXT,0, (LPARAM)"Greetings"); // Set the initial directory. If WPARAM is TRUE, then LPARAM is a // string that contains the path. If WPARAM is FALSE, then LPARAM // should be a lovely PIDL SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)"C:\\Delphi4"); break; case BFFM_SELCHANGED: if(SHGetPathFromIDList((LPITEMIDLIST)lParam, szDir)) Form1->Label3->Caption = szDir; break; } return 0; } void __fastcall TForm1::Button1Click(TObject *Sender) { BROWSEINFO info; char szDir[MAX_PATH]; char szDisplayName[MAX_PATH]; LPITEMIDLIST pidl; LPMALLOC pShellMalloc; if(SHGetMalloc(&pShellMalloc) == NO_ERROR) { memset(&info, 0x00, sizeof(info)); info.hwndOwner = 0; info.pidlRoot = NULL; info.pszDisplayName = szDisplayName; info.lpszTitle = "Browse Title"; info.ulFlags = BIF_RETURNONLYFSDIRS|BIF_STATUSTEXT; info.lpfn = BrowseProc; // callback function pidl = SHBrowseForFolder(&info); if(pidl) { if(SHGetPathFromIDList(pidl, szDir)) { Label1->Caption = szDir; } Label2->Caption = info.pszDisplayName; pShellMalloc->Free(pidl); pShellMalloc->Release(); } } }