Delphi - FAQ (1)

Przepraszam wszystkich kursowiczów Delphiego, ale w tym miesiącu nie zdołałem napisać czwartej części kursu.
W tym miesišcu FAQ

1 Jak zrobić pasek gradientowy?
2 Jak przejść z jednego komponentu TEdit do drugiego przy pomocy Entera (domyślnie przechodzi się przy pomocy Tab)?
3 Gdzie można znaleźć archiwum grupy pl.comp.lang.delphi?
4 Gdzie można znaleźć informacje i nagłówki do DirectX?
5 Co to jest DSP?
6 Co się stało z procedurą Delay z TurboPascala? Jak mam zrobić w Delphi pauzę?
7 Jak drukować tekstowo w Delphi?
8 Jak wywołać domyślny program pocztowy z wpisanym już adresem odbiorcy?
9 Dlaczego programy napisane w Delphi nie "fruną" na pasek zadań a po prostu się minimalizują, jak Windows 3.11?
10 Co to znaczy: okienko ładowane dynamicznie?
11 Jak sprawdzić w jakim trybie graficznym działa program?
12 Jak zapisać do pliku zawartość komponentu TMemo?
13 Jak w Delphi wykryć wejście i wyjście myszki w obszar przycisku?
14 Jak zmierzyć długość tekstu w pikselach a nie znakach?
15 Jak z poziomu Delphi wykonać program DOSa?
16 Jak obsłużyć komunikat Windows którego forma nie obsługuje np. wm_NCHitTest?
17 Jak dostosować wydruk do różnych drukarek?
18 Jak sprawić aby dymki z podpowiedziami nie znikały?
19 Mam problem z przesiadką z Delphi 1.0 na Delphi 2.0. Pliki binarne zapisywane przez program po rekompilacji przestały się wczytywać.
20 Jak załadować bitmapę z zasobów pod Delphi 1.0?
21 Jak sprawdzić gdzie znajduje się mysz?
22 Dlaczego nie działa StretchDraw dla ikon?
23 Jak zrobić listę otwartych okien w Windows?
24 Dlaczego program korzystający z baz danych po przeniesieniu na inny komputer nie chce działać?
25 Jak reagować na zmianę rozdzielczości w trakcie działania programu?
26 Jak dodać wizytówkę programu (ang. splash screen)?
27 Jak dodać właściwości do formy aby były widoczne w okienku ObjectInspector?
28 Jak pobrać listę właściwości obiektu w trakcie wykonywania programu?
29 Jak dodać własną pozycję do menu wywoływanego spod Exploratora po kliknięciu prawym przyciskiem myszy?
30 Jak sprawdzić czy uruchomiony program jest już w pamięci?
31 Jak wyświetlić standardowe okno Windows służące do wybierania katalogu?
32 Czy jest możliwe skompilowanie programu napisanego w Delphi 3.0 tak aby działał w Windows 3.11?
33 Gdzie i za ile można kupić Delphi, gdzie można znaleźć informację o tym produkcie?
34 Jak dodać do formy w czasie wykonywania programu kilka komponentów?
35 Jak wydrukować zawartość memo?
36 Jak obsłużyć COM spod Delphi 2.0?
37 Co to jest RxLib i skąd to można ściągnąć?
38 Jak dodać nową wartość klucza do rejestru?
39 Jak zamienić liczbę na "słownie złotych"?
40 Jak wyrażenie matematyczne zamienić na liczbę?
41 Jak odczytać numer seryjny dysku lub dyskietki?
42 Czy w Delphi istnieje odpowiednik komend IN, OUT umożliwiających wysłanie pod określony adres urządzenia WE-WY określonej liczby?
43 Napisałem program korzystający z THTML i po przeniesieniu na inny komputer pojawia się błąd "Exception EOleSysError in module..." co się dzieje?
44 Jak wywołać program 32-bitowy i poczekać na jego zakończenie?
45 Jak wyświetlić plik JPG (instalacja jpeg.dcu z katalogu LIB nie pomaga)?
46 Jak zrobić wygaszacz w Delphi 1.0?
47 Jak dodać skrót do Desktopu lub Menu Start w Windows 95?
48 Mam problemy z bazami danych w sieci, nie pojawiają się zmiany w bazach.
49 Jak przeszukiwać Delphi Help i Win32 Help jednocześnie?
50 Gdzie mogę znaleźć dodatkowe informacje o Delphi?

1.Jak zrobić pasek gradientowy?
gradient, tło, instalator

Należy narysować wiele prostokątów (poziomo lub pionowo) stopniowo zmieniając im kolor. Np.:

procedure TForm1.FormPaint(Sender: TObject);
const N=100;
var Y:Integer;
    Cl:TColor;
begin
  for Y:=0 to N-1 do
  with Canvas do
  begin
  Cl:=RGB(0,0,Round(50+205*(Y/N)));
  Pen.Color:=Cl;
  Brush.Color:=cl;
  Rectangle(0,Round(ClientHeight*(Y/N)),ClientWidth,Round(ClientHeight*((Y+1)/N)));
  end;
end;

Spowoduje to dodanie do formy tła jakie często występuje w programach instalacyjnych. Aby nie występowały problemy przy zmianie rozmiarów formy należy dodać jeszcze poniższy kod:

procedure TForm1.FormResize(Sender: TObject);
begin
 Invalidate;
end;


2.Jak przejść z jednego komponentu TEdit do drugiego przy pomocy Entera (domyślnie przechodzi się przy pomocy Tab)?
Tab, Enter, TEdit, klawisz

Należy zmienić obsługę klawisza Enter w każdym z komponentów. Przykładowy kod:

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key=#13 then
  begin
  Perform(wm_NextDlgCtl,0,0);
  Key:=#0;
  end;
end;

Do każdego komponentu TEdit należy podstawić powyższą procedurę jako obsługę zdarzenia OnKeyPress.Można to zrobić klikając na formie z wciśniętym klawiszem Shift na każdym komponencie TEdit a następnie w okienku ObjectInspector klikając podwójnie na polu OnKeyPress (w okienku nie będzie widoczna nazwa komponentu).

Krzysztof Świątkowski zwrócił mi uwagę na trochę odmienne podejście. Zamiast powyższego dodajemy do formy obsługę:

procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (Key=VK_RETURN) and ([ssCtrl,ssShift]*Shift=[]) then
  Perform(WM_NEXTDLGCTL,0,0);
end;

Dodatkowo ustawiamy właściwość formy KeyPreview na True. Wtedy Enter działa jak Tab na całej formie a nie tylko wybranych kontrolkach.

Źródło informacji: Krzysztof Świątkowski.


3.Gdzie można znaleźć archiwum grupy pl.comp.lang.delphi?
archiwum, pl.comp.lang.delphi

Na stronie DejaNews znajduje się archiwum wszystkich grup dyskusyjnych. Korzystając z tamtejszej wyszukiwarki należy zdefiniować filtr obejmujący szukaną grupę. Można też ściągnąć archiwum grupy wraz z przeszukiwarką z mojej strony.


4.Gdzie można znaleźć informacje i nagłówki do DirectX?
DirectX, Blake Stone, DelphiX, DelphiJedi

Informacje o DirectX oraz pliki nagłówkowe w formacie *.H (do języka C) można znaleźć na stronach firmy Microsoft dotyczących Microsoft Software Development Network. Jeśli jesteś tam po raz pierwszy to będziesz musiał się zarejestrować (jest to bezpłatne). Czasem można znaleźć DirectX SDK (Software Development Kit) na płytach CD dołączanych do czasopism komputerowych. Przetłumaczone pliki nagłówkowe znajdują się na stronach Blake'a Stone chociaż trudno jest się tam dostać. Mirror nagłówków prowadzi również Radosław Przybył. Na DSP znajduje się również biblioteka DelphiX znacznie ułatwiająca pisanie programów pod DirectX. Warto też zajrzeć na strony projektu JEDI.


5.Co to jest DSP?
DSP, sunsite.icm.edu.pl

Delphi Super Page prowadzona jest przez Roberta Czerwińskiego na serwerze ICM. Jest to jedna z największych (o ile nie największa) biblioteka komponentów do Delphi, C++Buildera i JBuildera na świecie. Jeśli czegoś nie ma na DSP to raczej małe są szanse, że w ogóle istnieje :-). DSP ma również wiele mirrorów poza Polską.


6.Co się stało z procedurą Delay z TurboPascala? Jak mam zrobić w Delphi pauzę?
Delay, pauza

