Wyliczanie zasobów.
Czym są zasoby nie muszę chyba nikomu tłumaczyć, w tej poradzie pokażę jak
wyliczyć zasoby. Skupię się na wyliczaniu zasobów według typu i nazwy zasobu, bo
to jest tutaj w zasadzie najważniejsze, szczególnie jeżeli chce się poddać
jakieś zasoby edycji, ale o edycji zasobów w innej poradzie.
Do wyliczania zasobów według typu służy funkcja: BOOL EnumResourceTypes
(HMODULE hModule, ENUMRESTYPEPROC lpEnumFunc, LONG_PTR lParam);
a do wyliczania zasobów na podstawie nazwy służy funkcja: BOOL EnumResourceNames(HMODULE hModule, LPCTSTR lpszType,
ENUMRESNAMEPROC lpEnumFunc, LONG_PTR lParam);
łącząc ze sobą te dwie funkcje otrzymamy jako wynik spis zasobów według typu z
przypisanymi do każdego typu nazwami zasobów. Jeżeli ktoś korzystał z programu
Resource Hacker to zauważył tam na pewno po lewej stronie drzewo zasobów, to
drzewo to obiekt typu TTreeView.
Pokażę tutaj jak zrobić dokładnie takie samo drzewo zasobów, czyli należy
umieścić na formularzu obiekt TreeView1. Zanim zaprezentuję kod, trochę
wyjaśnień.
W plikach zasobów np.*.EXE i w bibliotekach np.DLL występuje wiele rodzajów
zasobów, a funkcje wyliczą tutaj wszystkie zasoby, więc w zasadzie wszystko co
znajduje się w takim pliku jest zasobem, a więc wszelkie kursory i ikony, nie
tylko te dodane np. przez Ciebie do programu w postaci zasobów, ale również te
występujące w komponentach dodanych przez program.
Funkcje wyliczą wszystkie zasoby, a niektóre typy i nazwy zasobów będą posiadały
tylko numer i tak np. typ zasobu o numerze 1 oznacza Cursor i jest to typ zasobu
RT_CURSOR itp. Jak więc widać funkcje wyliczające nie zwracają nazwy typu
zasobu, lecz jego numer w tablicy zasobów, za wyjątkiem pewnych szczególnych
typów jak np. "WAVE". Jeżeli chcemy, żeby funkcje zwracały nazwy zamiast
numerów, no to trzeba je wyposażyć w odpowiedni kod tłumaczący. Ja zawrę tutaj
tylko te najbardziej oczywiste nazwy typów zasobów, a pozostałe będę
reprezentowane przez numer.
Przedstawiam kompletny kod wyliczający zasoby:
| //-------------------------------- #include <stdio.h> __int8 node; // ośmiobitowa zakree od -127 do 127, można też użyć po prostu __int16 lub int itp... BOOL WINAPI EnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, TTreeView *TreeView) { char buffer[100]; if((ULONG)lpName & 0xFFFF0000) { sprintf(buffer, "%s", lpName); } else { sprintf(buffer, "%u", (USHORT)lpName); } TreeView->Items->AddChild(TreeView->Items->Item[node], (String)buffer); return true; } //--------------------------------------------------------------------------- BOOL WINAPI EnumTypesFunc(HMODULE hModule, LPTSTR lpType, TTreeView *TreeView) { char buffer[100]; if((ULONG)lpType & 0xFFFF0000) { sprintf(buffer, "%s", lpType); } else { sprintf(buffer, "%u", (USHORT)lpType); } int nr; String name; try { nr = StrToInt((String)buffer); name = ((String)buffer); } catch(...) { nr = -1; name = (String)buffer; } if(nr >= 0) { switch(nr) { case 1: name = "Kursor"; break; case 2: name = "Bitmapa"; break; case 3: name = "Ikona"; break; case 5: name = "Dialog"; break; case 6: name = "String Table"; break; case 10: name = "RCDATA"; break; case 12: name = "Zbiór Kursorów"; break; case 14: name = "Zbiór Ikon"; break; case 24: name = "MANIFEST"; break; } } TreeView->Items->Add(0, name); node = TreeView->Items->Count - 1; EnumResourceNames(hModule, lpType, (ENUMRESNAMEPROC)EnumNamesFunc, (LONG)TreeView); return true; } //--------------------------------------------------------------------------- void __fastcall TForm1::Button3Click(TObject *Sender) { String FileName = "c:\\Projekty\\Project1\\Project1.exe"; // plik z zasobami if(!FileExists(FileName)) ShowMessage("BŁĄD! Nie znaleziono pliku"); HANDLE hfile = LoadLibrary(FileName.c_str()); if(hfile != NULL) { EnumResourceTypes(hfile, (ENUMRESTYPEPROC)EnumTypesFunc, (LONG)TreeView1); FreeLibrary(hfile); } } //-------------------------------- |
Na zakończenie jeszcze przykład wyliczania zasobów określonego typu, do tego celu trzeba trochę zmodyfikować funkcję EnumTypesFunc:
| BOOL WINAPI EnumTypesFunc(HMODULE hModule, LPTSTR lpType,
TTreeView *TreeView) { char buffer[100]; if((ULONG)lpType & 0xFFFF0000) { sprintf(buffer, "%s", lpType); } else { sprintf(buffer, "%u", (USHORT)lpType); } int nr; String name; try { nr = StrToInt((String)buffer); name = ((String)buffer); } catch(...) { nr = -1; name = (String)buffer; } if(nr >= 0) { switch(nr) { case 1: name = "Kursor"; break; case 2: name = "Bitmapa"; break; case 3: name = "Ikona"; break; case 5: name = "Dialog"; break; case 6: name = "String Table"; break; case 10: name = "RCDATA"; break; case 12: name = "Zbiór Kursorów"; break; case 14: name = "Zbiór Ikon"; break; case 24: name = "MANIFEST"; break; } } if((String)buffer == "WAVE" || (String)buffer == (USHORT)RT_CURSOR) { TreeView->Items->Add(0, name); node = TreeView->Items->Count - 1; EnumResourceNames(hModule, lpType, (ENUMRESNAMEPROC)EnumNamesFunc, (LONG)TreeView); } return true; } |
Edycja zasobów (modyfikacja, usuwanie, wstawianie).
Edycja zasobów jeszcze na etapie programowania jest zadaniem banalnie prostym,
dlatego ta porada jest o tym jak edytować zasoby w plikach zasobów np. *.EXE, *.DLL,
bez udziału kompilatora, czyli w plikach już skompilowanych z poziomu własnego
programu. Zaznaczę tutaj od razu, że program edytujący zasoby, nie może edytować
sam siebie, czyli mówiąc jeszcze prościej działający program, uruchomiony, czyli
aktywny proces nie może zmienić nic w swoim własnym pliku, dzieje się tak
dlatego, że nie można nic zmieniać w aktywnym procesie, a każdy uruchomiony plik
*.exe staje się aktywnym procesem, więc program edytujący zasoby nie zmieni nic
nie tylko we własnym pliku (*.exe), ale również nie będzie w stanie tego zrobić
w żadnym innym uruchomionym pliku *.EXE.
Do edycji zasobów służy funkcja UpdateResource pobierająca sześć
argumentów, i tak pierwszy w kolejności to uchwyt do pliku zawierającego zasoby,
które chcemy zmienić, drugi to typ zmienianego zasobu, trzeci to nazwa zasobu,
czwarty to identyfikator języka zasobu, który chcemy zmienić, piąty to wskaźnik
do zasobu który zamieni zamieni oryginalny zasób w pliku, a szósty to rozmiar
tego zasobu. Funkcja UpdateResource działa w połączeniu z dwiema innymi
funkcjami BeginUpdateResource, która zwraca uchwyt do pliku zawierającego
zasoby, które będziemy edytować i EndUpdateResurce kończąca dokonywanie
zmian w pliku z zasobami i zapisująca te zmiany. Zapisująca, gdyż funkcja
UpdateResource dokonuje zmian tylko w pamięci, a dopiero wywołanie funkcji
EndUpdateResource dokonuje tych zmian w pliku.
Zasób który chcemy podmienić w pliku zasobów trzeba wczytać do pamięci, gdyż nie
można go od razu "władować" do pliku, dlatego należy stworzyć obiekt klasy
TMemoryStream wczytując do niego podmieniany zasób. Co do identyfikatora
języka zasobu (dotyczy tylko niektórych typów zasobu), to trzeba podawać
prawidłowy identyfikator języka, natomiast jako sublanguage można podać
SUBLANG_DEFAULT o ile dany język nie ma jakiejś swojej odmiany, w przykładzie
podałem dane neutralne
| //-------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { TMemoryStream *memory = NULL; String FileName = ExtractFilePath(ParamStr(0)) + "test.exe"; // plik z zasobami if(!FileExists(FileName)) { ShowMessage("BŁĄD! NIe odnaleziono pliku"); return; } HANDLE hfile = BeginUpdateResource(FileName.c_str(), false); if(hfile == NULL ) return; memory = new TMemoryStream(); memory->LoadFromFile(ExtractFilePath(ParamStr(0)) + "music1.wav"); if(UpdateResource(hfile, "WAVE", "ID_SONG1", MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), memory->Memory, memory->Size)) { EndUpdateResource(hfile, false); } if(memory != NULL) delete memory; } //--------------------------------------------------------------------------- |
Tablice Language i SubLanguage:
| Identifier | Predefined symbol | Language |
|---|---|---|
| 0x00 | LANG_NEUTRAL | Neutral |
| 0x01 | LANG_ARABIC | Arabic |
| 0x02 | LANG_BULGARIAN | Bulgarian |
| 0x03 | LANG_CATALAN | Catalan |
| 0x04 | LANG_CHINESE | Chinese |
| 0x05 | LANG_CZECH | Czech |
| 0x06 | LANG_DANISH | Danish |
| 0x07 | LANG_GERMAN | German |
| 0x08 | LANG_GREEK | Greek |
| 0x09 | LANG_ENGLISH | English |
| 0x0a | LANG_SPANISH | Spanish |
| 0x0b | LANG_FINNISH | Finnish |
| 0x0c | LANG_FRENCH | French |
| 0x0d | LANG_HEBREW | Hebrew |
| 0x0e | LANG_HUNGARIAN | Hungarian |
| 0x0f | LANG_ICELANDIC | Icelandic |
| 0x10 | LANG_ITALIAN | Italian |
| 0x11 | LANG_JAPANESE | Japanese |
| 0x12 | LANG_KOREAN | Korean |
| 0x13 | LANG_DUTCH | Dutch |
| 0x14 | LANG_NORWEGIAN | Norwegian |
| 0x15 | LANG_POLISH | Polish |
| 0x16 | LANG_PORTUGUESE | Portuguese |
| 0x18 | LANG_ROMANIAN | Romanian |
| 0x19 | LANG_RUSSIAN | Russian |
| 0x1a | LANG_CROATIAN | Croatian |
| 0x1a | LANG_SERBIAN | Serbian |
| 0x1b | LANG_SLOVAK | Slovak |
| 0x1c | LANG_ALBANIAN | Albanian |
| 0x1d | LANG_SWEDISH | Swedish |
| 0x1e | LANG_THAI | Thai |
| 0x1f | LANG_TURKISH | Turkish |
| 0x20 | LANG_URDU | Urdu |
| 0x21 | LANG_INDONESIAN | Indonesian |
| 0x22 | LANG_UKRAINIAN | Ukrainian |
| 0x23 | LANG_BELARUSIAN | Belarusian |
| 0x24 | LANG_SLOVENIAN | Slovenian |
| 0x25 | LANG_ESTONIAN | Estonian |
| 0x26 | LANG_LATVIAN | Latvian |
| 0x27 | LANG_LITHUANIAN | Lithuanian |
| 0x29 | LANG_FARSI | Farsi |
| 0x2a | LANG_VIETNAMESE | Vietnamese |
| 0x2b | LANG_ARMENIAN | Armenian |
| 0x2c | LANG_AZERI | Azeri |
| 0x2d | LANG_BASQUE | Basque |
| 0x2f | LANG_MACEDONIAN | FYRO Macedonian |
| 0x36 | LANG_AFRIKAANS | Afrikaans |
| 0x37 | LANG_GEORGIAN | Georgian |
| 0x38 | LANG_FAEROESE | Faeroese |
| 0x39 | LANG_HINDI | Hindi |
| 0x3e | LANG_MALAY | Malay |
| 0x3f | LANG_KAZAK | Kazak |
| 0x40 | LANG_KYRGYZ | Kyrgyz |
| 0x41 | LANG_SWAHILI | Swahili |
| 0x43 | LANG_UZBEK | Uzbek |
| 0x44 | LANG_TATAR | Tatar |
| 0x45 | LANG_BENGALI | Not supported. |
| 0x46 | LANG_PUNJABI | Punjabi |
| 0x47 | LANG_GUJARATI | Gujarati |
| 0x48 | LANG_ORIYA | Not supported. |
| 0x49 | LANG_TAMIL | Tamil |
| 0x4a | LANG_TELUGU | Telugu |
| 0x4b | LANG_KANNADA | Kannada |
| 0x4c | LANG_MALAYALAM | Not supported. |
| 0x4d | LANG_ASSAMESE | Not supported. |
| 0x4e | LANG_MARATHI | Marathi |
| 0x4f | LANG_SANSKRIT | Sanskrit |
| 0x50 | LANG_MONGOLIAN | Mongolian |
| 0x56 | LANG_GALICIAN | Galician |
| 0x57 | LANG_KONKANI | Konkani |
| 0x58 | LANG_MANIPURI | Not supported. |
| 0x59 | LANG_SINDHI | Not supported. |
| 0x5a | LANG_SYRIAC | Syriac |
| 0x60 | LANG_KASHMIRI | Not supported. |
| 0x61 | LANG_NEPALI | Not supported. |
| 0x65 | LANG_DIVEHI | Divehi |
| 0x7f | LANG_INVARIANT |
| Identifier | Predefined symbol | Language |
|---|---|---|
| 0x00 | SUBLANG_NEUTRAL | Language neutral |
| 0x01 | SUBLANG_DEFAULT | User Default |
| 0x02 | SUBLANG_SYS_DEFAULT | System Default |
| 0x01 | SUBLANG_ARABIC_SAUDI_ARABIA | Arabic (Saudi Arabia) |
| 0x02 | SUBLANG_ARABIC_IRAQ | Arabic (Iraq) |
| 0x03 | SUBLANG_ARABIC_EGYPT | Arabic (Egypt) |
| 0x04 | SUBLANG_ARABIC_LIBYA | Arabic (Libya) |
| 0x05 | SUBLANG_ARABIC_ALGERIA | Arabic (Algeria) |
| 0x06 | SUBLANG_ARABIC_MOROCCO | Arabic (Morocco) |
| 0x07 | SUBLANG_ARABIC_TUNISIA | Arabic (Tunisia) |
| 0x08 | SUBLANG_ARABIC_OMAN | Arabic (Oman) |
| 0x09 | SUBLANG_ARABIC_YEMEN | Arabic (Yemen) |
| 0x0a | SUBLANG_ARABIC_SYRIA | Arabic (Syria) |
| 0x0b | SUBLANG_ARABIC_JORDAN | Arabic (Jordan) |
| 0x0c | SUBLANG_ARABIC_LEBANON | Arabic (Lebanon) |
| 0x0d | SUBLANG_ARABIC_KUWAIT | Arabic (Kuwait) |
| 0x0e | SUBLANG_ARABIC_UAE | Arabic (U.A.E.) |
| 0x0f | SUBLANG_ARABIC_BAHRAIN | Arabic (Bahrain) |
| 0x10 | SUBLANG_ARABIC_QATAR | Arabic (Qatar) |
| 0x01 | SUBLANG_AZERI_LATIN | Azeri (Latin) |
| 0x02 | SUBLANG_AZERI_CYRILLIC | Azeri (Cyrillic) |
| 0x01 | SUBLANG_CHINESE_TRADITIONAL | Chinese (Traditional) |
| 0x02 | SUBLANG_CHINESE_SIMPLIFIED | Chinese (Simplified) |
| 0x03 | SUBLANG_CHINESE_HONGKONG | Chinese (Hong Kong SAR, PRC) |
| 0x04 | SUBLANG_CHINESE_SINGAPORE | Chinese (Singapore) |
| 0x05 | SUBLANG_CHINESE_MACAU | Chinese (Macao SAR) |
| 0x01 | SUBLANG_DUTCH | Dutch |
| 0x02 | SUBLANG_DUTCH_BELGIAN | Dutch (Belgian) |
| 0x01 | SUBLANG_ENGLISH_US | English (US) |
| 0x02 | SUBLANG_ENGLISH_UK | English (UK) |
| 0x03 | SUBLANG_ENGLISH_AUS | English (Australian) |
| 0x04 | SUBLANG_ENGLISH_CAN | English (Canadian) |
| 0x05 | SUBLANG_ENGLISH_NZ | English (New Zealand) |
| 0x06 | SUBLANG_ENGLISH_EIRE | English (Ireland) |
| 0x07 | SUBLANG_ENGLISH_SOUTH_AFRICA | English (South Africa) |
| 0x08 | SUBLANG_ENGLISH_JAMAICA | English (Jamaica) |
| 0x09 | SUBLANG_ENGLISH_CARIBBEAN | English (Caribbean) |
| 0x0a | SUBLANG_ENGLISH_BELIZE | English (Belize) |
| 0x0b | SUBLANG_ENGLISH_TRINIDAD | English (Trinidad) |
| 0x0c | SUBLANG_ENGLISH_ZIMBABWE | English (Zimbabwe) |
| 0x0d | SUBLANG_ENGLISH_PHILIPPINES | English (Philippines) |
| 0x01 | SUBLANG_FRENCH | French |
| 0x02 | SUBLANG_FRENCH_BELGIAN | French (Belgian) |
| 0x03 | SUBLANG_FRENCH_CANADIAN | French (Canadian) |
| 0x04 | SUBLANG_FRENCH_SWISS | French (Swiss) |
| 0x05 | SUBLANG_FRENCH_LUXEMBOURG | French (Luxembourg) |
| 0x06 | SUBLANG_FRENCH_MONACO | French (Monaco) |
| 0x01 | SUBLANG_GERMAN | German |
| 0x02 | SUBLANG_GERMAN_SWISS | German (Swiss) |
| 0x03 | SUBLANG_GERMAN_AUSTRIAN | German (Austrian) |
| 0x04 | SUBLANG_GERMAN_LUXEMBOURG | German (Luxembourg) |
| 0x05 | SUBLANG_GERMAN_LIECHTENSTEIN | German (Liechtenstein) |
| 0x01 | SUBLANG_ITALIAN | Italian |
| 0x02 | SUBLANG_ITALIAN_SWISS | Italian (Swiss) |
| 0x01 | SUBLANG_KOREAN | Korean |
| 0x01 | SUBLANG_LITHUANIAN | Lithuanian |
| 0x01 | SUBLANG_MALAY_MALAYSIA | Malay (Malaysia) |
| 0x02 | SUBLANG_MALAY_BRUNEI_DARUSSALAM | Malay (Brunei Darassalam) |
| 0x01 | SUBLANG_NORWEGIAN_BOKMAL | Norwegian (Bokmal) |
| 0x02 | SUBLANG_NORWEGIAN_NYNORSK | Norwegian (Nynorsk) |
| 0x01 | SUBLANG_PORTUGUESE_BRAZILIAN | Portuguese (Brazil) |
| 0x02 | SUBLANG_PORTUGUESE | Portuguese (Portugal) |
| 0x02 | SUBLANG_SERBIAN_LATIN | Serbian (Latin) |
| 0x03 | SUBLANG_SERBIAN_CYRILLIC | Serbian (Cyrillic) |
| 0x01 | SUBLANG_SPANISH | Spanish (Castilian) |
| 0x02 | SUBLANG_SPANISH_MEXICAN | Spanish (Mexican) |
| 0x03 | SUBLANG_SPANISH_MODERN | Spanish (Spain) |
| 0x04 | SUBLANG_SPANISH_GUATEMALA | Spanish (Guatemala) |
| 0x05 | SUBLANG_SPANISH_COSTA_RICA | Spanish (Costa Rica) |
| 0x06 | SUBLANG_SPANISH_PANAMA | Spanish (Panama) |
| 0x07 | SUBLANG_SPANISH_DOMINICAN_REPUBLIC | Spanish (Dominican Republic) |
| 0x08 | SUBLANG_SPANISH_VENEZUELA | Spanish (Venezuela) |
| 0x09 | SUBLANG_SPANISH_COLOMBIA | Spanish (Colombia) |
| 0x0a | SUBLANG_SPANISH_PERU | Spanish (Peru) |
| 0x0b | SUBLANG_SPANISH_ARGENTINA | Spanish (Argentina) |
| 0x0c | SUBLANG_SPANISH_ECUADOR | Spanish (Ecuador) |
| 0x0d | SUBLANG_SPANISH_CHILE | Spanish (Chile) |
| 0x0e | SUBLANG_SPANISH_URUGUAY | Spanish (Uruguay) |
| 0x0f | SUBLANG_SPANISH_PARAGUAY | Spanish (Paraguay) |
| 0x10 | SUBLANG_SPANISH_BOLIVIA | Spanish (Bolivia) |
| 0x11 | SUBLANG_SPANISH_EL_SALVADOR | Spanish (El Salvador) |
| 0x12 | SUBLANG_SPANISH_HONDURAS | Spanish (Honduras) |
| 0x13 | SUBLANG_SPANISH_NICARAGUA | Spanish (Nicaragua) |
| 0x14 | SUBLANG_SPANISH_PUERTO_RICO | Spanish (Puerto Rico) |
| 0x01 | SUBLANG_SWEDISH | Swedish |
| 0x02 | SUBLANG_SWEDISH_FINLAND | Swedish (Finland) |
| 0x01 | SUBLANG_URDU_PAKISTAN | Urdu (Pakistan) |
| 0x02 | SUBLANG_URDU_INDIA | Urdu (India) |
| 0x01 | SUBLANG_UZBEK_LATIN | Uzbek (Latin) |
| 0x02 | SUBLANG_UZBEK_CYRILLIC | Uzbek (Cyrillic) |
Nie
wszystkie zasoby można jednak w tak prosty sposób modyfikować, działa to
bez problemu na bitmapach i zasobach typu WAVE, ale np. z ikonami i
kursorami są już problemy, tutaj wymagana jest specjalna technika o
której nie będzie w tej poradzie.
Funkcja UpdateResource pozwala również na dodawania nowych
zasobów, wystarczy podać typ zasobu i nazwę zasobu której nie ma w pliku
zasobów, a taki zasób zostanie dodany. Jeżeli chcemy usuwać zasób to
jako piąty parametr funkcji UpdateResource podajemy wartość
NULL, a jako szósty 0, oczywiście trzeba podać istniejący typ
zasobu i nazwę,
Komedy: Pokaż pulpit, Właściwosci Paska Zadań itp...
Tym razem pokażę kod na wywoływanie komendy Pokaż pulpit, która
jest chyba wszystkim użytkownikom Windows dobrze znana. W celu wywołania
tej komendy trzeba się podłączyć do biblioteki Shell32.dll,
a można to zrobić poprzez utworzenie obiektu klasy IShellDispatch4,
a żeby to było możliwe trzeba włączyć do pliku źródłowego do sekcji
include bibliotekę shlobj.h. Biblioteka ta ma jednak to do
siebie, że wywołuje liczne błędy, dlatego na samym początku pliku
źródłowego trzeba dodać definicję NO_WIN32_LEAN_AND_MEAN.
Niżej przedstawiam kompletny kod na wywołanie polecenia Pokaż pulpit w
zdarzeniu OnClick przycisku Button1:
|
//--------------------------------------------------------------------------- #define NO_WIN32_LEAN_AND_MEAN #include <vcl.h> #pragma hdrstop #include "Unit1.h" #include <shlobj.h> //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { CoInitialize(NULL); IShellDispatch4* pShell4; CoCreateInstance(__uuidof(Shell), NULL, CLSCTX_INPROC_SERVER, __uuidof(IShellDispatch4), (void**)&pShell4); pShell4->ToggleDesktop(); pShell4->Release(); CoUninitialize(); } |
Po
naciśnięciu przycisku Button1 wszystkie okna zostaną zminimalizowane,
problem jednak w tym, że okno naszego programu również, a żeby je
ponownie przywrócić na pulpit hurtem, czyli wszystkie na raz, trzeba
ponownie wywołać ten kod, ale skoro nasze okno jest zminimalizowane, to
żeby nacisnąć na Button1 musimy je przywrócić i tutaj jest problem,
ponieważ po ponownym kliknięciu na Button1 nasze okno znów zostanie
zminimalizowane, a pozostałe okna nie zostaną przywrócone. Można obejść
ten problem poprzez minimalizację naszego programu do paska zadań, za
pomocą komponentu TTrayIcon i przypisaniu do niego PopupMenu z
poleceniem Pokaż pulpit, wtedy gdy klikniemy prawym klawiszem myszy na
ikonie naszego programu to pokaże się menu z którego będzie można wybrać
polecenie pokaż pulpit.
Trochę to skomplikowane, ale jest prostszy sposób, zamiast minimalizacji
wszystkich okien za pomocą funkcji ToggleDesktop(), można je
zminimalizować używając funkcji MinimizeAll(), a przywrócić za
pomocą funkcji UndoMinimizeAll():
|
//--------------------------------------------------------------------------- #define NO_WIN32_LEAN_AND_MEAN #include <vcl.h> #pragma hdrstop #include "Unit1.h" #include <shlobj.h> //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { CoInitialize(NULL); IShellDispatch4* pShell4; CoCreateInstance(__uuidof(Shell), NULL, CLSCTX_INPROC_SERVER, __uuidof(IShellDispatch4), (void**)&pShell4); pShell4->MinimizeAll(); pShell4->Release(); CoUninitialize(); } //--------------------------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { CoInitialize(NULL); IShellDispatch4* pShell4; CoCreateInstance(__uuidof(Shell), NULL, CLSCTX_INPROC_SERVER, __uuidof(IShellDispatch4), (void**)&pShell4); pShell4->UndoMinimizeAll(); pShell4->Release(); CoUninitialize(); } |
Klasa
IShellDispatch4 pozwala na wiele więcej można np. przywołać okno
Właściwości Paska zadań i Menu Start: pShell4->TrayProperties();
Kilka innych komend:
CascadeWindows() -
rozmieszcza kaskadowo okna na pulpicie
FileRun()
- wyświetla okno Uruchom (menu Start - Uruchom)
FindComputer()
- uruchamia okno wyszukiwania komputera
FindFiles()
- uruchamia okno wyszukiwania plików
SetTime()
- uruchamia okno Właściwości data i godzina
ShutdownWindows() -
przywołuje okno Zamykanie systemu Windows
Zmiana wyglądu dymka podpowiedzi (HINT)
Do zmiany wyglądu, czyli np. koloru, obramowania, stylu i rodzaju
czcionki dymka podpowiedzi tzw. Hint służy klasa specjalna
HintWindowClass, jednak skorzystanie z tej klasy wymaga stworzenia
innej klasy dziedziczącej po klasie THintWindow, dlatego też
musimy najpierw stworzyć taką klasę.
Tworzymy nowy projekt, a następnie z menu File | New | Unite,
zapisujemy nowo utworzone pliki *.cpp i *.h pod dowolną nazwą w katalogu
z projektem, np. pod nazwą Hintclass.*. Jeżeli pliki nie zostały
automatycznie dodane do projektu to wybieramy z menu Project | Add to
project i włączmy do projektu plik Hintclass.cpp, a w pliku
nagłówkowym głównego formularza programu umieszczamy wpis
#include "Hintclass.h".
Teraz przechodzimy do pliku Hintclass.h i tworzymy nową
klasę, ja nadałem jej nazwę TBlackHint, lecz nazwa jest dowolna:
|
//--------------------------------------------------------------------------- #ifndef HintclassH #define HintclassH #include <Classes.hpp> #include <Controls.hpp> #include <Forms.hpp> //--------------------------------------------------------------------------- class TBlackHint : public THintWindow { public: __fastcall TBlackHint(TComponent* Owner); private: bool FActivating; void __fastcall Paint(void); virtual void __fastcall NCPaint(HDC hdc); virtual void __fastcall CreateParams(TCreateParams &Params); virtual void __fastcall ActivateHint(const TRect &Rect, const String AHint); }; #endif |
Następnie przechodzimy do pliku Hintclass.cpp i umieszczamy w nim taki kod:
|
//--------------------------------------------------------------------------- #pragma hdrstop #include "Hintclass.h" //--------------------------------------------------------------------------- #pragma package(smart_init) __fastcall TBlackHint::TBlackHint(TComponent* Owner) : THintWindow(Owner) { Canvas->Font->Name = "Verdana"; Canvas->Font->Color = clWhite; Canvas->Font->Size = 8; } //--------------------------------------------------------------------------- void __fastcall TBlackHint::Paint(void) { Width = Canvas->TextWidth(Caption) + 16; Height = Canvas->TextHeight(Caption) + 7; Color = clBlack; TRect R = ClientRect; R.Left = R.Left + 1; R.Top = R.Top + 2; R.Right = R.Right - 1; R.Bottom = R.Bottom; Canvas->Pen->Color = (TColor)0x002868D8; Canvas->Rectangle(R.Left, R.Top - 2, R.Right + 1, R.Bottom); DrawText(Canvas->Handle, Caption.c_str(), -1, &R, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_WORDBREAK | DT_NOCLIP | DrawTextBiDiModeFlagsReadingOnly()); } //--------------------------------------------------------------------------- void __fastcall TBlackHint::NCPaint(HDC hdc) { Invalidate(); } //--------------------------------------------------------------------------- void __fastcall TBlackHint::CreateParams(TCreateParams &Params) { Params.Style = Params.Style & ~WS_BORDER; THintWindow::CreateParams(Params); } //--------------------------------------------------------------------------- void __fastcall TBlackHint::ActivateHint(const TRect &Rect, const String AHint) { FActivating = true; try { Caption = AHint; TRect r = Rect; r.Left -= 10; r.Right += 10; r.Top -= 5; r.Bottom += 5; UpdateBoundsRect(r); if(r.Top + Height > Screen->DesktopHeight) r.Top = Screen->DesktopHeight - Height; if(r.Left + Width > Screen->DesktopWidth) r.Left = Screen->DesktopWidth - Width; if(r.Left < Screen->DesktopLeft) r.Left = Screen->DesktopLeft; if(r.Bottom < Screen->DesktopTop) r.Bottom = Screen->DesktopTop; HRGN hrgn = CreateRectRgn(0, 0, r.Width(), r.Height()); SetWindowRgn(Handle, hrgn, true); SetWindowPos(Handle, HWND_TOPMOST, r.Left, r.Top, r.Width(), r.Height(), SWP_SHOWWINDOW | SWP_NOACTIVATE); Invalidate(); } __finally { FActivating = false; } } //--------------------------------------------------------------------------- |
W
konstruktorze klasy definiujemy takie parametry jak np. krój czcionki,
kolor rozmiar, styl itp. W zdarzeniu Paint określamy wygląd
dymka, jak np. kolor, rozmiar i położenie tekstu, możemy sobie tutaj
dowoli kombinować, ja w przykładzie zdefiniowałem czarne tło z
pomarańczową ramką i białą czcionką. W funkcji ActiveHint
określamy położenie dymka podpowiedzi na ekranie w odniesieniu do
kursora i na początek proponuję nie zmieniać nic w tym kodzie, bo dymek
może się wcale nie wyświetlić, lub pokaże się w zgoła nieoczekiwanym
miejscu.
Gdy pliki z klasą są już gotowe i włączone do projektu, w pliku
źródłowym głównego formularza w konstruktorze klasy, aktywujemy
nowy Hint:
| TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { HintWindowClass = __classid(TBlackHint); } //--------------------------------------------------------------------------- |
Na zakończenie dodam, że tak zmodyfikowany Hint działa tylko w naszym programie, a nie w całym systemie, a dokładniej tylko w oknach naszego programu, przy czym klasę podłączamy tylko w głównym oknie aplikacji, w pozostałych powinno działać automatycznie.
Otwieranie plików powiązanych z programem zawsze
tylko w otwartym programie.
Niniejsza porada stanowi uzupełnieni porady:
powiązanie własnego typu pliku
z własny programem. W tamtej poradzie zostało pokazane jak można
otworzyć plik we własnym programie korzystając z funkcji ParamStr.
Przedstawiony tam sposób ma jednak dość poważny mankament, jeżeli już
otworzymy w naszym programie jakiś plik i klikniemy na innym pliku
powiązanym z naszym programem, to oczywiście taki plik otworzy się w
naszym programie, a dokładnie rzecz biorąc w kolejnej kopii naszego
programu, a nie w już uruchomionym programie, czyli za każdym razem gdy
będziemy otwierać nowy plik to będzie się jednocześnie uruchamiała nowa
kopia naszego programu.
W tej poradzie pokażę jak to zmienić i otwierać nowe pliki w już
otwartym programie, bez uruchamiania kolejnych kopii, czyli zawsze w
jednej kopii. Mechanizm opiera się na utworzeniu mutexu
uniemożliwiającemu utworzenie kopii programu, oraz na wysłaniu do już
otwartego programu komunikatu zawierającego polecenie uruchomienia
pliku.
Początek jest trochę nietypowy, gdy już utworzymy nowy projekt wybieramy
z menu Project | View source, wyskoczy na strona z mniej więcej taką
zawartością:
|
//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop //--------------------------------------------------------------------------- USEFORM("Unit1.cpp", Dupa1); //--------------------------------------------------------------------------- WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { try { Application->Initialize(); Application->CreateForm(__classid(TForm1), &Form1); Application->Run(); } catch (Exception &exception) { Application->ShowException(&exception); } catch (...) { try { throw Exception(""); } catch (Exception &exception) { Application->ShowException(&exception); } } return 0; } //--------------------------------------------------------------------------- |
I to
właśnie tutaj dokonujemy drobnych modyfikacji, wstawiając mutex i
funkcje odpowiedzialne za wyszukanie otwartego już okna naszego programu
w oparciu o nazwę klasy naszego programu. Tutaj kilka wyjaśnień, mutex
to dowolna jednoczłonowa nazwa zawierająca tylko liczby i litery języka
angielskiego, nazwa ta musi być niepowtarzalną, czyli niespotykana w
innych programach. Druga rzecz to nazwa klasy naszego programu, również
musi być niepowtarzalna jeżeli nasz program ma być prawidłowo
odnaleziony. Nazwa klasy programu, to nazwa okna głównego programu,
czyli załóżmy że nasze okno główne nazywa się Form1, nazwa klasy będzie
wtedy TForm1. Żeby uniknąć błędu polegającego na powtarzaniu się klas
programów w systemie, o co nie trudno jeżeli wszystkim formularzom
głównym w naszych programach nadajemy nazwę Form1. Dlatego w przykładzie
posłużę się mutexem o nazwie MyAssMutex i formularzem o nazwie
MyAss, czyli klasa będzie nosiła nazwę TMyAss.
Po modyfikacji źródło projektu powinno wyglądać mniej więcej tak:
|
//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop //--------------------------------------------------------------------------- USEFORM("Unit1.cpp", MyAss); //--------------------------------------------------------------------------- WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR cmdLine, int) // w nagłówku tej funkcji również występuje drobna zmiana { try { HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, 0, "MyAssMutex"); if(!hMutex) hMutex = CreateMutex(0, 0, "MyAssMutex"); else { HWND hWnd = FindWindow("TMyAss", 0); SetForegroundWindow(hWnd); if(strlen(cmdLine) != 0) { COPYDATASTRUCT cds; cds.cbData = strlen(cmdLine) + 1; cds.lpData = StringReplace(cmdLine, "\"", "", TReplaceFlags() << rfReplaceAll).c_str(); SendMessage(hWnd, WM_COPYDATA, 0, (LPARAM)&cds); } return 0; } Application->Initialize(); Application->CreateForm(__classid(TMyAss), &MyAss); Application->Run(); ReleaseMutex(hMutex); } catch (Exception &exception) { Application->ShowException(&exception); } return 0; } //--------------------------------------------------------------------------- |
Jak widać po odnalezieniu okna program wysyła do niego komunikat (a dokładniej do "uruchomionego siebie") WM_COPYDATA wraz z parametrem zawierającym ścieżkę dostępu do otwieranego pliku, żeby program mógł odebrać ten komunikat trzeba w nim zaimplementować funkcję przechwytującą komunikaty. Ja posłużę się tutaj mapą komunikatów i funkcją nastawionymi na przechwytywanie właśnie komunikatu WM_COPYDATA. W tym celu w pliku nagłówkowym deklarujemy funkcję WmCopyData (nazwa dowolna), dodatkowo zadeklarujemy tutaj zmienną typu AnsiString OlPathFile zapamiętującą ścieżkę dostępu do już otwartego pliku. To się przydaje, jeżeli otwieramy np. w Memo jakiś plik tekstowy i chcemy, żeby przed otwarciem i wczytaniem do Memo nowego pliku program sprawdzał czy w starym zaszły jakieś zmiany i żeby nas pytał czy chcemy te zmiany zapisać, można też będzie anulować otwarcie nowego pliku. W sekcji public umieszczamy mapę komunikatu:
| // Plik nagłówkowy np. Unit1.h private: void __fastcall WmCopyData(TWMCopyData& Message); String OldPathFile; public: __fastcall TMyAss(TComponent* Owner); BEGIN_MESSAGE_MAP MESSAGE_HANDLER(WM_COPYDATA, TWMCopyData, WmCopyData) END_MESSAGE_MAP(TForm) }; |
W pliku nagłówkowym umieszczamy teraz definicję tej funkcji, a wewnątrz ciała funkcji umieszczamy również kod sprawdzający czy w poprzednio otwartym pliku zaszły zmiany, no i oczywiście kod odpowiedzialny za wczytanie pliku do Memo. Dodatkowo tworzymy również zdarzenie OnCreate w którym umieszczamy kod otwierający plik w Memo gdy uruchamiamy taki plik w naszym programie, a ten program nie został jeszcze uruchomiony. Działa to tak, że jeżeli klikamy na jakimś pliku powiązanym z naszym programem, a ten program nie został jeszcze uruchomiony, to wykonuje się kod zawarty w zdarzeniu OnCreate, ale jeżeli otwieramy taki plik, a nasz program jest już uruchomiony to wykonuje się kod zawarty w funkcji WmCopyData:
| // Plik źródłowy np. Unit1.cpp //--------------------------------------------------------------------------- void __fastcall TMyAss::WmCopyData(TWMCopyData& Message) { AnsiString slCmdLine = (char*)Message.CopyDataStruct->lpData; if(Memo1->Modified) { __int16 idx = Application->MessageBox("Czy chcesz zapisać zmiany w pliku", "Zapisywanie pliku", MB_YESNOCANCEL | MB_ICONQUESTION); switch(idx) { case ID_YES: Memo1->Lines->SaveToFile(OldPathFile); break; case ID_NO: break; case ID_CANCEL: return; } } Memo1->Lines->LoadFromFile(slCmdLine); OldPathFile = slCmdLine; } //------------------------------------------------------------------------------ void __fastcall TMyAss::FormCreate(TObject *Sender) { if(!ParamStr(1).IsEmpty()) Memo1->Lines->LoadFromFile(ParamStr(1)); OldPathFile = ParamStr(1); } //--------------------------------------------------------------------------- |
Na koniec
małe wyjaśnienie odnośnie Memo1 i plików tekstowych. Notatnik w Windows
XP ma możliwość zapisywania plików tekstowych w kodowaniu Unicode, ale
Memo1 nie potrafi prawidłowo interpretować takich plików. Memo
rozpoznaje tylko kodowanie ANSII.
Jeszcze jedna informacja dla użytkowników środowiska Borland Developer
Studio, jeżeli w tym środowisku nie działa wam komponent TRichEdit, to
znaczy, że w systemie brakuje biblioteki Winspool.dll, trzeba ją więc
skądś ściągnąć i skopiować do katalogu [...]\Windows\System32.
Wywoływanie nie modalnego okna
komunikatu.
Chyba wszyscy wiedzą jak wywołać okno komunikatu
i zapewne wszystkim jest wiadome, że takie okno zawsze jest wywoływane jako
modalne, czyli dopóki nie zostanie zamknięte, to nie można się przemieszczać
między innymi oknami w programie. Mamy na przykład program składający się z okna
głównego i okna wtórnego (drugiego), jeżeli obydwa okna będą uruchomione, jako
nie modalne, no to oczywiście można oczywiście się między nimi przemieszczać,
jeżeli jednak w oknie głównym lub wtórnym wywołamy komunikat, czy to za pomocą
funkcji ShowMessage, czy też Application->MessageBox, to wyskoczy
nam okno komunikatu i nie będziemy mogli nic zrobić w żadnym innym oknie
programu dopóki okno komunikatu nie zostanie zamknięte.
Istnieje jednak sposób na ominięcie tego, możemy wywołać okno komunikatu w taki
sposób, że możliwe będzie przemieszczanie się pomiędzy innymi oknami w
programie, nawet jeżeli okno komunikatu będzie otwarte. Sposób ten polega na
wywołaniu funkcji MessageBox i przekazaniu jej jako pierwszego argumentu
wartości NULL, gwoli wyjaśnienia, ten argument to uchwyt do okna pod które
chcemy podpiąć komunikat, przekazując wartość NULL nie podpinamy tego komunikatu
pod żadne okno, w ten sposób okno komunikatu nie blokuje innych okien programu.
| //-------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { MessageBox(NULL, "Treść komunikatu", "Tytuł komunikatu", MB_OK | MB_ICONHAND); } //-------------------------------- |
Lub z zapytaniem:
| //-------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { __int8 idx = MessageBox(NULL, "Czy chcesz zrobić coś tam?", "Tytuł komunikatu", MB_YESNOCANCEL | MB_ICONQUESTION | MB_DEFBUTTON2); switch(idx) { case ID_YES: // jakieś zadanie jeżeli wybrano przycisk TAK; break; case ID_NO: // jakieś zadanie jeżeli wybrano przycisk NIE; break; case ID_CANCEL: // przerwanie operacji jeżeli wybrano ANULUJ; return; } } //-------------------------------- |
Nie będę
wyjaśniał co to znaczy MB_OK, MB_YESNOCANCEL, MB_ICONHAND itp... bo można o tym
przeczytać w poradzie obsługa
komunikatów.
Wywołanie okna komunikatu jako okno nie modalne, ma poważną wadę, jeżeli
przejdziemy do innego okna w programie, to okno komunikatu zajedzie na dalszy
plan, co może zaowocować tym, że zapomnimy je zamknąć, ale i temu można zaradzić
wywołując to okno z parametrm MB_TOPMOST, można też wyrównać tekst w oknie
komunikatu do prawej strony wywołując je z parametrem MB_RIGHT, domyślnie tekst
jest wyrównywany do strony lewej, nie ma argumentu umożliwiającego wyśrodkowanie
tekstu. Ciekawy efekt uzyskamy wywołując okno z parametrem MB_RTLREADING, który
powoduje odwrócenie okna, czyli lewa strona staje się prawą, takie lustrzane
odbicie okna komunikatu, jednak tekst nie ulega odwróceniu.
| //-------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { MessageBox(NULL, "Treść komunikatu", "Tytuł komunikatu", MB_YESNO | MB_ICONHAND | MB_TOPMOST | MB_RTLREADING | MB_RIGHT); } //-------------------------------- |
Blokada klawiatury i myszki.
W celu zablokowania klawiatury i myszki można posłużyć się funkcją BlockInput,
która pobiera argument typu BOOL. Przekazując wartość TRUE
blokujemy jednocześnie klawiaturę i myszkę, przekazując wartość FALSE
wyłączamy blokadę. W celu użycia tej funkcji należy włączyć do projektu
bibliotekę winable.h w sekcji include pliku
nagłówkowego. Jeżeli okaże się, że funkcja nie działa prawidłowo lub wcale, to
trzeba jeszcze włączyć do projektu poprzez menu Project | Add to project
bibliotekę user32.lib, która powinna znajdować się w katalogu lib\psdk,
np: C:\Program Files\Borland\CBuilder6\Lib\Psdk
| #include "winable.h"
//-------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { BlockInput(true); // włączenie blokady Sleep(10000); // wstrzymanie wykonywania kodu na 10 sekund BlockInput(false); // wyłączenie blokady } //-------------------------------- |
W przedstawionym
przykładzie umieściłem w zdarzeniu OnClick przycisku Button1 jednocześnie kod
blokujący i odblokowujący po 10 sekundach, a to dlatego, że po włączeniu blokady
komputer nie reaguje ani na klawiaturę, ani na myszkę.
Jeżeli zablokujemy myszkę i klawiaturę to nie będziemy mieli możliwości wywołania w programie funkcji
odblokowującej, dlatego trzeba postępować z tym ostrożnie, gdyż w przypadku
zablokowania jedyne co pozostaje to restart systemu, dlatego używasz tego kodu
na własną odpowiedzialność.
Zmiana kursorów myszy dla całego systemu.
Zmiany kursorów myszki można dokonać w Aplecie Mysz
Panelu sterowania, ale to nie sztuka. Możliwa jest zmiana
zmiana kursorów z poziomu własnego programu poprzez modyfikację rejestru i
wysłanie do systemu polecenia zaktualizowania kursorów myszy za pomocą funkcji
SystemParametersInfo.
Zadanie jest banalnie proste w realizacji, na przykładzie pokażę jak zmienić
kursor wskazujący (strzałka - Arrow) na własny kursor animowany, zaznaczam od
razu, że przy zmianie kursorów dla całego systemu, trzeba użyć kursora
znajdującego się gdzieś na dysku twardym, a nie w zasobach programu. Zanim
przystąpimy do programowania trzeba przygotować sobie jakiś kursor zwykły lub
animowany, ja posłużę się kursorem rainbow.ani, który u mnie znajduje się na
dysku C: w katalogu Windows\Cursors. Użycie klasy TRegistry wymaga włączenia do
projektu w sekcji include biblioteki Registry.hpp.
| #include <Registry.hpp>
//-------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { String cursor = "C:\\WINDOWS\\Cursors\\rainbow.ani"; TRegistry *Rejestr = new TRegistry(); Rejestr->RootKey = HKEY_CURRENT_USER; Rejestr->OpenKey("Control Panel\\Cursors", true); Rejestr->WriteString("Arrow", cursor); // tutaj następuje zmiana wybranego kursora Rejestr->Free(); SystemParametersInfo(SPI_SETCURSORS, 0, NULL, NULL); // aktualizacja kursorów } //-------------------------------- |
Jak widać na
przykładzie kursor strzałki w rejestrze to wartość ciągu o nazwie Arrow,
chcąc zmienić inny kursor wystarczy tylko zmienić wartość ciągu. Niżej
przedstawiam listę możliwych wartości:
Wybór normalny
- Arrow
Wybór pomocy
- Help
Praca w tle
- AppStarting
![]()
Zajęty
- Wait
![]()
Wybór precyzyjny
- Crosshair
![]()
Wybór tekstowy
- IBeam
(iBeam)![]()
Pismo ręczne
- NWPen
![]()
Niedostępny
- No
![]()
Zmiana wymiaru
pionowego - SizeNS
![]()
Zmiana wymiaru
poziomego - SizeWE
![]()
Zmiana wym. po
przekątnej 1 - SizeNWSE
![]()
Zmiana wym. po
przekątnej 2 - SizeNESW
![]()
Przenieś
- SizeAll
![]()
Wybór alternatywny
- UppArrow
![]()
Wybór łącza
- Hand
![]()
Uruchamianie okna Mój komputer.
Z uruchomieniem okna Mój komputer jest ten problem, że nie istnieje do
niego żadna ścieżka dostępu. W przypadku np. Panelu sterowania
wystarczy wywołać program Control.exe, ale Mój komputer to nie
jest program, lecz miejsce w rejestrze systemowym.
Uruchomienie okna Mo komputer opiera się na pobraniu ścieżki dostępu do katalogu
specjalnego CSIDL_DRIVES i przepisania jej do listy PIDL (item identifier list),
a potem to już wystarczy wywołać funkcję SHELLEXECUTEINFO:
| //-------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { LPITEMIDLIST ppidl; LPMALLOC pShellMalloc; if(SUCCEEDED(SHGetMalloc(&pShellMalloc))) { SHGetSpecialFolderLocation(Handle, CSIDL_DRIVES, &ppidl); SHELLEXECUTEINFO ShExecInfo; ShExecInfo.cbSize = sizeof(ShExecInfo); ShExecInfo.fMask = SEE_MASK_IDLIST; ShExecInfo.hwnd = Handle; ShExecInfo.lpVerb = NULL; ShExecInfo.lpFile = NULL; ShExecInfo.lpParameters = NULL; ShExecInfo.lpDirectory = NULL; ShExecInfo.nShow = SW_SHOWNORMAL; ShExecInfo.hInstApp = NULL; ShExecInfo.lpIDList = ppidl; ShellExecuteEx(&ShExecInfo); } pShellMalloc->Free(ppidl); pShellMalloc->Release(); } //-------------------------------- |
Sprawdzanie i opróżnianie kosza.
W celu sprawdzenia aktualnego rozmiaru kosza, czyli sprawdzenia jakie rozmiary
mają w sumie wszystkie pliki w koszu oraz sprawdzenia ile jest tych plików można
posłużyć się funkcją SHQueryRecycleBin, która
poprzez strukturę SHQUERYRBINFO zwraca właśnie aktualny rozmiar kosza
(i64Size) i liczbę plików w koszu. Funkcja SHQueryRecycleBin
pobiera dwa argumenty pierwszy to ścieżka dostępu do kosza, który chcemy
sprawdzić np. c:\recycler, co ciekawe, jeżeli
zamiast pełnej ścieżki dostępu podamy tylko literę dysku (np. c:) na
którym ten kosz się znajduje, to funkcja sama ten kosz odnajdzie. Jeżeli mamy
kilka koszy i chcemy sprawdzić jaki jest rozmiar wszystkich koszy i ile jest
plików we wszystkich koszach to zamiast podawać ścieżkę dostępu do wybranego
kosza, podajemy wartość NULL. Drugi argument to adres do struktury
SHQUERYRBINFO, która zwraca nam te dane:
| //-------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { SHQUERYRBINFO shqi; shqi.cbSize = sizeof(SHQUERYRBINFO); SHQueryRecycleBin("c:\\recycler", &shqi); // dla wszystkich koszy: SHQueryRecycleBin(NULL, &shqi); Label1->Caption = "Rozmiar: " + (String)(shqi.i64Size/1024) + " KB; Liczba plików: " + (String)shqi.i64NumItems; } //-------------------------------- |
W celu opróżnienia kosza można posłużyć się funkcją SHEmptyRecycleBin,
która pobiera trzy argumenty. Pierwszy to uchwyt do okna, do którego
chcemy przypisać okno dialogowe opróżniania kosza, czyli można tutaj
przekazać uchwyt do okna naszego programu (this->Handle), ale równie
dobrze można przekazać wartość NULL, czyli bez uchwytu. Drugi argument
to ścieżka dostępu do wybranego kosza, zasada jest tutaj dokładnie taka
sama jak w przypadku powyższej funkcji SHQueryRecycleBin, podanie
wartości NULL sprawi, że opróżnione zostaną wszystkie kosze. Trzeci
argument to flaga określająca jak wizualowany będzie proces opróżniania
i dostępne są trzy parametry:
SHERB_NOCONFIRMATION - Okno
dialogowe opróżniania kosza, nie będzie wyświetlane
SHERB_NOPROGRESSUI -
Pasek postępu opróżniania kosza (ProgressBar) w oknie dialogowym
opróżniania kosza nie będzie wyświetlany
SHERB_NOSOUND
- Nie będzie odtwarzany dźwięk opróżnienia kosza (ma
znaczenie tylko jeżeli w aplecie Dźwięki został przypisany dźwięk dla
kosza).
| //-------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { SHEmptyRecycleBin(NULL, "c:\\recycler", SHERB_NOCONFIRMATION); // dla wszystkich koszy: SHEmptyRecycleBin(NULL, NULL, SHERB_NOCONFIRMATION); } //-------------------------------- |
Jeżeli chcemy wyświetlić listę plików w koszu, to postępujemy z katalogiem RECYCLER dokładnie tak samo jak z każdym innym, czyli trzeba się tutaj posłużyć funkcją wyszukiwania folderów, podfolderów i plików, gdyż nie ma na to odrębnej funkcji.