Delphi, cz. 15
Powoli zbliżamy się do zakończenia kursu "Delphi 5 dla początkujących" :( Nie martwcie się jednak. Na stronie www.programowanie.of.pl możecie znaleźć sporo informacji na temat Delphi. Zajrzyj!
Wykorzystanie rejestru i plików INI
Być może zastanawiasz się do czego służy rejestr i pliki INI. Rejestr Windows służy do zapisywania konfiguracji programów. Wyobraź sobie, że chciałbyś zapisać ustawienia Twojego programu. Przykładowo piszesz tekst w komponencie "Edit", zamykasz program, a następnie uruchamiasz go ponownie i na etykiecie widnieje taki sam tekst jak przy zamykaniu programu. Można to łatwo zrobić z wykorzystaniem rejestru lub plików INI.
Najpierw rejestr. Przede wszystkim, aby korzystać z rejestru musisz do listy modułów uses dodać słowo "Registry".
1. Umieść na formularzu komponent "MaskEdit" ( paleta: Additional ). Komponent ten służy do ustawiania tzw. maski. Znaczy to, że Delphi 'dopilnuje', żebyś wpisał np. numer kodu. Za chwilę sam się o tym przekonasz. W Inspektorze Obiektów zmień nazwę komponentu na "Password". Teraz odnajdź pole "EditMask" - pojawi się okienko do wyboru maski. Po lewej stronie kliknij "ZipCode". Oznacza to, że hasło składać się będzie z pięciu cyfr. Kliknij OK - maska została ustawiona. Teraz odnajdź pole "PasswordChar". W tym polu należy wpisać znak, który będzie "przysłaniał" pisany przez Ciebie kod. W tej właściwości wpisz znak *. Teraz zamiast pisanych cyfr pojawiać się będzie właśnie ten znak.
2. Wygeneruj procedurę "OnDestroy" formy - uruchamiana ona będzie w chwili gdy użytkownik zamknie program. Wpisz w nią taki tekst ( później objaśnię ):
procedure TForm1.FormDestroy(Sender: TObject);
var
Reg : TRegistry; // zmienna wskazuje na rejestr
begin
Reg := TRegistry.Create;
try
Reg.OpenKey('Software\Delphi Kurs', True); // stworzenie klucza
Reg.WriteString('Haslo', Password.Text); // utworzenie wartosci
finally
Reg.Free; // zwolnij pamięć
end;
end;
Od tej pory przy zamykaniu programu do rejestru zostanie wpisane hasło z komponentu "Password". Już wyjaśniam o co właściwie chodzi. Na samym początku należało utworzyć zmienną typu "Registry". Później ją stworzyłem. Następnie polecenie "Reg.OpenKey" otwiera klucz z rejestru. Wartości domyślnie są zapisywane do klucza głównego HKEY_CURRENT_USER. A więc w nawiasie tej funkcji jako pierwszy parametr podawana jest nazwa klucza, który ma zostać otwarty. Drugi parametr "mówi" czy klucz ma być utworzony jeżeli nie istnieje ( True ), czy też nie ( False ).
Kolejna funkcja do "Reg.WriteString". Zapisuje ona do rejestru wartość typu "String". Pierwszym parametrem tej funkcji jest nazwa klucza do zapisania, a drugim sama wartość typu "String". W tej funkcji do rejestru zapisana została treść kontrolki "Edit". Na samym końcu następuje zwolnienie pamięci zajętej przez zmienną "Reg". Zauważ, że użyłem tutaj wyjątków. Powoduje to bezwzględne zwolnienie pamięci bez względu na to czy operacja się powiedzie, czy też nie. OK, uruchom teraz program i zamknij go - do rejestru zapisana została wartość typu "String". Możesz to sprawdzić. Uruchom rejestr ( Start -> Uruchom -> Regedit ). Wartość nasza została do rejestru zapisana pod kluczem:
HKEY_CURRENT_USER\Software\Delphi Kurs. Możesz to sprawdzić.
Dobra. Jeżeli hasło znajduje się już w rejestrze to należy wygenerować procedurę "OnCreate" formy, która sprawdzać będzie hasło. Poniższa procedura ma sprawdzać czy klucz w rejestrze istnieje. Jeżeli nie to nic nie robi - jeżeli istnieje to wyświetl okno z prośbą o wpisanie hasła:
procedure TForm1.FormCreate(Sender: TObject);
var
Reg : TRegistry; // zmienna oznaczajaca rejestr
Istnieje : Boolean;
begin
Reg := TRegistry.Create; // stworz rejestr
try
{ Jezeli klucz istnieje to zmienna "Istnieje" przyjmuje wartosc True }
Istnieje := Reg.OpenKey('Software\Delphi Kurs', False);
if Not Istnieje then // Jezeli zmienna = False to znaczy, ze klucz nie istnieje...
Exit else //... wtedy nie rob nic. Jezeli klucz istnieje to wywolaj okno:
if InputBox('Podaj hasło!', 'Wpisz hasło:', '') <> Reg.ReadString('Haslo') then
begin // Jezeli haslo jest nieprawidlwe
MessageDlg('Błędne hasło. Program zostanie zakończony!', mtError, [mbOK], 0);
Application.Terminate; // zakoncz program
end;
finally
Reg.Free;
end;
end;
Ta procedura jest trochę dłuższa i trudniejsza. Sprawdza ona bowiem czy klucz istnieje. Zmienna "Istnieje" przechowuje informacje o tym, czy klucz istnieje, czy też nie. Następuje otwarcie klucza. Jeżeli klucz istnieje to zmienna "Istnieje" przybiera wartość True. Jeżeli jest odwrotnie to False. Następnie następuje sprawdzenie jaka jest wartość zmiennej "Istnieje". Jeżeli False ( if not Istnieje then... znaczy to samo co if Istnieje = False then.... ) to nic się nie dzieje. Polecenie "Exit" działa tak samo jak "Break" - przerywa działanie programu. Jeżeli klucz istnieje to wyświetlane jest okno z informacją o podanie hasła. Umożliwia to funkcja "InputBox. Pierwszym jej parametrem jest tekst, który pojawi się na pasku okna, drugi to tekst etykiety, a ostatni to domyślne hasło, które pojawi się w oknie typu "Edit". Następuje tutaj porównanie wpisanego hasła z wartością wczytaną z rejestru ( Reg.ReadString ). Jeżeli te dwie wartości są różne to następuje wyświetlenie odpowiedniej informacji ( MessageDlg - mowa o tym była we wcześniejszych odcinkach ) i zakończenie programu. Normalnie do zakończenia działania programu służy polecenie "Close", ale działa ono tylko wtedy gdy widoczna jest forma. Jeżeli forma jest niewidoczna to należy stosować Application.Terminate, która kończy działanie programu.
Oczywiście możesz także zapisywać wartości typu "Integer" czy "Boolean", a nawet datę czy wartości zmienno - przecinkowe. Oto przykłady:
// typ Integer
Reg.WriteInteger('WartoscInteger', 12);
// zapisana liczba 12
// odczyt typu Integer
Edit1.Text := IntToStr(
Reg.ReadString('WartoscInteger'));
// typ Boolean
Reg.WriteBool('WartoscBoolean', True);
// zapisana wartość True
// odczytanie wartości Boolean:
Checkbox1.Checked := Reg.ReadBool(
'WartoscBoolean');
// typ zmienno - przecinkowy
Reg.WriteCurrency('WartoscCurrency', 3.12);
// zapisanie 3.12
// odczyt wartości Currency:
Edit2.Text := CurrToStr(
Reg.ReadCurrency('WartoscCurrency'));
// zapisz aktualną datę i czas
Reg.WriteDateTime('DataIczas', Now);
// odczytaj datę i czas
Edit3.Text := DateTimeToStr(
Reg.ReadDateTime('DataIczas')); // odczytaj datę i czas
Oto dodatkowe funkcje związane z rejestrem:
CloseKey; Zamyka dotychczasowo otwarty klucz.
DeleteKey; Usuwa klucz - np: Reg.DeleteKey('Software\Programowanie');
DeleteValue; Usuwa jedynie wartość klucza: Reg.DeleteValue('WartoscBoolean');
KeyExists; Funkcja zwraca wartość True jeżeli podany w nawiasie klucz istnieje.
MoveKey; Przenosi klucz z jednego miejsca na drugie: Reg.MoveKey('Software\Programowanie', 'Software\SFP', False); Ostarni parametr mówi czy stary klucz ma być usunięty.
RootKey; Nazwa klucza głównego: Reg.RootKey := HKEY_CLASSES_ROOT;
ValueExists; Sprawdza, czy wartość klucza podana w nawiasie istnieje.
ReadString,
WriteString Zapisuje i odczytuje wartość String;
WriteInteger,
ReadInteger Zapisuje i odczytuje wartość Integer;
WriteBool,
ReadBool; Zapisuje i odczytuje wartość typu Boolean
WriteDateTime,
ReadDateTime Odczytuje i zapisuje datę oraz czas.
WriteCurrency,
ReadCurrency Zapisuje i odczytuje wartość zmienno - przecinkową.
GetKeyNames Zwraca w postaci "TStrings" wszystkie pod-klucze danego klucza.
GetValueNames Zwraca wartości danego klucza: Reg.GetValueNames('Software\Programowanie');
Domyślnie w Delphi wszystkie klucze zapisywane są do klucza głównego HKEY_CURRENT_USER. Jeżeli chcesz to zmienić to rób tak:
Reg.RootKey := HKEY_CLASSES_ROOT;
Reg.OpenKey('Moj Klucz', True);
Pliki INI
Operowanie na plikach INI jest bardzo podobne. Istnieją różnice, oczywiście, ale są one małe. Być może wiesz lub nie, że plik INI składa się z sekcji. Są różne sekcje, a w nich nazwy kluczy i ich wartości. Żeby korzystać z plików INI musisz do listy modułów uses dodać słowo "INIFiles". W procedurze możesz napisać:
var
INI : TINIFile;
begin
INI := TINIFile.Create('C:\plikini.ini'); // stworz plik INI na dysku C
try
INI.WriteString('Sekcja01', 'Klucz', Edit1.Text);
finally
INI.Free;
end;
end;
Taka procedura spowoduje zapisanie odpowiednich wartości do pliku INI, który zostanie stworzony na dysku C. Na początku stworzona została zmienna typu "TINIFile", której parametrem jest ścieżka gdzie plik ma być zapisany. Później następuje do pliku INI wartości typu String. Pierwszym parametrem tej funkcji jest nazwa sekcji. Drugi parametr to nazwa klucza, a ostatnim to wartość która ma być zapisana do pliku INI. Wartość ta to tekst , który przechowywany jest w kontrolce Edit.
Jeżeli podczas tworzenia pliku INI nie podczas konkretnej ścieżki, a tylko nazwę pliku INI to ten plik zostanie stworzony w katalogu z Windowsem.
Teraz odczytanie wartości:
var
INI : TINIFile;
begin
INI := TINIFile.Create('C:\Plikini.ini');
try
Edit1.Text := INI.ReadString('Sekcja01', 'Klucz', 'Domyślna wartość');
finally
INI.Free;
end;
end;
Odczytywanie jest bardzo podobne. Zostanie stworzenie zmiennej INI, a następnie odczytanie wartości. Do tekstu kontrolki Edit zostanie odczytany klucz z pliku INI. Pierwszym jej parametrem jest nazwa sekcji z pliku INI, kolejny to nazwa klucza, a ostatnia wartość to domyślna wartość. Ta domyślna wartość zostanie w kontrolce wpisana jeżeli plik INI nie istnieje lub jeżeli nie istnieje taki klucz.
Podobnie ma się sprawa z wartościami typu Integer lub Boolean:
WriteString, ReadString Odczytuje lub zapisuje wartość typu String.
WriteInteger, ReadInteger Odczytuje lub zapisuje wartość typu Integer.
WriteBool, ReadBool Odczytuje lub zapisuje wartość Boolowską. INI.WriteBool('Ogolne','Konfig',True) = True
WriteFloat, ReadFloat Zapisuje lub odczytuje wartość zmienną przecinkową typu Double
WriteDateTime, ReadDateTime Odczytuje lub zapisuje aktualną datę.
DeleteKey Usuwa daną wartość. INI.DeleteKey('Ogolne','Imie');
ReadSections Zwraca wszystkie sekcje danego pliku w postaci wartości TStrings.
EraseSection Usuwa daną sekcje i pod klucze z nią związane.
ValueExists Sprawdza czy dana wartość istnieje. INI.ValueExists('Ogolne','Imie');
SectionExists Zwraca wartość True jeżeli sekcja o podanej nazwie istnieje.
To tyle. Jeżeli chcesz uzyskać szczegóły to możesz doczytać w systemie pomocy Delphi.
Postanowiłem jeszcze omówić typ "TStrings", a dokładnie "ReadSections". Odczytuje ona z pliku INI wszystkie sekcie i zwraca wynik w postaci zmiennej "TStrings".
Oto przykład:
var
INI : TINIFile;
begin
INI := TINIFile.Create('C:\Plikini.ini');
try
INI.ReadSections(ListBox1.Items);
finally
INI.Free;
end;
end;
Umieść na formie komponent "ListBox" i sprawdź działanie procedury. "Items" przy komponencie "ListBox" oznacza wartości znajdujące się w tym komponencie.
Budujemy instalatora
Przedstawię tutaj proces tworzenia instalatora. Będzie to instalator na własne potrzeby. Będzie on w jednym pliku EXE zawierał instalowane pliki. Wygląd samego instalatora pozostawiam Wam.
Oczywiście nasz instalator ma być w jednym pliku tak żeby nasz instalowany program "odklejał" się od EXEka i zapisywał w wybranym przez użytkownika katalogu.
Pewnie największym problemem było włączenie samego pliku do EXEka, prawda?
Dołączanie plików:
Przygotuj sobie najpierw jakiś katalog - załóżmy "Setup". Tam skopiuj pliki, które chcesz włączyć do EXEka. Ja w swoim przykładzie włączam tylko jeden plik wykonywalny EXE. Naszym celem będzie stworzenie zasobu zawierającego właśnie plik EXE, który chcesz zainstalować. W przykładzie, który podaje plik dołączony do zasobów nazywać się będzie "Pad.exe".
Najpierw stwórz plik tekstowy z zmień jego rozszerzenie na *.rc ( np. files.rc ). W Notatniku dopisz taką linię:
PAD RCDATA "Pad.exe"
README RCDATA "Readme.txt"
Pierwszy człon to nazwa programu, drugi to typ pliku - w tym wypadku jest to program, a ostatni człon podany w cudzysłowach to nazwa pliku wykonywalnego - w moim przypadku jest to "Pad.exe" oraz plik Readme "Readme.txt". Zapisz cały plik. Jeżeli posiadasz Delphi to z pewnością program "brcc32.exe". Znajduje się on w katalogu ...Delphi\Bin. Skopiuj go do katalogu z Twoimi plikami ( tam gdzie masz plik *.rc ).
Jest to program DOS-owy więc musisz uruchomić go z okienka MS -DOS. Ten program przekształci plik *.rc na *.res.
Odpal więc okienko MS - DOS-a i przejdź do katalogu z plikiem *.rc oraz z programem "brcc32.exe". W DOS-ie do katalogów przechodzi się poleceniem:
cd NazwaKatalogu
Jeżeli chcesz przejść o katalog wyżej to stosujesz polecenie:
cd..
A więc jeżeli już jesteś w tym katalogu to wpisz taką linię i wciśnij ENTER:
brcc32.exe files.rc
"Files.rc" to nazwa Twojego zasobu. Jeżeli wszystko pójdzie dobrze to obok pliku *.rc powinieneś ujrzeć plik z rozszerzeniem *.res.( w moim przypadku jest to plik o nazwie "files.RES". ). Gratulacje! Właśnie stworzyłeś zasób, który zawiera Twój plik EXE. Jest on zapewne dość duży ( w moim przypadku zajmuje 250 kB ).
Wyciągnięcie programu z EXEka.
Gdzieś w kodzie Twojego instalatora umieść taką linię:
{$R FILES.RES}
"Files.res" to Twoja nazwa zasobu. Od tej pory zasoby te są włączone do instalatora.
Pozostało jeszcze napisanie samej procedury, która wyciągnie z zasobów program i zapisze go gdzieś na dysku:
Wygląda ona tak:
procedure TMainFrm.InstallClick(Sender: TObject);
{
Rekord ten zawiera dwie pozycje. Pierwsza okresla tutul
instalowanego pliku, ktory okresliles podczas kompilacji
zasobow. Drugi to dokladna nazwa instalowanego pliku - np: Pad.exe
}
type
_DATA = record
Title: PChar;
FName: PChar;
end;
{
Stala okreslajaca tablice bazujaca na rekordzie. Tablica ta
zawiera dokladna nazwe pliku jaki bedzie stworzony podczas
instalacji oraz nazwa, ktora zostala podana podczas kompilacji
zasobow.
}
const
Files : array [0..1] of _DATA =
((Title: 'PAD'; FName: 'Pad.exe'),
(Title: 'README'; FName: 'Readme.txt'));
var
Res : TResourceStream;
I : Integer;
{
Procedura, korej tresc widzisz pokazuje wskaznik postepu
na pasku "Gauge". Jest to tylko tak dla "bajeru", aby pokazac
postep w kopiowaniu pliki :)
}
procedure Postep;
var
I : Integer;
begin
For I := 0 to 100 do // petla
begin
Application.ProcessMessages;
Sleep(3); // czekaj 3 milisekundy
Gauge.Progress := i;
end;
end;
begin
{
Jezeli ostatnim znamiem znajdujacym sie w "Dir" jest znak "\" to nie
rob nic. Jezeli jest inaczej dopisz ten znak na koncu tekstu.
}
if Dir.Text[Length(Dir.Text)] = '\' then
Dir.Text := Dir.Text else
Dir.Text := Dir.Text + '\';
For I := Low(Files) to High(Files) do
begin
Application.ProcessMessages; // daj odetchnac systemowi
Postep; // wywyolaj procedure
Res := TResourceStream.Create(hInstance, Files[i].Title, RT_RCDATA);
Res.SaveToFile(Dir.Text + Files[i].FName);// zapisz plik na dysku
Res.Free; // zwolnij zmienna
end;
MessageBox(Handle, 'Instalacja zakończona sukcesem :)', 'Informacja',
MB_OK + MB_ICONINFORMATION);
end;
Być może ta procedura wygląda trochę strasznie i trudno. Zastosowałem tutaj rekord oraz tablice, aby pokazać zastosowanie ich w praktyce. Powyższy kod zadziała wtedy gdy na formie umieścisz komponent "Gauge" odpowiedzialny za pokazywanie wskaźnika postępu. Znajdziesz go na palecie "Samples".
W przykładzie instalowane są dwa pliki. Gdy masz więcej niż dwa pliki to wystarczy, że do stałych "const" dopiszesz kolejne wartości odpowiadające za pliki. Jeżeli czegoś nie rozumiesz to pisz: boduch@poland.com
Zauważ, że w tym przykładzie procedura ( nowa ) została zadeklarowana jako zmienna w sekcji "var". Teraz może ona być wywoływana z każdego miejsca w danej procedurze. Procedura ta odpowiada za pokazywanie wskaźnika postępu ( tak dla bajeru ) żeby fajnie wyglądało ( tak jakby plik się naprawdę instalował ). Pierwszą rzeczą którą procedura robi po uruchomieniu jest sprawdzenie ostatniego znaku w kontrolce "Dir". Aha, kontrolka "Dir" to komponent "Edit". Sprawdzany jest czy ostatnim znakiem jest back-slash. Jeżeli nie to jest on dodawany na końcu. Następnie wywoływana jest pętla która instaluje wszystkie pliki. Jednocześnie z instalowaniem pliku wywoływana jest procedura "Postęp", która pokazuje postęp na komponencie "Gauge".
Przy końcu instalacji pokazywane jest okienko z informacją o zakończonej instalacji.
To tyle w tym rozdziale. W kolejnym biblioteki DLL! |