Nie ma w Delphi zaimplementowanej procedury Delay. Można jako jej zamiennika użyć funkcji WinAPI o nazwie Sleep. Powoduje ona zawieszenie wykonania programu na określoną liczbę milisekund. Jednakże w tym czasie Twoja aplikacja nie będzie mogła obsługiwać komunikatów Windows. Dlatego też czasem lepszym rozwiązaniem jest użycie takiego kodu:

procedure TForm1.Button1Click(Sender: TObject);
var Teraz:TDateTime;
begin
  // Tu wstawiamy operacje wykonywane przed pauzą

 Teraz:=Now;
  repeat
  Application.ProcessMessages; // Pozwalamy aplikacji obsłużyć komunikaty
  until Teraz+5/SecsPerDay<Now;  // 5 to liczba sekund pauzy

  // Tu operacje wykonywane po pauzie
end;

Należy pamiętać o ważnej rzeczy: powyższy kod nie gwarantuje że inne procedury obsługi zdarzeń nie zostaną wykonane a tylko, że wykonanie kodu tej procedury zostanie wstrzymane na kilka sekund.


7.Jak drukować tekstowo w Delphi?
drukowanie, tekst

Należy korzystać z funkcji WinAPI operujących na drukarkach:

uses WinSpool,Printers;

procedure TForm1.Button1Click(Sender: TObject);
var Size,n:Integer;
    H:THandle;
    Info:PAddJobInfo1;
    F:TextFile;
    sPrinterName,sDriver,sPort:array[0..255]of Char; // sDriver i sPort nie będą
                                                     // wykorzystane
begin
 Printer.GetPrinter(sPrinterName,sDriver,sPort,h);

 OpenPrinter(sPrinterName,H,nil);
  try
  AddJob(H,1,nil,0,Size); // pobranie rozmiaru bufora

  GetMem(Info,Size);

  try
   // Poniższa funkcja zwraca nam nazwę pliku do którego możemy zapisywać
   AddJob(H,1,Info,Size,n);

   // Tutaj zapisujemy do pliku
   AssignFile(F,Info^.Path);Rewrite(F);
   try
    Writeln(F,'Hello world!');
    Writeln(F,'To jest test drukowania tekstowego...');
   finally
    CloseFile(F);
   end;

   // Wrzucamy plik do kolejki drukowania, potem Windows go skasuje
   ScheduleJob(H,Info^.JobId);
   finally
    // Zwalniamy pamięć...
    FreeMem(Info,Size);
   end;
  finally
  // ...i drukarkę
  ClosePrinter(H);
  end;
end;

Można też spróbować innego sposobu. Użyć CreateFile aby otrzymać uchwyt do LPT1:

LPTHandle:=CreateFile('LPT1',GENERIC_WRITE,0,PSecurityAttributes(nil),
                      OPEN_EXISTING, FILE_FLAG_OVERLAPPED,0);

Następnie użyć WriteFile aby wysłać kolejne znaki lub:

while not TransmitCommChar(LPTHandle,CharToSend) do Application.ProcessMessages;

Powyższy kod wysyła kolejne znaki na port równoległy za każdym razem czekając na obsłużenie znaku przez drukarkę.

Źródło informacji: Tomasz Pytlik, Krzysztof Świątkowski, Chris Monson


8.Jak wywołać domyślny program pocztowy z wpisanym już adresem odbiorcy?
email, adres, poczta

Należy skorzystać z funkcji WinAPI ShellExecute na przykład w ten sposób:

uses ShellApi;

procedure TForm1.Button1Click(Sender: TObject);
begin
 ShellExecute(Handle,'open','mailto:wieczor@polbox.com','','',sw_Normal);
end;

Oprócz samego nadawcy można też podać tytuł i treść listu umieszczając jako argument ShellExecute następujący tekst:

mailto:s_dusza@koti.com.pl?subject=test&body=Tu+jest+tresc

W podobny sposób można otworzyć okno Exploratora Windows podając nazwę katalogu a także wywołać aplikację obsługującą dany format pliku.

Marcin Qfel Zaleski podesłał kod krzystający z funkcji MAPI. Dzięki MAPI można w pełni kontrolować proces wysyłania poczty, w szczególności dodać do listu plik jako załącznik:

procedure cos_tam;
var
  MAPIFileDesc    : TMAPIFileDesc;
  MAPIMessage     : TMAPIMessage;
  MAPIRecipDesc   : TMapiRecipDesc;
  hMAPIDLL        : THandle;
  pfnMAPISendMail : TFNMAPISendMail;
begin

  //załadowanie biblioteki
  hMAPIDLL := LoadLibrary('MAPI32.DLL');
  if hMAPIDLL=0 then
    begin
      //zle się dzieje
    end;

  //pobranie adresu funkcji
  @pfnMAPISendMail := GetProcAddress(hMAPIDLL,'MAPISendMail');
  if @pfnMAPISendMail=nil then
    begin
      FreeLibrary(hMAPIDLL);
      //zle się dzieje
    end;

  //przygotowanie opisu adresata
  FillChar(MAPIRecipDesc,SizeOf(TMAPIRecipDesc),0);
  with MAPIRecipDesc do
    begin
      ulRecipClass := MAPI_TO;
      lpszName := 'John Smith';
      lpszAddress := 'johnsmith@server.com';
    end;

  //przygotowanie opisu załącznika
  FillChar(MAPIFileDesc,SizeOf(TMAPIFileDesc),0);
  with MAPIFileDesc do
    begin
      nPosition := Cardinal(-1);
      lpszPathName := 'C:\Moje dokumenty\list.doc';
      lpszFileName := 'list.doc';
    end;

  //przygotowanie rekordu wiadomości
  FillChar(MAPIMessage,SizeOf(TMAPIMessage),0);
  with MAPIMessage do
    begin
      lpszSubject := 'temat listu';
      lpszNoteText := 'tresc listu';
      nRecipCount := 1;
      lpRecips := @MAPIRecipDesc;
      nFileCount := 1;
      lpFiles := @MAPIFileDesc;
    end;

  //wysłanie
  if
pfnMAPISendMail(0,Handle,MAPIMessage,MAPI_DIALOG,0)<>SUCCESS_SUCCESS
then
    begin
      FreeLibrary(hMAPIDLL);
      //zle się dzieje
    end;
  //zwolnienie zasobów
  FreeLibrary(hMAPIDLL);
end;

Źródło informacji: Sebastian A. Dusza, Marcin Qfel Zaleski.


9.Dlaczego programy napisane w Delphi nie "fruną" na pasek zadań a po prostu się minimalizują, jak Windows 3.11?
minimalizacja, animacja, Win95, pasek zadań

Dlaczego, że programiści z Borland Int. wyłączyli animację okienek. Oddaję głos Krzyśkowi Świątkowskiemu:

"Dlatego że przy minimalizacji chłopcy z Borlanda animację wyłączają, jak się ją włączy to to głupio wygląda bo tak naprawdę minimalizuje się nie to okienko co trzeba. Na upartego można to zrobić samemu funkcją API ale nie pamiętam jak się nazywała."

Dlaczego Borland tak to rozwiązał?

"Żeby można było w każdej chwili wołać funkcje które wymagają uchwytu do okna Application [Delphi M.W.] tworzy prawdziwe okno główne u siebie. To okno o którym my mówimy że jest główne (MainForm) jest po prostu widoczne a prawdziwe okno główne to od kolejki komunikatów aplikacji siedzi w TApplication. Po zmianie tej funkcji o której wspomniałem widać animację okna głównego, czyli tego co siedzi w Application a nie głównej formy i dlatego wygląda głupio. Ktoś kiedyś mówił że na DSP jest komponent który pozwala to jako obejść. Ja znalazłem jedynie obejście w postaci funkcji rysującej animacje ramki okna."

Jak to obejść?

"Przekompilować unit Forms tam jest jakaś taka funkcyjka która wyłącza animacje, jak chcesz to mogę sprawdzić bo gdzieś mam chyba stare posty na ten temat"

Podejrzewam, że Krzyśkowi chodziło o funkcję:

procedure ShowWinNoAnimate(Handle: HWnd; CmdShow: Integer);
var
  Animation: Boolean;
begin
  Animation := GetAnimation;
  if Animation then SetAnimation(False);
  ShowWindow(Handle, CmdShow);
  if Animation then SetAnimation(True);
end;

Jest ona wywoływana w kilku miejscach modułu Forms i należałoby ją zmienić na:

procedure ShowWinNoAnimate(Handle: HWnd; CmdShow: Integer);
begin
  ShowWindow(Handle, CmdShow);
end;

W tym miejscu muszę dodać, że dobrze jest zrobić sobie kopię zapasową wszystkich plików bibliotecznych (PAS, DCU, DPL itp.) przed rekompilacją bibliotek standardowych.

