Хм. Вообще рассказывать хорошо, но, как кто-то сказал (и теперь очень многие на него ссылаются :)), программисту не нужны комментарии - у него есть текст программы. Так это конечно, но все же я откомментирую нижеследующий текст, то есть листинг:
//---------------------------------- #define NO_WIN32_LEAN_AND_MEAN #include <shlobj.h> #include <vcl.h> #pragma hdrstop #include "FolderDialog.h" #pragma package(smart_init) //---------------------------------- // ValidCtrCheck is used to assure that // the components created do not have // any pure virtual functions. // static inline void ValidCtrCheck(TFolderDialog *) { new TFolderDialog(NULL); } //---------------------------------- __fastcall TFolderDialog::TFolderDialog(TComponent* Owner) : TCommonDialog(Owner) { } int __stdcall FolderBrowseProc(HWND hwnd,UINT uMsg,LPARAM lParam,LPARAM lpData) { if (!lpData)return 0; TFolderDialog* dialog = (TFolderDialog*)lpData; dialog->FHandle = hwnd; if (uMsg == BFFM_SELCHANGED) { char buf[MAX_PATH]; SHGetPathFromIDList(LPITEMIDLIST(lParam),buf); if (dialog->FOnFolderChanged) dialog->OnFolderChanged(dialog, buf); } else if ((uMsg == BFFM_INITIALIZED)&&(dialog->FOnShow)) dialog->OnShow(dialog); return 0; }; //---------------------------------- bool __fastcall TFolderDialog::Execute(void) { BROWSEINFO bi; setmem(&bi, sizeof(bi), 0); bi.hwndOwner = NULL; bi.lpszTitle = Title.c_str(); bi.lpfn = FolderBrowseProc; bi.lParam = (long)this; int flags = 0; if(FOptions.Contains(fdBrowseForComputer)) flags|=BIF_BROWSEFORCOMPUTER; if(FOptions.Contains(fdBrowseForPrinter)) flags|=BIF_BROWSEFORPRINTER; if(FOptions.Contains(fdDontGoBelowDomain)) flags|=BIF_DONTGOBELOWDOMAIN; if(FOptions.Contains(fdReturnOnlySysAncestors)) flags|=BIF_RETURNFSANCESTORS; if(FOptions.Contains(fdReturnOnlySysDirs)) flags|=BIF_RETURNONLYFSDIRS; if(FOptions.Contains(fdStatusText)) flags|=BIF_STATUSTEXT; bi.ulFlags = flags; LPITEMIDLIST lst = SHBrowseForFolder(&bi); FHandle = 0; if(FOnClose) OnClose(this); if(!lst) return false; char buf[MAX_PATH]; SHGetPathFromIDList(lst,buf); FFolder = buf; return true; } //---------------------------------- void __fastcall TFolderDialog::SetStatusText(AnsiString text) { FStatusText = text; if(Handle) ::SendMessage(Handle,BFFM_SETSTATUSTEXT,NULL,(long)text.c_str()); } //---------------------------------- void __fastcall TFolderDialog::SetFolder(AnsiString folder) { if(Handle) ::SendMessage(Handle,BFFM_SETSELECTION,TRUE,(long)folder.c_str()); } //---------------------------------- void __fastcall TFolderDialog::SetOkEnabled(bool enabled) { FOkEnabled = enabled; enabled = (enabled)?(TRUE):(FALSE); if(Handle) ::SendMessage(Handle,BFFM_ENABLEOK,NULL,enabled); } //---------------------------------- namespace Folderdialog { void __fastcall PACKAGE Register() { TComponentClass classes[1] = {__classid(TFolderDialog)}; RegisterComponents("Samples", classes, 0); } } //----------------------------------
Хы. А кто говорил, что будет легко? Но, на мой взгляд, это самый полнофункциональный вариант использования этой функции шела. Надо сказать, здесь я не стал использовать отдельно функции преобразования списков идентификаторов. Каркас реализации - тот же, что и при прямом использования функции, который мы обсуждали в прошлых шагах. Он виден в реализации функции Execute. Единственная ремарка. Функции (а соответственно и свойства StatusText, Folder, OkEnabled) работают только при наличии открытого диалога. Поэтому их можно использовать в обработчиках событий, но никак не до инициализации.
Вобщем-то честно говоря, код не самый сложный, вполне можно разобраться... Так что разбирайтесь.
А. Самое главное. Чтобы установить компонент, дважды щелкните по значку проекта пакета. Откроется следующее окно:
Жмем Compile, а потом Install. И наслаждаемся результатом своей работы...