Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Artur 'Mrowqa' Jamro
Inne artykuły

[Windows, rejestr] Własne rozszerzenie pliku

[artykuł] Artykuł opisujący w jaki sposób można zarejestrować własne rozszerzenie w systemie Windows oraz w jaki sposób je obsługiwać z poziomu C++.
Wiele aplikacji, które posiadają własne rozszerzenia plików mają ciekawą funkcjonalność. Otóż, gdy kliknie się dwukrotnie na plik o danym rozszerzeniu to otwierany jest on w określonej aplikacji. Przykładowo plik *.txt jest odczytywany przez notatnik. Jak osiągnąd coś takiego? Sprawa jest stosunkowo prosta :)

Jak to się dzieje?

Przecież nie włączysz pliku z danymi :P Musi być wywołana aplikacja, która jest poinformowana o otwieranym pliku. Jak system postępuje w przypadku próby otwarcia pliku z danymi? Sprawdza rejestr. Tam są zapisane informacje o rozszerzeniach oraz o tym, jak postępować w przypadkach próby otwarcia.

Pora na wizytę w rejestrze :)

Rejestr jest to największy śmietnik w Windowsie – głoszą słowa przeczytane kiedyś przeze mnie w Internecie. Mimo to, jest to także bardzo ciekawe miejsce i źródło wielu cennych informacji. My dodamy własne. Zajrzyjmy więc do rejestru :)
W Menu Start wybieramy „Uruchom”, wpisujemy „regedit” i zatwierdzamy. W Viście (przepraszam za formę, nie wiem jak to odmienić :P) i nowszych windowsach wystarczy na pasku do wyszukiwania wpisać rzekomą frazę i stamtąd to włączyć. Uruchomi się nam edytor rejestru. Najlepiej uruchomić go jako Administrator. Jak widać jest kilka głównych kluczy:
Nas interesuje klucz HKEY_CLASSES_ROOT. Właśnie tam znajdują się informacje na temat rozszerzeń plików. Przyjrzyjmy się przykładowo rozszerzeniu *.txt (chcesz to możesz wybrać inne).
Odszukajmy zatem klucz .txt. Klikamy na niego i patrzymy na wartość domyślną:
Jak widać jest to ciąg znaków „txtfile”. On jest ważny, reszta nas nie interesuje :). Szukamy zatem nowego klucza o nazwie txtfile w kluczu głównym HKEY_CLASSES_ROOT (czyli jakby „obok” klucza *.txt). Możesz użyć opcji szukaj niż ręcznie to robić :P. Znaleźliśmy i mamy takie o to drzewko:
Nas będzie interesować drzewko kluczy zaznaczone na obrazku. Znowu sprawa z wartościami domyślnymi :). Objaśnię co one oznaczają w konkretnych podkluczach. Użyłem *.XXX oraz <ext>, dlatego bo te wartości zależą od rozszerzenia, którym się aktualnie zajmujemy :)
KluczZnaczenie
<ext>
Krótki opis pliku wykorzystywany przez system. Przykładowo „Dokument tekstowy”.
<ext>\DefaultIcon
Ścieżka do ikony. Pliki o rozszerzeniu *.XXX będą przedstawiane za pomocą tej ikony. Standardowe rozszerzenie pliku z ikoną to *.ico. Niemniej jednak może ona się znajdować w plikach takich jak *.exe czy *.dll. Wtedy należy podać ścieżkę do tego pliku, potem przecinek i po nim numer ikony (która to ikona w zasobach pliku). Przykładowo: „C:\Program Files\Nasz program\nasz program.exe,5”. Uwaga! Nie może być spacji między ścieżką do pliku, przecinkiem czy liczbą.
<ext>\shell
Nazwa jednego z podkluczy; jest to domyślnie wybierana gdy podwójnie klikniemy LPM na plik o rozszerzeniu *.XXX.
<ext>\shell\<tryb>
Tekst wyświetlany na liście rozwijanej, gdy naciśnie się PPM na plik o rozszerzeniu *.XXX. Znak & (ampresand) stawia się przed jednym ze znaków. Powoduje on podkreślenie tego znaku i ustawienie go jako domyślnego (tzn. jak go klikniemy na klawiaturze to automatycznie jest wybierana ta opcja). Jeśli wartość jest pusta, zostanie użyta wartość domyślna (np. dla klucza „open” jest to „Otwórz”).
<ext>\shell\<tryb>\command
Tu tkwi cała istota problemu :). Wartość domyślna tego klucza jest to szablon, jaki jest wysyłany pod linię poleceń. W naszym przypadku będzie to:
”C:\(…)\nasz program.exe” ”%1”
Czyli „włączenie” naszego pliku *.XXX spowoduje tak naprawdę wywołanie naszego programu z parametrem %1. Ale co to te %1? Oznacza on ścieżkę pliku *.XXX, który „włączamy”! Wystarczy więc dopisać odpowiedni kawałek kodu, który ten parametr odbierze!
Użyłem <tryb> zamiast open. Sprawa tkwi w tym, że tych trybów możemy sobie robić ile chcemy! Jednak w zupełności wystarczy nam ten jeden – open. Przykładowy obrazek z wieloma trybami:
To w czerwonej jasnej ramce to wszystkie tryby jakie ma plik *.txt. Możemy dopisać własne (zob. czerwona ciemna ramka)! Mogą się one odwoływać do, np. naszego programu! :). W zielonym kółku widać ikonkę pliku tekstowego… W rejestrze możemy ją zmienić :)! Jeszcze jedna warta uwagi rzecz to zawartość pomarańczowej elipsy. Są tam propozycje programów, przez które można otworzyć dany plik. System na podstawie rejestru sam sobie tę listę robi.