Suplement

Ostatnio (dzięki Darkowi Brzezińskiemu) doszły nowe informacje. Aby poprawnie działało minimalizowanie okien w Windows 95 należy:

W module projektu:

  • dodać do uses Windows,
  • zadeklarować zmienną np. ES : Integer;
  • po Application.Initialize dopisać:

ES:=GetWindowLong(Application.Handle, GWL_EXSTYLE);
ES:=ES or WS_EX_TOOLWINDOW and not WS_EX_APPWINDOW;
SetWindowLong(Application.Handle,GWL_EXSTYLE,ES);

Okno z ustawionym stylem WS_EX_TOOLWINDOW nie jest pokazywane na pasku zadań.

W module głównego formularza aplikacji należy dodać:

procedure CreateParams(var Params:TCreateParams);override;
procedure WMSysCommand(var Message:TWMSysCommand);message WM_SYSCOMMAND;
...
procedure TForm1.CreateParams(var Params:TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do ExStyle:=ExStyle or WS_EX_APPWINDOW;
end;

procedure TForm1.WMSysCommand(var Message:TWMSysCommand);
begin
  if (Message.CmdType and $FFF0=SC_MINIMIZE) then WindowState:=wsMinimized
  else inherited;
end;

Ominięcie domyślnej obsługi komunikatu, która wywołuje Application.Minimize. Po tych zmianach minimalizuje się formularz (z animacją), a nie ukryte okno aplikacji.

Źródło informacji: Darek Brzeziński, Krzysztof Świątkowski


10. Co to znaczy: okienko ładowane dynamicznie?
forma, ładowanie dynamiczne

Standardowo Delphi tworzy wszystkie formy przy starcie programu (spójrz do pliku *.DPR). Tak stworzone formy istnieją przez cały czas działania aplikacji i zasoby przez nie zajmowane zwalniane są dopiero po jej zakończeniu. Przez większość czasu formy są ukryte i pokazują się dopiero gdy wywołamy procedurę Show. Aby dynamicznie ładować okienka należy przesunąć je w opcjach projektu z listy Auto-Create Forms na Available Froms. Potem jeśli będziemy chcieli skorzystać z okienka dialogowego dynamicznie to możemy to zrobić na przykład w ten sposób:

procedure TForm1.Button1Click(Sender: TObject);
begin
 Form2:=TForm2.Create(Application);
  try
  Form2.ShowModal;
  finally
  Form2.Free;
  end;
end;

Widać tu ręczne utworzenie formy-obiektu, jego wywołanie i skasowanie. Z oknami niemodalnymi jest inaczej bo nie zachowują się one jak "procedury" (wybaczcie mi ten skrót myślowy). Jeśli chcemy utworzyć okno niemodalne w jednym egzemplarzu to możemy zrobić to tak:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(Form2)then Form2:=TForm2.Create(Application);
 Form2.Show;
end;

Czyli najpierw sprawdzamy czy już utworzyliśmy formę (jeśli nie to ją tworzymy) a potem pokazujemy ją na ekranie. Od tej chwili forma żyje własnym życiem. Jeśli chcemy zwolnić zajmowaną przez nią pamięć gdy użytkownik ją zamknie to należy oprogramować jej zdarzenie OnClose (zwracam uwagę na nazwę formy: Form2 a nie Form1):

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 Action:=caFree; // None, Hide, Minimize
 Form2:=Nil;
end;

Tu mieliśmy do wyboru: zwolnić pamięć zajmowaną przez formę, nic nie robić (wtedy nie da się zamknąć takiego okienka), ukryć formę lub ją zminimalizować. Ostatnia linia jest potrzebna gdyż zwalniana forma nie aktualizuje zmiennej Form2. W efekcie przy ponownym wywołaniu TForm1.Button1Click pojawiłby się błąd.


11. Jak sprawdzić w jakim trybie graficznym działa program?
tryb graficzny, rozdzielczość ekranu

W module Forms jest zadeklarowany obiekt Screen którego dwie właściwości Screen.Width i Screen.Height określają rozmiary ekranu.


12. Jak zapisać do pliku zawartość komponentu TMemo?
TMemo, plik, zapisywanie

Skorzystać z metody SaveToFile. Memo ma również kilka innych ciekawych możliwości:

TMemo.Lines.SaveToFile Zapisuje zawartość memo do pliku tekstowego
TMemo.Lines.LoadFromFile Ładuje z pliku tekstowego zawartość memo
TMemo.Lines.Count Podaje liczbę linii tekstu zawartego w memo
TMemo.CopyToClipboard Kopiuje zaznaczony tekst do schowka
TMemo.PasteFromClipboard Na odwrót
TMemo.SelectAll Zaznacza cały tekst w memo

Właściwość Lines jest typu TStrings (dokładniej pochodzi od tego typu) i da się z nią zrobić to samo co z obiektem typu TStringList. Aby skopiować zaznaczony w memo tekst do schowka należy wykonać:

Memo1.CopyToClipboard;

zaś aby zaznaczyć cały tekst w memo:

Memo1.SelectAll;

Po więcej informacji proponuję zajrzeć do helpa.


13. Jak w Delphi wykryć wejście i wyjście myszki w obszar przycisku?
myszka, przycisk, wejście, wyjście

Ten problem pojawia się najczęściej przy pisaniu własnych komponentów i najprościej rozwiązać go właśnie pisząc komponent. Poniżej podaję deklarację przykładowego komponentu wykorzystującego komunikaty cm_MouseEnter i cm_MouseLeave generowane przez Delphi do sprawdzenia pozycji myszki (za uwagi dotyczące tego kodu dziękuję Markowi Parfianowiczowi):

unit Button1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TMyButton = class(TButton)
    protected   
    FMouseOver, FMouseOut : TNotifyEvent;
    procedure CMMouseEnter(var Message:TMessage);message cm_MouseEnter;
    procedure CMMouseLeave(var Message:TMessage);message cm_MouseLeave;
    published  
    property OnMouseOver: TNotifyEvent read FMouseOver write FMouseOver;
    property OnMouseOut: TNotifyEvent read FMouseOut write FMouseOut;
  end;

procedure Register;

implementation

procedure TMyButton.CMMouseEnter(var Message:TMessage);
begin
  if Assigned(FMouseOver)then OnMouseOver(Self);
  Message.Result:=1;
end;

procedure TMyButton.CMMouseLeave(var Message:TMessage);
begin
  if Assigned(FMouseOut)then OnMouseOut(Self);
  Message.Result:=1;
end;

procedure Register;
begin
  RegisterComponents('T-1000', [TMyButton]);
end;

Po dodaniu komponentu do palety możemy już z niego korzystać.


14. Jak zmierzyć długość tekstu w pikselach a nie znakach?
tekst, TCanvas, długość, font, czcionka

Należy użyć metody TCanvas.TextWidth('Ala ma kota') podającej szerokość tekstu właśnie w pikselach na konkretnym urządzeniu (ekranie, drukarce) przy aktualnie ustawionym foncie.


15. Jak z poziomu Delphi wykonać program DOSa?
Exec, DOS, program, uruchomić

Należy użyć funkcji WinExec z WinAPI. Na przykład:

procedure TForm1.Button1Click(Sender: TObject);
begin
 WinExec('rar.exe a archiwum *.*',sw_Normal);
end;

Spowoduje to wywołanie programu RAR z odpowiednimi parametrami i utworzenie przez niego archiwum.


16. Jak obsłużyć komunikat Windows którego forma nie obsługuje np. wm_NCHitTest?
komunikaty

Należy dopisać w sekcji public:

type
  TForm1 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
    procedure WMNCHitTest(var Message:TWMNCHitTest);message wm_NCHitTest;
  end;

zaś w części implementation:

procedure TForm1.WMNCHitTest(var Message : TWMNCHitTest);
var P:TPoint;
begin

  inherited;
  P:=ScreenToClient(SmallPointToPoint(Message.Pos));
  with Label1,Message do
    if (P.X>=Left) and (P.X<Left+Width) and
        (P.Y>=Top) and (P.Y<Top+Height) then
      Result:=htCaption;
end;

W tym przypadku powierzchnia etykiety Label1 zachowuje się jak jej pasek tytułowy. Za poprawki w powyższym kodzie dziękuję Maciejowi "MACiASowi" Pilichowskiemu


17. Jak dostosować wydruk do różnych drukarek?
drukowanie, drukarka, rozdzielczość

Należy skorzystać z funkcji GetDeviceCaps z WinAPI i z modułu Printers. Na przykład:

procedure TForm1.Button1Click(Sender: TObject);
var XD,YD:Integer;
begin
 XD:=GetDeviceCaps(Printer.Handle,LogPixelSX); // liczba pikseli na cal w poziomie
 YD:=GetDeviceCaps(Printer.Handle,LogPixelSY); // liczba pikseli na cal w poziomie
  with Printer,Printer.Canvas do
  begin
  Title:='Wydruk próbny';
  BeginDoc;
  try
   // Linia w poprzek całej kartki
   MoveTo(PageWidth,0);LineTo(0,PageHeight);
   // Linia o długości 1 cala
   MoveTo(0,0);LineTo(XD,YD);
  finally
   EndDoc;
  end;
  end;
end;

To jednak nie koniec. Okazuje się, że NetManiak ma do tego kilka uwag:

"Już znalazłem formułę, dzięki której można dokładnie obliczyć ile musi mieć pixeli linia, by na drukarce objawiła się jako 1 calowa. Teoretycznie powinno to być (jak sugeruje kolega BACIK, tudzież dokumentacja windows) LOGPIXELSX i LOGPIXELSY. Moje doświadczenia wskazują jednakże, iż rzeczywista wartość wynosi:

w poziomie: LOGPIXELX * PHYSICALWIDTH / HORZRES
w pionie: LOGPIXELSY * PHYSICALHEIGHT / VERTRES

gdzie LOGPIXELX - wynik funkcji GetDeviceCaps(LOGPIXELX ) itd...

Sprawdziłem to na 2 drukarkach: Cannon BJC4300 i HP (atramentówka, A4, oznaczenia nie pamiętam)."

Źródło informacji: Adam K. "NetManiak".


18. Jak sprawić aby dymki z podpowiedziami nie znikały?
dymki, hint

Należy ustawić HintHidePause na dość dużą wartość:

Application.HintHidePause:=100000;


19. Mam problem z przesiadką z Delphi 1.0 na Delphi 2.0. Pliki binarne zapisywane przez program po rekompilacji przestały się wczytywać.
błędy odczytu, plik, rekordy, record

Problemem może być zarówno zmiana wielkości typu Integer (teraz jest 4 bajtowy czyli dawne Longint) jak i wyrównywanie przez Delphi zmiennych w pamięci do adresów podzielnych przez 4. Na to pierwsze pomoże zmiana typów zmiennych w programie z Integer na SmallInt (które jest 2 bajtowe). Aby obejść ten drugi problem trzeba albo wyłączyć wyrównywanie zmiennych w ustawieniach kompilatora (ale spowolni to program) albo rekordy zapisywane na dysk zadeklarować ze słówkiem packed co lokalnie wyłączy wyrównywanie zmiennych.


20. Jak załadować bitmapę z zasobów pod Delphi 1.0?
bitmapa, zasoby, ładowanie

Skorzystaj z funkcji LoadBitmap:

Image.Picture.Bitmap.Handle:=LoadBitmap(hInstance,'NAZWA_BITMAPY');


21. Jak sprawdzić gdzie znajduje się mysz?
myszka, pozycja, ekran

Pozycję myszy (we współrzędnych ekranowych) podaje funkcja GetCursorPos.


22. Dlaczego nie działa StretchDraw dla ikon?
ikony, StretchDraw, rysowanie

Niedoróbka Delphi. Aby narysować rozciągniętą ikonę należy skorzystać z funkcji:

DrawIconEx(Canvas.Handle, 0, 0, Icon.Handle, szerokosc, wysokość, 0, 0,DI_NORMAL);


23. Jak zrobić listę otwartych okien w Windows?
lista okien, Windows, pulpit, pasek zadań

Oto przykładowy kod wykorzystujący funkcje WinAPI:

function EnumWindowsProc(WHandle: HWND; LParM: LParam): Boolean;StdCall;Export;
var Title,ClassName:array[0..128] of char;
    sTitle,sClass,Linia:STRING ;
begin
 Result:=True;
 GetWindowText(wHandle, Title,128);
 GetClassName(wHandle, ClassName,128);
 sTitle:=Title;
 sClass:=ClassName;
  if IsWindowVisible(wHandle) then
  begin
  Linia:=sTitle+'        '+sClass+'       '+IntToHex(wHandle,4);
  Form1.Listbox1.Items.Add(Linia);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 EnumWindows(@EnumWindowsProc,0);
end;

Na formie powinien być komponent typu TListBox. Zostanie on wypełniony listą aktywnych okien.

Źródło informacji: Sławomir Świder


24. Dlaczego program korzystający z baz danych po przeniesieniu na inny komputer nie chce działać?
bazy danych, BDE, błąd

Delphi korzysta z BDE (Borland Database Engine). Jest to program pośredniczący pomiędzy Delphi a bazami danych i musi być zainstalowany na docelowym komputerze. Można to zrobić "ręcznie" - na kompakcie z Delphi jest katalog z wersją instalacyjną samego BDE. Można też użyć InstallShield Express - jest to okrojona wersja programu do tworzenia programów instalacyjnych. Znajduje się na płycie z Delphi. Jedną z jego opcji jest właśnie instalowanie programów wymagających BDE. Jest szybki i robi to całkiem nieźle.

Najczęstszym objawem braku BDE jest błąd nr 2109 z komunikatem "brak pliku IDAPI32.DLL".


25. Jak reagować na zmianę rozdzielczości w trakcie działania programu?
rozdzielczość, ekran, zmiana

Należy obsłużyć komunikat wm_DisplayChange dopisując do formy procedurę:

procedure WMDisplayChange(var msg : TWMDisplayChange);message wm_DisplayChange;   

Dodatkowo w Delphi 2.0 trzeba dopisać definicję typu:

type TWMDisplayChange = record
      Msg: Cardinal;
      BitsPerPixel: Integer;// ilość kolorów - 8-256, 15-32k, 16-64k,24/32-16mln
      Width: Word;    //szerokość
      Height: Word;   //wysokość
      end;

Należy ją umieścić przed deklaracją procedury. Aby przeczytać o dodawaniu własnej obsługi komunikatów zajrzyj do pytania 16.

Uwaga: Komunikat wm_DisplayChange jest specyficzny dla Windows 95 nie występuje ani w Windows NT ani w Win32s API. Wczesne wersje Windows 95 mogą wysyłać ten komunikat dwukrotnie - przed i po zmianie rozdzielczości.

Źródło informacji: Krzysztof Świątkowski.


26. Jak dodać wizytówkę programu (ang. splash screen)?
wizytówka, logo, splash, ładowanie

Należy stworzyć nową formę, nazwać ją np. TLogo, ustawić właściwości

BorderStyle bsNone
BorderIcons []
FormStyle fsStayOnTop

Do formy dodać TImage z obrazkiem. Tak przygotowaną formę należy jeszcze usunąć z listy automatycznie tworzonych form (jak to zrobić patrz pytanie 10). Teraz przechodzimy do kodu źródłowego projektu.

begin
  Application.Initialize;
  // Utworzenie i pokazanie formy
  Logo:=TLogo.Create(Application);
  Logo.Show;
  Logo.Update;
 
  // Tu wstawia Delphi utworzenie automatycznych form
  Application.Run;
end;

Do okna głównego dodajemy:

procedure TForm1.FormShow(Sender : TObject);
begin
  if assigned(Logo) then  
  begin
  Logo.Free; 
  Logo:=nil;
  end;
end;

I gotowe. W katalogu DEMOS na płycie z Delphi znajduje się program MASTAPP z winietą.


27. Jak dodać właściwości do formy aby były widoczne w okienku ObjectInspector?
właściwości, ObjectInspector, dziedziczenie, forma

Dokładnej odpowiedzi nie znam. Przytoczę tu fragment listu Roberta Perlińskiego:

"Niestety nie mogę odnaleźć kawałka kodu, który napisałem jakiś czas temu, a który implementował dokładnie to o czym mówimy. Z głowy i z tego co pamiętam:

1. Tworzymy moduł z definicją formy np. TPawelForm, która zawiera "custom property" np. PawelProperty. Forma powinna przeciążać konstruktor Create, ale zamiast standardowego inherited powinna wołać CreateNew i InitInheritedComponent (patrz TCustomForm.Create zdefiniowane w pliku forms.pas)

2. Tworzymy "Module Creator" np. TPawelFormCreator = class(TIModuleCreator)

3. Tworzymy "Expert" np. TPawelFormExpert = class(TIExpert)

4. Rejestrujemy TPawelForm i TPawelFormExpert

5. Jeśli w p. 1-4 zrobiliśmy wszystko jak należy, każda nowa forma utworzona przy pomocy TPawelFormExpert, posiadać powinna PawelPropety dostępną z poziomu Object Inspectora. Obiecuję, że jeśli odnajdę pełny tekst programu, wyślę go na listę."

I tyle Robert. Niestety programu chyba nie wysłał (przynajmniej ja go nie zauważyłem).

Źródło informacji: Robert Perliński


28. Jak pobrać listę właściwości obiektu w trakcie wykonywania programu?
właściwości, RTTI, runtime

Poniżej jest tłumaczenie Delphi TI 3166:

Czasem przydatna jest informacja o właściwościach komponentu w momencie wykonywania programu. Listę właściwości można uzyskać przy pomocy funkcji GetPropList. Typy, funkcje i procedury (włączając w to GetPropList) pozwalające na dostęp do właściwości znajdują się w pliku TYPINFO.PAS.

GetPropList jest zdefiniowana jako:

function GetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds;
  PropList: PPropList): Integer; 