Rejestrujemy własne rozszerzenie

Na podstawie powyższych informacji jesteśmy w stanie zarejestrować własne rozszerzenie w systemie. My zrobimy sobie to ręcznie. Będzie to krótko opisane, ponieważ większość informacji jest zawarta wyżej :). Można to zrobić także korzystając z WinAPI bądź w pliku instalacyjnym – co jest raczej zalecane. Przecież nie zrobimy pliku „CzytajTo!.txt”, w którym krok po kroku każemy użytkownikowi rejestrować naszego rozszerzenia, nieprawdaż :)? Więc do dzieła :P!

Jak zapewne zdążyłeś zauważyć, standardowe rozszerzenia składają się z trzech znaków. To nie jest wymóg. My sobie zrobimy piecioznakowe :P. Załóżmy, że będzie to *.myext (od my extension). Uwaga! Aby móc edytować rejestr trzeba Edytor Rejestru włączyć jako administrator. Otwieramy zatem główny klucz HKEY_CLASSES_ROOT i dodajemy tam nasz podklucz – nazwany .myext. Jako jego wartość główną musimy wpisać jakiś unikalny (tzn. niepowtarzalny) ciąg znaków. Jeśliby istniał już taki klucz, to tak jakbyśmy postanowili wykorzystać obsługę innego rozszerzenia, a tego nie chcemy :). My wpiszemy sobie ”My_extension_unique_string”. W rzeczywistości wystarczy, że w tym stringu zawrze się jakieś krótkie informacje o naszej aplikacji… małe jest prawdopodobieństwo powtórzenia :). Zobaczmy co mamy:
Jak widać nad naszym rozszerzeniem znajduje się inne – jeszcze dłuższe :P. Nie musimy się ograniczać do trzech znaków. Dobra, teraz robimy kolejny klucz o takiej samej nazwie, co wartość domyślna poprzedniego klucza, czyli w tym przypadku to będzie ”My_extension_unique_string”. Klucz ma się znajdować na równi z .myext, czyli w kluczu głównym HKEY_CLASSES_ROOT. Zakładamy teraz takie drzewko kluczy:
Nie będę powtarzał jeszcze raz co znaczą konkretne wartości danych kluczy – to jest bez sensu. Zerknij w górę na tabelkę – tam masz objaśnienie. Zaznaczyłem jeszcze na obrazku wartość domyślną podklucza command. Ja tu przesadziłem z cudzysłowami. Stawia się je wtedy, gdy w ścieżce są spacje. Chodzi o to, że są one ogranicznikami parametru. Jeśli w parametrze nie ma spacji – nie trzeba ujmować go w cudzysłów (co ja zrobiłem :) – to w niczym nie przeszkadza). Jest tam zapisane:

”C:\my_app\my_app.exe” ”--shell” ”%1”

Wyszczególniłem sobie ten środkowy parametr. Co on oznacza? Dla systemu – nic. Zrobiliśmy sobie to dla siebie :). Gdy ktoś będzie „włączał” plik *.myext poprzez system (tzn. nie wczytując go normalnie w programie) będziemy o tym powiadomieni przez dodatkowy parametr ––shell :). Wcale nie musisz tego dodawać. Chciałem tylko pokazać, że tak wolno :P.

Czas odpalić nasze IDE ;)

Wreszcie dobrnęliśmy do tego momentu! :). Nic innego, tylko się cieszyć, co :P? Więc klawiatura do rąk (a raczej ręce na klawiaturę :P) i klepać kod :D.

Jak zapewne wiesz, funkcja main ma taką formę:
C/C++
int main();
Posiada ona także inną formę:
C/C++
int main( int argc, char * argv[] );
Teraz sprawa co oznaczają te argumenty. Spostrzegawcze osoby zapewne powiedzą, że ma to coś wspólnego z parametrami wywołania programu – i mają rację :). argc to skrót od Argument Counter (ang. Licznik argumentów). Jak nazwa mówi – jest to zmienna przechowująca liczbę przysłanych argumentów do funkcji. argv jest to skrót od Argument Vector (ang. Wektor argumentów). Jest to tablica wskaźników (ale nie odwrotnie!) wskazujących na C-stringi będące parametrami (czyli argumentami) wywołania aplikacji. Uwaga! Parametr o indeksie 0 to zawsze relatywna ścieżka do wywołanej aplikacji. Relatywna, czyli jak np. wywołamy ją spod wiersza poleceń jako ”my_app” (może być nawet bez .exe!) to parametr przesłany do aplikacji to będzie właśnie to co wpisaliśmy w wierszu poleceń, czyli w naszym przypadku ”my_app” :).

Jak widzisz, kod będzie prosty, więc napisze go za Ciebie (:P):
C/C++
int main( int argc, char * argv[] )
{
    if( argc == 3 ) // *
    {
        if( strcmp( argv[ 1 ], "--shell" ) == 0 ) // jeśli wywołanie "systemowe"
        {
            // to wczytujemy plik, jego ścieżka to argv[ 2 ] :)
        }
    }
    else //...
   
}
Dlaczego argc ma być równy 3? Jeśli system wywoła naszą aplikację, wywoła ją z trzema argumentami: relatywna ścieżka programu, ”--shell” oraz ścieżka otwieranego pliku – my sami to określiliśmy w rejestrze. Funckja strcmp porównuje określone C-stringi. Jeśli są one identyczne zwraca zero. ”No dobra, ale czemu po prostu nie użyć operatora ==?”. Odpowiedź jest prosta. Gdybyś napisał:
C/C++
if( argv[ 1 ] == "--shell" )
To wtedy to wyrażenie zawsze by było fałszywe. Dlaczego? Otóż jest to porównanie adresów C-stringów. A jak wiadomo, dwa osobne ciągi znaków nie mogą się znajdować w jednym miejscu. (Pomieściłbyś dwa kilogramy jabłek w jednym? :P).

No, to by było na tyle… Nie jest to takie trudne, co :P? W rejestrze przy tym zapewne da się tam zapisać wiele innych informacji… Na Internecie nie mogłem znaleźć o tym informacji i wszystko co o rejestrze pisze to szczerze mówiąc „sam odkryłem”, dlatego jest trochę ubogo w dodatkowe szczegóły :P. Mimo, że rejestr jest największym śmietniskiem w Windowsie to czasem można z niego wygrzebać ciekawe informacje… :P