Pierwszym parametrem GetPropList jest zmienna typu PTypeInfo, jest to część RTTI (Run Time Type Information) dostępnej dla każdego obiektu. Typ ten jest zdefiniowany następująco:

PPTypeInfo = ^PTypeInfo;  
PTypeInfo = ^TTypeInfo;  
TTypeInfo = record
    Kind: TTypeKind;    
    Name: ShortString;   
    {TypeData: TTypeData}  
end;

Rekord TTypeInfo może być odczytany przy pomocy właściwości ClassInfo obiektu. Na przykład, jeśli pobieramy informacje o TButton wywołanie może wyglądać następująco:

GetPropList(Button1.ClassInfo, ....

Drugi parametr (typu TTypeKinds) jest typu zbiorowego i działa jak filtr decydując o tym jakie rodzaje właściwości zamieścić w liście. Jest kilka możliwych wartości jakie można mu nadać jednakże tkProperties obsługuje najważniejsze. Teraz wywołanie ma postać:

GetPropList(Button1.ClassInfo, tkProperties ....

Ostatni parametr, PPropList jest tablicą typów PPropInfo:

PPropList = ^TPropList;  
TPropList = array[0..16379] of PPropInfo;

Teraz nasze wywołanie może mieć postać:

procedure TForm1.FormCreate(Sender: TObject);
var   PropList: PPropList;
begin
  PropList := AllocMem(SizeOf(PropList^));
  GetPropList(TButton.ClassInfo, tkProperties + [tkMethod], PropList);
...

Przykład przytoczony poniżej pokazuje nie tylko nazwę właściwości ale także jej typ. Nazwa typu znajduje się w dodatkowej strukturze w rekordzie TPropInfo. Zauważmy, że pole PropType wskazuje na rekord TTypeInfo zawierający nazwę typu właściwości:

PPropInfo = ^TPropInfo;  
TPropInfo = packed record     
             PropType: PPTypeInfo;
         GetProc: Pointer;    
     SetProc: Pointer;    
             StoredProc: Pointer;
             Index: Integer;    
             Default: Longint;    
             NameIndex: SmallInt;
             Name: ShortString;  
            end;  

PPTypeInfo = ^PTypeInfo;  
PTypeInfo = ^TTypeInfo;
TTypeInfo = record     
             Kind: TTypeKind;    
             Name: ShortString;
             {TypeData: TTypeData}  
            end;

Poniższy kod przykładowy pokazuje jak wywołać GetPropList i jak odwoływać się do elementów zwróconej tablicy. Przykład wymaga obecności na formie TListBox:

uses TypInfo;

procedure TMainForm.FormCreate(Sender: TObject);
var PropList: PPropList;
    i: integer;
begin
 PropList:=AllocMem(SizeOf(PropList^));
 i:=0;
  try
  GetPropList(TForm.ClassInfo,tkProperties+[tkMethod],PropList);
  while (PropList^[i]<>Nil)and(i<High(PropList^)) do
  begin
   ListBox1.Items.Add(PropList^[i].Name+':'+PropList^[i].PropType^.Name);
   Inc(i);
  end;
  finally
  FreeMem(PropList);
  end;
end;

Tyle Borland. Ze swej strony dodam, że aby powyższe działało obiekt musi być kompilowany z włączeniem generowania RTTI lub być pochodną takiego obiektu. RTTI jest włączone dla jednego obiektu z VCL - TPersistent. Wystarczy więc, że nasz obiekt będzie pochodną TPersistent. To dla tych, którzy nie mają dostępu do źródeł bibliotek. Dla pozostałych informacja jak włączyć generowanie RTTI. Proszę spojrzeć na deklarację TPersistent:

{ TPersistent abstract class }

{$M+}

  TPersistent = class(TObject)
  private
    procedure AssignError(Source: TPersistent);
  protected
    procedure AssignTo(Dest: TPersistent); virtual;
    procedure DefineProperties(Filer: TFiler); virtual;
    function   GetOwner: TPersistent; dynamic;
  public
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); virtual;
    function   GetNamePath: string; dynamic;
  end;

{$M-}

Widać, że za RTTI jest odpowiedzialny przełącznik {$M+} (można go zapisać też jako {$TYPEINFO ON}). Polecam odpowiednią stronę w helpie. Jest tam między innymi napisane, że RTTI jest generowane tylko dla pól w części published (nad czym niezmiernie boleję).

Źródło informacji: Delphi TI 3166


29. Jak dodać własną pozycję do menu wywoływanego spod Exploratora po kliknięciu prawym przyciskiem myszy?
Explorer, Explorator, menu podręczne, prawy przycisk myszy

Odpowiedź podał Michał Jaskólski:

procedure JakasTam;

var
  Rejestr:TRegistry;
  NazwaTypu:string;

begin
  try   
    Rejestr:=TRegistry.Create;
    Rejestr.RootKey := HKEY_CLASSES_ROOT;
    Rejestr.OpenKey('\.rozszerzenie',true);
    NazwaTypu:=Reg.ReadString('');
    Rejestr.CloseKey;
    Rejestr.OpenKey('\'+NazwaTypuHtml+'\shell\Koduj',true);  
    Rejestr.WriteString('','Koduj do...');
    Rejestr.CloseKey;
    Rejestr.OpenKey('\'+NazwaTypuHtml+'\shell\Koduj\command',true);
    Rejestr.WriteString('','"'+Application.ExeName+'" "%1"');
    Rejestr.CloseKey;
  finally
    Rejestr.Free;
  end;
end;

Źródło informacji: Michał Jaskólski


30. Jak sprawdzić czy uruchomiony program jest już w pamięci?
powtórne uruchamianie, dwa programy

Odpowiedź podał Paweł Schmidt:

hMapping:=CreateFileMapping(THANDLE($FFFFFFFF),nil,
 PAGE_READONLY,0,32,'ApplicationTestMap');
if GetLastError=ERROR_ALREADY_EXISTS then
begin
 Application.MessageBox('Program jest już uruchomiony','Informacja',
  mb_OK+MB_IconInformation);
 Application.Terminate;
end;

Od siebie dodam, że przy końcu aplikacji warto zrobić CloseHandle(hMapping). W Delphi 1.0 wystarczy sprawdzić wartość parametru HPrevInstance, jeśli jest niezerowy to program został już uruchomiony.


31. Jak wyświetlić standardowe okno Windows służące do wybierania katalogu?
katalog, standardowe okno, dialog, wybór

Należy skorzystać z funkcji SHBrowseForFolder:

uses ShlObj,ActiveX;

procedure TForm1.Button1Click(Sender: TObject);
var BI:TBrowseInfo;
    Buf:PChar;
    Dir,Root:PItemIDList;
    Alloc:IMalloc;
begin
  // Pobieramy obiekt zarządzający pamięcią
 SHGetMalloc(Alloc);
  // Przydzielamy pamięć na string
 Buf:=Alloc.Alloc(Max_Path);

  // Ograniczamy wybór tylko do katalogu "Menu Start\Programs"
 SHGetSpecialFolderLocation(Handle,CSIDL_PROGRAMS,Root);

  with BI do
  begin
  hwndOwner:=Form1.Handle;
  pidlRoot:=Root; // Tu można podać NIL żeby można było wybrać każdy katalog
  pszDisplayName:=Buf;
  lpszTitle:='Wybierz katalog'; // Etykietka przed listą katalogów
  ulFlags:=0;
  lpfn:=nil;
  end;

  try
  Dir:=SHBrowseForFolder(BI);
  if Dir<>Nil then
  begin
   // Pobieramy pełną ścieżkę do katalogu
   SHGetPathFromIDList(Dir,Buf);
   // Przykładowe zastosowanie
   ShowMessage(Buf);
   Alloc.Free(Dir);
  end;
  finally
  Alloc.Free(Root);
  Alloc.Free(Buf);
  end;
end;

Inne możliwe do wybrania katalogi specjalne:

CSIDL_BITBUCKET RecycleBin czyli kosz na śmieci
CSIDL_CONTROLS Wirtualny katalog ControlPanel
CSIDL_DESKTOP Wirtualny katalog Desktop
CSIDL_DESKTOPDIRECTORY Katalog na dysku przechowujący obiekty z desktopu
CSIDL_DRIVES My Computer
CSIDL_FONTS Wirtualny folder z fontami
CSIDL_NETHOOD Otoczenia sieciowe
CSIDL_NETWORK Wirtualny odpowiednik powyższego
CSIDL_PERSONAL Katalog Personal
CSIDL_PRINTERS Wirtualny folder z drukarkami
CSIDL_PROGRAMS Programy z menu Start
CSIDL_RECENT Ostatnio użyte dokumenty
CSIDL_SENDTO Folder SendTo
CSIDL_STARTMENU Całe StartMenu
CSIDL_STARTUP Grupa Autostart
CSIDL_TEMPLATES Szablony dokumentów

Wartości CSIDL_PROGRAMS można użyć przy dodawaniu własnych pozycji w menu Start.


32. Czy jest możliwe skompilowanie programu napisanego w Delphi 3.0 tak aby działał w Windows 3.11?
Windows 3.11, thunk, Win32s, 16-bit

Nie próbowałem tego w Delphi 3.0 ale kompilowałem kilka programów pod Delphi 2.0 korzystających z wielu komponentów Delphi (ale nie korzystałem bezpośrednio z WinAPI) i nie było żadnych problemów z uruchomieniem ich pod Windows 3.11. Oczywiście w docelowym systemie musi być zainstalowana nakładka Win32s. Jeśli chcesz korzystać bezpośrednio z WinAPI to sprawdź czy funkcja, której używasz ma swój odpowiednik w Win32s API. Pod Delphi 3.0 nie powinno być żadnych problemów pod warunkiem, że nie korzystamy z obiektów z zakładki Win32. Dodam jeszcze, że moje programy nie korzystały z baz danych (co nie znaczy, że takowe nie będą działać, po prostu testy musisz przeprowadzić we własnym zakresie).


33. Gdzie i za ile można kupić Delphi, gdzie można znaleźć informację o tym produkcie?
Delphi, kupno, BSC

Dystrybutorem produktów Borlanda w Polsce jest Borland Support Center. Tam też znajdują się aktualne ceny. Informacje można znaleźć na wspomnianym BSC jak również na stronach Inprise (dawniej Borland).

Niektórzy twierdzą, iż taniej jest sprowadzić Delphi ze Stanów niż kupować w Polsce.


34. Jak dodać do formy w czasie wykonywania programu kilka komponentów?
forma, komponenty, dodawanie

Należy dokładnie powtórzyć to co robi Delphi przy dodawaniu komponentów:

  • utworzyć komponent przy pomocy TEdit.Create(Form1)
  • ustawić właściwość parent na komponent nadrzędny (najczęściej formę ale czasem będzie to na przykład panel) wykonując Edit.Parent:=Form1
  • Poustawiać resztę interesujących nas właściwości

procedure TForm1.Button1Click(Sender: TObject);
var I,Y:Integer;
    Edit:TEdit;
begin
 Y:=5; // Pozycja, od której zaczynamy dodawać obiekty
  for I:=1 to 5 do
  begin
  Edit:=TEdit.Create(Self);
  Edit.Parent:=Self;

  // Tyle wystarczy aby poprawnie dodać obiekt
  // Poniżej ustawiamy te właściwości, które chcemy
  Edit.Top:=Y;
  Edit.Left:=5;
  Edit.Text:=Format('Okienko nr %d',[I]);

  // Następne okno utworzymy poniżej z odstępem 2 pikseli
  Y:=Y+Edit.Height+2;
  end;
end;

Nie musimy martwić się o zniszczenie obiektów destruktorem. Są one automatycznie zwalniane przez formę.


35. Jak wydrukować zawartość memo?
TMemo, drukowanie

Należy skorzystać z modułu Printers i procedury AssignPrn.

procedure TForm1.Button1Click(Sender: TObject);
var I:Integer;
    F:TextFile;
begin
 AssignPrn(F);Rewrite(F);
  try
  // Wypisujemy nagłówek kursywą...
  Printer.Canvas.Font.Style:=[fsBold];
  Writeln(F,'Zawartość memo');Writeln;

  // ... i zawartość memo linia po linii
  Printer.Canvas.Font.Style:=[];
  for I:=0 to Memo1.Lines.Count-1 do
   Writeln(F,Memo1.Lines[I]);
  finally
  CloseFile(F);
  end;
end;

Plik zaraz po wydruku musimy zamknąć gdyż tylko jeden plik może być skojarzony z drukarką. Należy też dodać w sekcji Uses moduł Printers bo stamtąd pochodzi procedura AssignPrn.


36. Jak obsłużyć COM spod Delphi 2.0?
COM, port szeregowy, transmisja

Oto przykładowy kod nadesłany przez MARFI:

procedure TForm.Button1Click(Sender : TObject);
var hCOM:THandle;
    nrWrit:DWORD;
    nrRead:DWORD;
    Errors:DWORD;
    Dcb:TDCB;
    ComStat:TComStat;
    buf:array[0..2048] of char;
begin
  //Otwarcie łącza COM
 hCOM:=CreateFile('COM3',GENERIC_WRITE OR GENERIC_READ,0,nil,OPEN_EXISTING,0,0);

  //Ustawienie parametrów transmisji - jak  MODE w DOS'ie
  if hCOM<>INVALID_HANDLE_VALUE then
  begin
  GetCommState(hCOM,Dcb);
  BuildCommDCB('19200,n,8,2',Dcb);
  SetCommState(hCOM,Dcb);
  end
  else
  begin
  ShowMessage('Błąd otwarcia portu COM : '+IntToStr(GetLastError()));
  Exit;
  end;

  try
  //Przygotowanie bufora
  ZeroMemory(@buf,SizeOf(buf));
  StrCopy(buf,'AT&V'+#13+#10);

  //Zapis bufora
  if not WriteFile(hCOM,buf,StrLen(buf),nrwrit,nil) then
   ShowMessage('Błąd zapisu do portu COM.');

  //Sprawdzenie czy jest coś w buforze COM
  ClearCommError(hCOM,Errors,@ComStat);

  //Odczytanie bufora gdy są dane
  if ComStat.cbInQue>0 then
   ReadFile(hCOM,buf,ComStat.cbInQue,nrRead,nil);
  finally
  CloseHandle(hCOM);
  end;
end;

Źródło informacji: MARFI


37. Co to jest RxLib i skąd to można ściągnąć?
RxLib, RxHint

RxLib jest biblioteką komponentów do Delphi 1, 2 i 3 w postaci kodów źródłowych. Jej autorami są Rosjanie a ściągnąć ją można z DSP lub bezpośrednio od autorów.


38. Jak dodać nową wartość klucza do rejestru?
rejestr, klucz

Należy skorzystać z obiektu TRegistry. Przykład:

procedure TMainForm.Button1Click(Sender: TObject);
var Reg:TRegistry;
begin
 Reg:=TRegistry.Create;
  try
  Reg.RootKey:=HKEY_LOCAL_MACHINE;
  Reg.OpenKey('Firma',True);
  Reg.WriteString('Sciezka','c:\Program Files\Firma');
  Reg.WriteInteger('Wersja',1);
  finally
  Reg.Free;
  end;
end;


39. Jak zamienić liczbę na "słownie złotych"?
słownie, kwota

Tu możesz ściągnąć przykładowy moduł.


40. Jak wyrażenie matematyczne zamienić na liczbę?
obliczanie, VAL$, funkcje, kalkulator

Tu możesz ściągnąć przykładowy moduł do Turbo Pascala. Aby użyć go z Delphi trzeba go trochę przerobić bo w Delphi zmienił się sposób obsługi obiektów.


41. Jak odczytać numer seryjny dysku lub dyskietki?
numer seryjny, dysk, dyskietka, CD-ROM

Dla dysku metoda jest nieznana :-((dokładniej - działa poniższy kod ale nie daje numeru dysku tylko numer seryjny partycji) Dla dyskietki powinna działać poniższa procedura:

var
 Buf:array[0..MAX_PATH] of Char;
 NotUsed,VolFlags:Integer;
 DriveChar:Char;
 Serial:PDWORD;
begin
 DriveChar := 'a';
 GetVolumeInformation(PChar(DriveChar + ':\'),Buf,sizeof(Buf),
  @Serial,NotUsed,VolFlags,nil,0);
end;

W zmiennej Serial jest numer dyskietki.

Źródło informacji: Artur Bajor


42. Czy w Delphi istnieje odpowiednik komend IN, OUT umożliwiających wysłanie pod określony adres urządzenia WE-WY określonej liczby?
Port, IN, OUT, porty

Zacytuję Artura Bajora:

"Nie istnieje.(...) Pisanie i odczyt portów można realizować przez wstawkę asm:

function PortIn(Port:word):Byte;
var Help:Byte;
begin
  asm
  mov DX   ,Port
  in   AL   ,DX
  mov Help ,AL
  end;
 PortIn:=Help;
end;

procedure PortOut(Port:word;Value:Byte);assembler;
asm
  mov DX,Port
  mov AL,Value
  out DX,AL
end;

"Podkreślam (co już było dyskutowane na tej liście), że Win95 skutecznie zabroni Ci dostępu do niektórych portów we/wy (np. HDD Controller) , co jest całkiem naturalne nawet w tak stabilnym systemie jak winda ;-) Jeżeli jednak są to porty np. Twojej karty, z powodzeniem będziesz mógł pisać i czytać we/wy, bo to samo robiłem w swojej."

Źródło informacji: Artur Bajor


43. Napisałem program korzystający z THTML i po przeniesieniu na inny komputer pojawia się błąd "Exception EOleSysError in module..." co się dzieje?
EOleSysError, OCX, HTML

Komponent THTML trzeba zarejestrować gdyż jest to kontrolka OCX. Można zrobić to używając programu regsrv32.exe lub ręcznie na początku programu. Większy problem to to, że ta kontrolka składa się z kilku plików i wszystkie trzeba przenieść do komputera docelowego do katalogu Windows\System lub katalogu z Twoim programem. Jakie pliki przenieść można sprawdzić w dokumentacji kontrolki lub sprawdzając w QuickView jakich bibliotek używa (poza standardowymi z Windows). Metodą czołgową można również kopiować po jednym pliku gdyż komunikaty o błędach podają czasem, którego pliku brakuje. Poniżej podaję procedury do rejestracji kontrolek OCX:

function CheckOCX:Boolean;
var Reg:TRegistry;
begin
 Reg:=TRegistry.Create;
  try
  Reg.RootKey:=HKEY_CLASSES_ROOT;
  // Poniżej jest UID kontrolki wyciągnięty z rejestru Windows
  Result:=Reg.OpenKey('CLSID\{B7FC3550-8CE7-11CF-9754-00AA00C00908}',False);
  if Result then Reg.CloseKey;  
  finally
  Reg.Free;
  end;
end;

procedure RegisterOCX;
var Lib:THandle;
    S:String;
    P:TProcedure;
begin
 OleInitialize(nil);
  try
  S:=ExtractFilePath(Application.ExeName)+'HTML.OCX';
  Lib:=LoadLibrary(PChar(S));
  if Lib<HINSTANCE_ERROR then
   raise Exception.CreateFmt('Cannot initialize library %s. '+
   'Internal Windows error %d',[S,Lib]);
  try
   P:=GetProcAddress(Lib,'DllRegisterServer');
   if not Assigned(P) then raise Exception.Create('Cannot find '+
    'procedure DllRegisterServer');
   P;
  finally
   FreeLibrary(Lib);
  end;
  finally
  OleUninitialize;
  end;
end;

procedure Uninstall;
var Lib:THandle;
    S:String;
    P:TProcedure;
begin
 S:=ExtractFilePath(Application.ExeName)+'HTML.OCX';
 Lib:=LoadLibrary(PChar(S));
  if Lib<HINSTANCE_ERROR then
  raise Exception.CreateFmt('Cannot initialize library %s.'+
      ' Internal Windows error %d',[S,Lib]);
  try
  P:=GetProcAddress(Lib,'DllUnregisterServer');
  if not Assigned(P) then raise Exception.Create('Cannot find procedure '+
   'DllUnregisterServer');
  P;
  finally
  FreeLibrary(Lib);
  end;
end;

Powinno się udostępnić opcję wymuszenia instalacji komponentu. Miałem problem gdy komponent był zarejestrowany ale nie było go na dysku.Wtedy pojawiał się błąd. Dzieje się tak najczęściej na komputerach, na których ktoś wcześniej instalował Delphi. Możliwe, że uninstall z Delphi nie usuwa wpisów w rejestrze a usuwa pliki. Aby powyższe działało można:

  • wrzucić wszystkie pliki kontrolki do katalogu z programem
  • na początku projektu sprawdzać czy kontrolka jest zarejestrowana
  • jeśli nie jest to ją zarejestrować
  • jeśli jest to zapamiętać, że jest
  • na końcu programu (przy wyjściu) odrejestrować kontrolkę ale tylko wtedy gdy nie była zarejestrowana przed wywołaniem programu
  • jeśli ktoś użyje parametru przy uruchomieniu programu to traktować ten przypadek jako brak instalacji kontrolki

Można też sprawdzanie instalacji zamienić na próbne utworzenie kontrolki. Jeśli Delphi rzuci wyjątek EOleSysError to znaczy, że trzeba ją zainstalować. IMHO OCX-y są trochę niewygodne. Wolę komponenty "100% pure Delphi". Przykładowe procedury były pisane do komponentu THTML. Aby rejestrować inne komponenty trzeba znać ich GUID i nazwę pliku, w którym się znajdują. Informacje te można wziąć z dokumentacji lub rejestru Windows.


44. Jak wywołać program 32-bitowy i poczekać na jego zakończenie?
wywołanie, 32-bit

Można skorzystać z poniższego przykładu:

procedure TForm1.Button1Click(Sender: TObject);
var SI:TStartupInfo;
    PI:TProcessInformation;
    S,Dir:String;
begin
 Dir:=ExtractFilePath(Application.ExeName);
 S:='winrar95.exe a '+Dir+'test.rar '+Dir+'*.*';

 FillChar(SI,sizeof(SI),0);
  with SI do
  begin
  dwFlags:=STARTF_USESHOWWINDOW;
  wShowWindow:=SW_SHOW;
  cb:=sizeof(TStartupInfo);
  end;

  if CreateProcess(nil,PChar(S),nil,nil,FALSE,NORMAL_PRIORITY_CLASS,nil,nil,SI,PI) then
  with PI do
  begin
   WaitForInputIdle(hProcess,1000);
   WaitForSingleObject(hProcess,10000);
   WaitForSingleObject(hThread,10000);
   CloseHandle(hProcess);
   CloseHandle(hThread);
  end;
end;

Oczywiście trzeba zmienić wartości przekazywane w zmiennej S ale idea pozostaje ta sama.

Źródło informacji: Marian Ficek


45. Jak wyświetlić plik JPG (instalacja jpeg.dcu z katalogu LIB nie pomaga)?
JPEG, JPG, LIB

Oto co proponuje Grzegorz Meus:

"Koniecznie wpisz w sekcji USES twojego modułu nazwę JPEG. Wtedy gdy wrzucisz na swój formularz np. komponent OpenPictureDialog (sekcja Dialogs z Delphi 3) będziesz miał dostęp do plików graficznych typu .JPG i od razu także ich podgląd w okienku tego dialogu. Aby samodzielnie taki obrazek wyświetlać dodatkowo wrzuć jakiś PaintBox na formularz, następnie zadeklaruj zmienną typu TPicture do przechowywania obrazu JPEG w pamięci

TForm1 = class(TForm)
  ...
  OpenPictureDialog1: TOpenPictureDialog;
  ...
private
  { Private declarations }
  FPicture : TPicture;
  ...
end;

Nie zapomnij o utworzeniu zmiennej FPicture zadeklarowanej wyżej (np. w TForm1.FormCreate):

FPicture := TPicture.Create;

oraz oczywiście jej zniszczeniu (np. w TForm1.FormDestroy)

FPicture.Free;

No i teraz w programie, zakładając że masz ścieżkę do pliku typu JPG w zmiennej FName : String, robisz coś takiego:

try
 FPicture.LoadFromFile(FName);
except
  on EInvalidGraphic do
begin
  MessageDlg('Invalid graphic file: '+FName,mtError,[mbOk],0);
  Exit;
  end;
end;
PaintBox1.Invalidate;

a w procedurce TForm1.PaintBox1Paint:

with PaintBox1 do
begin
 DrawRect:=Rect(0,0,Width,Height); //ramka wokół ...
//i
 Canvas.Draw(Left+(Right-Left-FPicture.Width)div 2,
  Top+(Bottom-Top-FPicture.Height)div 2,FPicture.Graphic);
//lub
 Canvas.StretchDraw(DrawRect,FPicture.Graphic);
end;

Powodzenia !"

Źródło informacji: Grzegorz Meus


46. Jak zrobić wygaszacz w Delphi 1.0?
wygaszacz

Oto co proponuje Tomasz Witek:

"...napisałem kiedyś taki wygaszacz. Bardzo prosto. Forma bez ramek, bez linijki, maximized, czuła na myszkę, zawierająca description w postaci:

{$D SCRNSAVE:Wygaszacz}

{Tylko jedna kopia programu może być uruchomiona}
if hPrevInst = 0 then  
begin
  if (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') 
  begin
  { Setup wygaszacza }
  end
  else
  begin
   { Wygaszacz }
  end;

Sam wygaszacz powinien uruchamiać się tylko po /C ale nie musi :)))
To by było wszystko."

Źródło informacji: Tomasz Witek


47. Jak dodać skrót do Desktopu lub Menu Start w Windows 95?
skrót, pasek Start, menu, desktop, ikona

Poniższe pochodzi z Delphi TI 3234:

Poniższy przykład pokazuje jak dodać skróty na desktop i menu Start w Windows 95 i Windows NT 4.0. Skrót zostanie dodany w jednym z tych miejsc (patrz kod). Położenie desktopu i menu Start pobierane jest z rejestru (z gałęzi HKEY_CURRENT_USER):

Software\MicroSoft\Windows\CurrentVersion\Explorer\Shell Folders

uses   ShlObj, ActiveX, ComObj, Registry;

procedure TForm1.Button1Click(Sender: TObject);
var   MyObject:IUnknown;
     MySLink:IShellLink; 
     MyPFile:IPersistFile;  
     FileName:String;
     Directory:String;  
     WFileName:WideString;  
     MyReg:TRegIniFile;
begin
 MyObject:=CreateComObject(CLSID_ShellLink);
 MySLink:=MyObject as IShellLink;  
 MyPFile:=MyObject as IPersistFile;
 FileName:='NOTEPAD.EXE';  
  with MySLink do  
  begin
  SetArguments('C:\AUTOEXEC.BAT');    
  SetPath(PChar(FileName));
  SetWorkingDirectory(PChar(ExtractFilePath(FileName)));  
  end;

 MyReg := TRegIniFile.Create('Software\MicroSoft\Windows\CurrentVersion\Explorer');

  // Poniższe dodaje skrót do desktopu
 Directory := MyReg.ReadString('Shell Folders','Desktop','');

  // A to do menu Start
  // Directory := MyReg.ReadString('Shell Folders','Start Menu','')+
  // '\Whoa!';// CreateDir(Directory);

 WFileName := Directory+'\FooBar.lnk';  
 MyPFile.Save(PWChar(WFileName),False);
 MyReg.Free;
end;

Źródło informacji: Delphi TI 3234.


48. Mam problemy z bazami danych w sieci, nie pojawiają się zmiany w bazach.
sieć, BDE, bazy danych, Paradox

Oto co na ten temat pisze Krzysztof Szyszka

"Ponieważ już parę razy odpowiadałem na pytania dotyczące różnych problemów związanych z pracą w sieci na bazach dBase i Paradox, a pytania ciągle się pojawiają, więc pokuszę się o krótkie zebranie zaleceń wynikających z moich własnych doświadczeń. (...)

  1. Ustawić w BDE Administratorze parametr Configuration/System/INIT/LOCAL SHARE na True na wszystkich stacjach, jeśli korzystamy z sieci typu peer-to-peer np. Microsoft Network.
  2. Dla baz Paradoxa ustawić Configuration/Drivers/Native/PARADOX/NET DIR lub Session.NetFileDir na ten sam fizycznie plik PDOXUSRS.NET. Niektórzy zalecają umieścić ten plik w katalogu baz danych.
  3. Ustawić Session.PrivateDir na roboczy katalog lokalnego dysku, chyba że mamy pewność, że bieżącym katalogiem przy starcie programu będzie katalog na lokalnym dysku.
  4. Zostawić domyślne ustawienie Table.CachedUpdates na False. Przy ustawieniu na True należy być świadomym, że wszystkie operacje na bazie wykonywane są jedynie w pamięci bez zapisu na dysk, a więc i bez wykonywania funkcji sieciowych.
  5. W zdarzeniu AfterPost każdej tabeli danych wymusić zapis buforów BDE na dysk poleceniem: Check(dbiSaveChanges(Table.Handle)). Dla tabel służących do przetwarzania większej porcji rekordów można wykonać to po zakończeniu aktualizacji, jeśli nie dokonujemy zmian w polach kluczowych dla pracy w sieci. Z punktu widzenia bezpieczeństwa danych lepiej wykonywać to po każdym Post, kosztem szybkości przetwarzania rekordów.
  6. Po wykonaniu każdego polecenia dostępnego użytkownikowi, które operuje na bazach danych, należy wykonać Table.Refresh dla uwidocznienie zmian dokonanych ostatnio w bazie przez innych użytkowników. Ma to szczególne znaczenie wtedy, gdy polecenie zakończy się komunikatem o błędzie np. 'Record locked by another user.', 'No current record.', 'Rekord/Key deleted.', itp.
  7. Dla tabel podłączonych do komponentów, które operują jednocześnie na więcej niż jednym rekordzie danych (np. DBGrid) wykonać dodatkowo DBGrid.Refresh, dla uwidocznienie zmian w pozostałych wierszach danych. W programach które prezentują jednocześnie kilka okien danych warto podłączyć Table.Refresh (i DBGrid.Refresh) pod zdarzenie OnActivate, żeby uczynienie okna aktywnym, wiązało się zawsze z odświeżeniem danych.
  8. Wykonując instalacje programu wymagającego BDE przy pomocy InstallShielda można dopisać własne Aliasy, ale nie można spowodować zmian w parametrach z zakładki Configuration (w każdym razie mnie się to nie udało), dlatego warto zmodyfikować ręcznie plik \InstallShield\redist\IDAPI32.CNF, dokonując np. zmiany domyślnego ustawienia dla LOCAL SHARE na True."

Źródło informacji: Krzysztof Szyszka


49. Jak przeszukiwać Delphi Help i Win32 Help jednocześnie?
help, pomoc, przeszukiwanie

Należy dopisać poniższy tekst do Delphi3.CNT:

:Index Win32=win32.hlp

wtedy w indeksie jest zarówno Delphi jak i WinAPI.

Źródło informacji: Krzysztof Świątkowski


50. Gdzie mogę znaleźć dodatkowe informacje o Delphi?
linki

Poniżej jest kilka linków zebranych i opracowanych przez Tomasza Kustrę i Radosława "Radio Erewan" Przybyła:

I. Komponenty

Delphi Super Page
Torry's Delphi Page
Delphi Free Stuff
Radek Delphi Page
Animated Menus98
Artem's Delphi Stuff
CoolForm ("okrągłe okienka")
RX Library (lepiej ściągać z mirrora w Polsce)
Latające toolbary Jordana Russela
Delphi Games Creator
Une Page Delphi 2+
Delphi Companion
Programers Heaven
JG's Home Page
American Freehold C++Builder/Delphi Freeware
Delphi3.com
InfoTrade Virtual Code Library
http://www.balticsolutions.com/
http://www.htmlreport.com/

II. Wiedza/Żródła

Archiwum pl.comp.lang.delphi
Delphi Companion
Delphi Sites
The Unoficial Newsletter of Delphi Users
Dr Bob`s Delphi Clinic
Programers Heaven
The Delphi EXchange
Delphi3.com
eMEDES Software : Delphi Source Code
Ask the Delphi Pro
FreeCode
Delphi Developer on line
Zagozda Software - Delphi w przykładach
WinAPI-FAQ

III. Borland/Inprise:

Strona główna
Delphi Developer's Jurnal
Delphi Developer Support
Common Delphi Question and Answers
BSC (Borlsnd Support Center) - polski reprezentant Inprise

IV. Inne źródła:

MSDN Technologies
Win32 Development
COM Technologies
SWAG (Sourceware Archive Group) (np.formaty większości plików graficznych)

V. Linki

Delphi Sites
Programers Heaven
ITM - Delphi Search Engine
Delphi Developers
Delphi3.com
Turbo Pascal Programers Page

Źródło informacji: Tomasz Kustra, Radosław "Radio Erewan" Przybył, Piotr Neil "Gawron" Gawronski

 

  Copyright 2001 at-online. Wszelkie prawa zastrzeżone.