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ę.
Bogdan Polak zwrócił mi uwagę, że w nowszych wersjach Delphi deklaracja nagłówka funkcji AddJob w pliku winspool.pas wygląda tak:
function AddJob (
hPrinter: THandle; Level: DWORD; pData: Pointer;
cbBuf: DWORD; var pcbNeeded: DWORD
): BOOL; stdcall;
parametr pcbNeeded jest typu DWORD, a w powyższym przykładzie użyto zmiennych Integer.
Krzysztof Świątkowski zwrócił uwagę na wadę powyższego rozwiązania. Nie zawsze działa ono poprawnie dla drukarek sieciowych. Oto jego propozycja:
function DirectPrint( const PrinterName : string;Data : PByte;
DataLen : Cardinal; out Error : string) : Boolean;
var
hPrinter : THandle;
DocInfo : TDocInfo1;
Job, DataWritten : Cardinal;
begin
Error := 'Unknown Error';
Result := False;
if not OpenPrinter(PChar(PrinterName), hPrinter, nil) then begin
Error := SysErrorMessage(GetLastError);
Exit;
end;
DocInfo.pDocName := 'My Document name';
DocInfo.pOutputFile := nil;
DocInfo.pDatatype := 'RAW';
Job := StartDocPrinter(hPrinter, 1, @DocInfo);
if Job=0 then begin
Error := SysErrorMessage(GetLastError);
ClosePrinter(hPrinter);
Exit;
end;
if not StartPagePrinter( hPrinter ) then begin
Error := SysErrorMessage(GetLastError);
EndDocPrinter(hPrinter);
ClosePrinter(hPrinter);
Exit;
end;
if not WritePrinter(hPrinter, Data, DataLen, DataWritten) then begin
Error := SysErrorMessage(GetLastError);
EndPagePrinter(hPrinter);
EndDocPrinter(hPrinter);
ClosePrinter(hPrinter);
Exit;
end;
if not EndPagePrinter(hPrinter) then begin
Error := SysErrorMessage(GetLastError);
EndDocPrinter(hPrinter);
ClosePrinter(hPrinter);
Exit;
end;
if not EndDocPrinter(hPrinter) then begin
Error := SysErrorMessage(GetLastError);
EndDocPrinter(hPrinter);
ClosePrinter(hPrinter);
Exit;
end;
ClosePrinter(hPrinter);
Result := DataWritten=DataLen;
end;
procedure PrintString(s : string);
var
h : Cardinal;
sPrinterName,sDriver,sPort:array[0..255]of Char; // sDriver i sPort nie będą
// wykorzystane
Err : string;
begin
Printer.GetPrinter(sPrinterName,sDriver,sPort,h);
if not DirectPrint(sPrinterName,PByte(PChar(s)),length(s),Err) then
ShowMessage(Err);
end;
Źródło informacji: Tomasz Pytlik, Krzysztof Świątkowski, Chris Monson, Bogdan Polak
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.
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:
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:
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(F);
// ... 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 dyskietki działa poniższy kod:
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.
Krzysztof Świątkowski podesłał rozwiązanie tego problemu działające również dla twardych dysków:
// (c) Alex Konshin mailto:alexk@mtgroup.ru 30 jul 2000
program HDDSerial;
// PURPOSE: Simple console application that extract first IDE disk serial
number.
uses
Windows,
SysUtils; // only for Win32Platform and SysErrorMessage
//-------------------------------------------------------------
function GetIdeDiskSerialNumber : String;
type
TSrbIoControl = packed record
HeaderLength : ULONG;
Signature : Array[0..7] of Char;
Timeout : ULONG;
ControlCode : ULONG;
ReturnCode : ULONG;
Length : ULONG;
end;
SRB_IO_CONTROL = TSrbIoControl;
PSrbIoControl = ^TSrbIoControl;
TIDERegs = packed record
bFeaturesReg : Byte; // Used for specifying SMART "commands".
bSectorCountReg : Byte; // IDE sector count register
bSectorNumberReg : Byte; // IDE sector number register
bCylLowReg : Byte; // IDE low order cylinder value
bCylHighReg : Byte; // IDE high order cylinder value
bDriveHeadReg : Byte; // IDE drive/head register
bCommandReg : Byte; // Actual IDE command.
bReserved : Byte; // reserved. Must be zero.
end;
IDEREGS = TIDERegs;
PIDERegs = ^TIDERegs;
TSendCmdInParams = packed record
cBufferSize : DWORD;
irDriveRegs : TIDERegs;
bDriveNumber : Byte;
bReserved : Array[0..2] of Byte;
dwReserved : Array[0..3] of DWORD;
bBuffer : Array[0..0] of Byte;
end;
SENDCMDINPARAMS = TSendCmdInParams;
PSendCmdInParams = ^TSendCmdInParams;
TIdSector = packed record
wGenConfig : Word;
wNumCyls : Word;
wReserved : Word;
wNumHeads : Word;
wBytesPerTrack : Word;
wBytesPerSector : Word;
wSectorsPerTrack : Word;
wVendorUnique : Array[0..2] of Word;
sSerialNumber : Array[0..19] of Char;
wBufferType : Word;
wBufferSize : Word;
wECCSize : Word;
sFirmwareRev : Array[0..7] of Char;
sModelNumber : Array[0..39] of Char;
wMoreVendorUnique : Word;
wDoubleWordIO : Word;
wCapabilities : Word;
wReserved1 : Word;
wPIOTiming : Word;
wDMATiming : Word;
wBS : Word;
wNumCurrentCyls : Word;
wNumCurrentHeads : Word;
wNumCurrentSectorsPerTrack : Word;
ulCurrentSectorCapacity : ULONG;
wMultSectorStuff : Word;
ulTotalAddressableSectors : ULONG;
wSingleWordDMA : Word;
wMultiWordDMA : Word;
bReserved : Array[0..127] of Byte;
end;
PIdSector = ^TIdSector;
const
IDE_ID_FUNCTION = $EC;
IDENTIFY_BUFFER_SIZE = 512;
DFP_RECEIVE_DRIVE_DATA = $0007c088;
IOCTL_SCSI_MINIPORT = $0004d008;
IOCTL_SCSI_MINIPORT_IDENTIFY = $001b0501;
DataSize = sizeof(TSendCmdInParams)-1+IDENTIFY_BUFFER_SIZE;
BufferSize = SizeOf(SRB_IO_CONTROL)+DataSize;
W9xBufferSize = IDENTIFY_BUFFER_SIZE+16;
var
hDevice : THandle;
cbBytesReturned : DWORD;
pInData : PSendCmdInParams;
pOutData : Pointer; // PSendCmdOutParams
Buffer : Array[0..BufferSize-1] of Byte;
srbControl : TSrbIoControl absolute Buffer;
procedure ChangeByteOrder( var Data; Size : Integer );
var ptr : PChar;
i : Integer;
c : Char;
begin
ptr := @Data;
for i := 0 to (Size shr 1)-1 do
begin
c := ptr^;
ptr^ := (ptr+1)^;
(ptr+1)^ := c;
Inc(ptr,2);
end;
end;
begin
Result := '';
FillChar(Buffer,BufferSize,#0);
if Win32Platform=VER_PLATFORM_WIN32_NT then
begin // Windows NT, Windows 2000
// Get SCSI port handle
hDevice := CreateFile( '\\.\Scsi0:',
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, 0, 0 );
if hDevice=INVALID_HANDLE_VALUE then Exit;
try
srbControl.HeaderLength := SizeOf(SRB_IO_CONTROL);
System.Move('SCSIDISK',srbControl.Signature,8);
srbControl.Timeout := 2;
srbControl.Length := DataSize;
srbControl.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY;
pInData := PSendCmdInParams(PChar(@Buffer)
+SizeOf(SRB_IO_CONTROL));
pOutData := pInData;
with pInData^ do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
bDriveNumber := 0;
with irDriveRegs do
begin
bFeaturesReg := 0;
bSectorCountReg := 1;
bSectorNumberReg := 1;
bCylLowReg := 0;
bCylHighReg := 0;
bDriveHeadReg := $A0;
bCommandReg := IDE_ID_FUNCTION;
end;
end;
if not DeviceIoControl( hDevice, IOCTL_SCSI_MINIPORT,
@Buffer, BufferSize, @Buffer, BufferSize,
cbBytesReturned, nil ) then Exit;
finally
CloseHandle(hDevice);
end;
end
else
begin // Windows 95 OSR2, Windows 98
hDevice := CreateFile( '\\.\SMARTVSD', 0, 0, nil,
CREATE_NEW, 0, 0 );
if hDevice=INVALID_HANDLE_VALUE then Exit;
try
pInData := PSendCmdInParams(@Buffer);
pOutData := @pInData^.bBuffer;
with pInData^ do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
bDriveNumber := 0;
with irDriveRegs do
begin
bFeaturesReg := 0;
bSectorCountReg := 1;
bSectorNumberReg := 1;
bCylLowReg := 0;
bCylHighReg := 0;
bDriveHeadReg := $A0;
bCommandReg := IDE_ID_FUNCTION;
end;
end;
if not DeviceIoControl( hDevice, DFP_RECEIVE_DRIVE_DATA,
pInData, SizeOf(TSendCmdInParams)-1, pOutData,
W9xBufferSize, cbBytesReturned, nil ) then Exit;
finally
CloseHandle(hDevice);
end;
end;
with PIdSector(PChar(pOutData)+16)^ do
begin
ChangeByteOrder(sSerialNumber,SizeOf(sSerialNumber));
SetString(Result,sSerialNumber,SizeOf(sSerialNumber));
end;
end;
//=============================================================
var s : String;
rc : DWORD;
begin
s := GetIdeDiskSerialNumber;
if s='' then
begin
rc := GetLastError;
if rc=0 then MessageBox(0,'IDE drive is not support SMART
feature','',0)
else MessageBox(0,PChar(SysErrorMessage(rc)),'',0);
end
else MessageBox(0,PChar('Disk serial number: '''+ s+''''),'',0);
end.
Źródło informacji: Artur Bajor, Krzysztof Świątkowski, Alex Konshin.
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:
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ń. (...)
Ź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
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
About Delphi Programming
Źródło informacji: Tomasz Kustra, Radosław "Radio Erewan" Przybył, Piotr Neil "Gawron" Gawronski, Paweł Świerzko.
51. Przy próbie dodania
nowego rekordu do tabeli Paradoxa pojawia się błąd "Index
is read-only". Przy ustawianiu nazwy indeksu wyskakuje
wyjątek "Index is out of date".
Paradox, indeksy
Dla tabel Paradox'a indeksy typu secondary nie mogą
być modyfikowane bez istnienia indeksu primary. Rozwiązać to
można na 2 sposoby:
- przez dodanie primary index w Database Desktop,
- dodawanie danych do tabeli bez ustawionego indeksu, a
następnie przez ponowne
utworzenie indeksów w Database Desktop ręcznie
lub programowo przy użyciu poniższego kodu:
try Table1.Active:=False; Table1.Exclusive:=True; Table1.Active:=True; Check(DbiRegenIndexes(Table1.Handle)); finally Table1.Active:=False; Table1.Exclusive:=False; Table1.Active:=True; end;
Table1 nie może posiadać ustawionego indeksu w IndexFieldNames ani IndexName.
Źródło informacji: Krzysztof Borys.
52. Delphi nie chce
działać z kartą S3 Virge. Co robić?
Virge, S3
Należy zmniejszyć w ustawieniach karty akcelerację sprzętową na minimum lub nie instalować komponentów internetowych. Problem ten podobno występuje tylko na kartach czteromegowych.
Inne rozwiązanie opisał Konrad:
"Chcę dorzucić swoje 3 grosze do pytania nr.52.
Ze zmianą akceleracji nie sprawdzałem (nie lubię takich kompromisów), ale rozwiązałem ten problem w trochę inny sposób, pozwalający na utrzymanie pełnej akceleracji (ach te gierki...:)): Po reinstalacji windy i sformatowaniu twardziela (i tak trzeba to robić raz na kwartał), zainstalowałem oryginalne sterowniki do Virge i starego DirectX'a (dołączonego do Virge). Delphi (ver.3) jeszcze chodzi. Ale instalacja DirectX 6 albo 5 (z innymi nie sprawdzałem) rozwala Delphi całkowicie...
Można to ominąć podczas procesu instalacji nowej wersji DirectX, nie zgadzając się na próby zmiany sterowników karty graficznej. Wtedy i DirectX (gierki :)) i Delphi działają bez zarzutu. Ominięcie instalacji nowych sterowników nie wpływa na wydajność DirectX (przynajmniej w widoczny sposób)."
Artur Solich podał też inny sposób (sprawdzony na Delphi3 i Win98): należy w pliku system.ini w sekcji [Display] (jeżeli jej nie ma to trzeba ją utworzyć) dopisać DeviceBitmap=0. Po restarcie systemu Delphi powinno działać poprawnie.
Źródło informacji: Konrad Z, Artur Solich.
53. Jak usunąć przycisk programu z
paska zadań?
pasek zadań, ikona, przycisk
Należy:
W module projektu:
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ń.
Źródło informacji: Darek Brzeziński
54. Jak dodać swój program
obok ikonki zegara w Windows 95?
pasek zadań, tray, szuflada, ikona, zegar
Poniżej znajduje się przetłumaczony tekst Brendana Delumpa (tu podziękowania dla Radka "Radio Erewan" Przybyła za wyszukanie go w odmętach Sieci):
"Zabawne jak niektóre rzeczy w Windows, które wyglądają na proste w implementacji okazują się być bardzo stresujące. I nie jest tak dlatego, że są to trudne rzeczy - często po porostu informacje potrzebne do ukończenia programu kryją się za wieloma odnośnikami WWW, stronami helpa lub nie ma ich tam gdzie spodziewalibyśmy się je znaleźć (częste luki w dokumentacji Delphi). Tak również jest z tworzeniem aplikacji umieszczającej ikonę w obszarze systemowego traya (szuflady). Implementacja tego efektu jest trywialna ale dotarcie do potrzebnych informacji nie jest proste.
Są dwie rzeczy, które trzeba wziąć pod uwagę tworząc aplikację do traya. Pierwsza to "ukrycie" aplikacji przed Windows. Mimo, że aplikacje takie wyglądają i zachowują się jak zwykłe aplikacje Windows, nie można się na nie przełączyć przy użyciu Alt-Tab ani nie mają swojego przycisku na pasku zadań. Tym zajmiemy się najpierw.
Każde okno posiadające styl WS_EX_TOOLWINDOW ani nie ma przycisku na pasku zadań ani nie można się na nie przełączyć. Z początku może wydawać się właściwym ustawienie tego stylu przy użyciu CreateParams. Niestety nie zadziała to dla formy. Tu mała dygresja. Główna forma aplikacji nie jest oknem (w terminologii Windows) aplikacji. Obiekt aplikacji ma swoje własne okno - nie można go zobaczyć ale ono "tam" jest. To jest właśnie to okno, do którego należy przypisać styl WS_EX_TOOLWINDOW. Gdzie więc należy wstawić kod? Oczywiście w źródle projektu. Po wybraniu View|Project Source należy skopiować poniższy kod:
program Project1;
uses Forms,
Unit1 in 'Unit1.pas' {Form1},
Windows; //To jest wymagane aby znana była stała WS_EX_TOOLWINDOW i pozostałe
{$R *.RES}
//Deklaracja zmiennej do przyjęcia informacji o stylu okna
var ExtendedStyle : Integer;
begin
Application.Initialize;
//Pobranie informacji o oknie aplikacji przy użyciu GetWindowLong
ExtendedStyle:=GetWindowLong(Application.Handle, GWL_EXSTYLE);
//Teraz ustawiamy styl rozszerzony przy użyciu operacji na bitach
//Przekształca to okno z okna-aplikacji do okna-narzędzia
SetWindowLong(Application.Handle,GWL_EXSTYLE,
ExtendedStyle or WS_EX_TOOLWINDOW and not WS_EX_APPWINDOW);
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
A teraz aby utworzyć właściwy efekt aplikacji w trayu będziemy potrzebowali przede wszystkim głównej formy aplikacji. Połóż na formie komponent TPopupMenu. Będzie to główny interfejs do naszej aplikacji. Popatrz na poniższy kod:
{ Poniższe umieszcza aplikację w trayu.
Jest to główna forma aplikacji. Posiada ona menu popup używane do
wyświetlenia formy i zamknięcia aplikacji.
Używając modułu ShellApi w prosty sposób pokażemy ikonę aplikacji w trayu
i spowodujemy aby reagowała na kliknięcia myszą }
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ShellAPI, ExtCtrls, Menus;
type
TForm1 = class(TForm)
PopupMenu1: TPopupMenu;
ShowMainForm1: TMenuItem;
N1: TMenuItem;
ExitApplication1: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure ShowMainForm1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure ExitApplication1Click(Sender: TObject);
private
procedure WndProc(var Msg : TMessage); override;
public
IconNotifyData : TNotifyIconData;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
// Zostawiamy tylko przycisk zamykający okno
BorderIcons := [biSystemMenu];
// Teraz wypełniamy rekord IconNotifyData tak aby przyjmował
// komunikaty wysyłane do aplikacji i pokazywał "dymki" podpowiedzi.
with IconNotifyData do begin
hIcon:=Application.Icon.Handle;
uCallbackMessage:=WM_USER+1;
cbSize:=SizeOf(IconNotifyData);
Wnd:=Handle;
uID:=100;
uFlags:=NIF_MESSAGE+NIF_ICON+NIF_TIP;
end;
// Kopiujemy tytuł aplikacji jako "dymek"
StrPCopy(IconNotifyData.szTip, Application.Title);
// Dodajemy ikonę do traya
Shell_NotifyIcon(NIM_ADD,@IconNotifyData);
end;
procedure TForm1.WndProc(var Msg : TMessage);
var
p : TPoint;
begin
if (Msg.Msg=WM_USER+1)and(Msg.lParam=WM_RBUTTONDOWN) then
begin
GetCursorPos(p);
PopupMenu1.Popup(p.x, p.y);
end;
inherited; end; // To jedna z procedur obsługi elementów menu procedure TForm1.ShowMainForm1Click(Sender: TObject); begin Form1.Show; end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caNone; Form1.Hide; end; procedure TForm1.ExitApplication1Click(Sender: TObject); begin Shell_NotifyIcon(NIM_DELETE, @IconNotifyData); Application.ProcessMessages; Application.Terminate; end; end.
Jak widać nie ma wiele do zrobienia. Ale ważne jest aby rozumieć co zrobiliśmy w metodzie Create i jakie znaczenie ma rekord IconNotifyData. Jest to rekord zdefiniowany w module ShellAPI, który przechowuje informację o ikonie w trayu. Zauważ flagi, których użyliśmy: NIF_MESSAGE + NIF_ICON + NIF_TIP. Oznaczają one kolejno: obsługę komunikatów dla aplikacji, pokazywanie ikony aplikacji i pokazywanie "dymku" z podpowiedzią.
Następna sprawa to nadpisanie procedury WndProc (skrót od WindowProcedure). Dostaje ona wszystkie komunikaty przesyłane do okna i zachowuje się jak centralna rozdzielnia komunikatów. Można przejąć obsługę komunikatu pisząc własną jego obsługę i wywołując odziedziczoną procedurę. Przy obsłudze komunikatu sprawdzamy czy jest to nasz własny (wm_User+1) zdefiniowany w zmiennej IconNotifyData oraz czy nastąpiło kliknięcie prawym przyciskiem myszy. Pozostałe komunikaty przesyłamy bez zmian.[...]"
Źródło informacji; Brendan Delupma, Radosław "Radio Erewan" Przybył
55. Jak odegrać dźwięk przechowywany w
zasobach?
dzwięk, zasoby, WAV, WAVE
Należy skorzystać z poniższego kodu:
var FindHandle, ResHandle: THandle;
ResPtr: Pointer;
begin
FindHandle:=FindResource(HInstance, 'TUTAJ NAZWA ZASOBU', 'WAVE');
if FindHandle<>0 then
begin
ResHandle:=LoadResource(HInstance, FindHandle);
if ResHandle<>0 then
begin
ResPtr:=LockResource(ResHandle);
if ResPtr<>Nil then
SndPlaySound(PChar(ResPtr), snd_ASync or snd_Memory);
UnlockResource(ResHandle);
end;
FreeResource(FindHandle);
end;
end;
Krzysztof Świątkowski zwrócił uwagę na prostszą metodę możliwą jednak do wykorzystania tylko w Win32. Jeśli mamy zasób typu WAVE wystarczy tylko wykonać:
PlaySound('MUZYKA', hInstance, SND_RESOURCE or SND_ASYNC);
hInstance jest uchwytem do instancji aplikacji lub biblioteki. W ten sposób można np odtworzyć WAVE zapisany w jakimś dll'u.
Źródło informacji: Stefan Westner, Krzysztof Świątkowski
56.Jak schować lub wyłączyć przycisk
start w Win95?
przycisk Start, pasek zadań
Należy wykonać poniższy kod:
procedure TForm1.Button1Click(Sender: TObject);
var
Rgn : hRgn;
begin
//Ukrycie przycisku Start
Rgn := CreateRectRgn(0, 0, 0, 0);
SetWindowRgn(FindWindowEx(FindWindow('Shell_TrayWnd', nil),0,'Button',nil),Rgn,true);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
//Przywrócenie przycisku
SetWindowRgn(FindWindowEx(FindWindow('Shell_TrayWnd', nil),0,'Button',nil),0,true);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
//Wyszarzenie przycisku Start
EnableWindow(FindWindowEx(FindWindow('Shell_TrayWnd', nil),0,'Button',nil),false);
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
//Ponowne włączenie przycisku
EnableWindow(FindWindowEx(FindWindow('Shell_TrayWnd', nil),0,'Button',nil),true);
end
57. Czy jest coś dokładniejszego niż
TTimer?
TTimer, zegar, przerwanie
Należy użyć procedury QueryPerformanceFrequency. Oto przykład:
procedure TForm1.Button1Click(Sender: TObject); var li : TLARGEINTEGER; begin QueryPerformanceFrequency(li); ShowMessage(FloatToStr(Comp(li))); QueryPerformanceCounter(li); ShowMessage(FloatToStr(Comp(li))); QueryPerformanceCounter(li); ShowMessage(FloatToStr(Comp(li))); end;
Jak widać TLargeInteger jest kompatybilny wewnętrznie z Comp i może być na niego rzutowany.
58. Jak wykryć obecność karty
dźwiękowej?
karta dźwiękowa
Należy użyć funkcji WaveOutGetNumDevs:
uses MMSystem;
procedure TForm1.Button1Click(Sender: TObject);
begin
if WaveOutGetNumDevs > 0 then
ShowMessage('Karta dźwiękowa jest zainstalowana')
else
ShowMessage('Brak karty dźwiękowej')
end;
59. Co zrobić aby w Delphi 4
używać polskich liter?
polskie litery
Należy dodać do rejestru klucz:
HKEY_CURRENT_USER\Software\Borland\Delphi\4.0\Editor\Options\NoCtrlAltKeys
z wartością "1".
60. Jak włączyć w Delphi 3
okno debugera?
debuger
Należy dodać do rejestru klucz:
HKEY_CURRENT_USER\Software\Borland\Delphi\3.0\Debugging\EnableCPU
z wartością "1".
61. Jak utworzyć lub odtworzyć indeksy
dla istniejących tabel?
indeks, Paradox
Należy skorzystać z metody AddIndex:
Table1.AddIndex('NewIndex','CustNo;CustName',[ixUnique,ixCaseInsensitive]);
zaś aby odtworzyć indeks:
Check(dbiRegenIndexes(Table1.Handle));
Użycie dbiRegenIndexes może wymagać dodania modułu BDE do klauzuli uses, zaś tabele powinny być otwarte w trybie wyłączności.
Źródło informacji: Tomasz Hejman, Krzysztof Borys
62. Mam Delphi 2.0 i NT 4.0. Nie widzę
polskich znaków, co robić?
polskie znaki, NT
Należy zmienić w rejestrze klucz:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\NLS\CodePage\1252
z "c_1252.nls" na "c_1250.nls". Problem ten pojawia się tylko gdy zainstalujemy Service Pack 3
Źródło informacji: Marian Ficek
63. Mimo że usuwam rekordy z bazy to jej
rozmiar nie zmniejsza się, co robić?
rozmiar bazy, rekordy, BDE, pakowanie, kasowanie
Paradox (i inne bazy) nie usuwają fizycznie rekordu z bazy a tylko zaznaczają go jako usuniętego. Przyspiesza to operacje na rekordach. Aby odzyskać zajmowane przez te rekordy miejsce należy użyć procedur pakujących tabelę.
W klauzuli uses dopisujemy:
uses DbiProcs,DbiTypes,DbiErrs;
A potem:
function TForm1.PackTable():DbiResult;
var
Props: CURProps;
hDb: hDBIDb;
TableDesc: CRTblDesc;
begin
if Table1.Active then
Table1.Active := False;
try
Table1.Exclusive:=True;
Table1.Active:=True;
except
ShowMessage('Błąd: Nie mogę otworzyć tabeli ' + Table1.TableName + ' na wyłączność;'
+ #13#10 + 'prawdopodobnie jest uszkodzona, lub tabela jest w używana');
Result:=66;
Exit;
end;
// Pobieramy właściwości tabeli aby sprawdzić jej typ...
Check(DbiGetCursorProps(Table1.Handle, Props));
// Jeśli to tabela Paradoxa, wywołujemy DbiDoRestructure...
if Props.szTableType = szPARADOX then
begin
// Zerujemy rekord...
FillChar(TableDesc, sizeof(TableDesc), 0);
// Pobieramy uchwyt tabeli z uchwytu kursora...
Check(DbiGetObjFromObj(hDBIObj(Table1.Handle), objDATABASE, hDBIObj(hDb)));
// Przepisujemy nazwę tabeli...
StrPCopy(TableDesc.szTblName, Table1.TableName);
// i jej typ...
StrPCopy(TableDesc.szTblType, Props.szTableType);
// Ustawiamy opcję Pack na TRUE...
TableDesc.bPack := True;
// Zamykamy tabelę (dBase oczekuje otwartej tabeli)
Table1.Close;
// Wywołujemy DbiDoRestructure...
Result:=DbiDoRestructure(hDb, 1, @TableDesc, nil, nil, nil, FALSE);
Check(Result);
end
else
begin
// Jeśli to tabela dBASE to po prostu wywołujemy DbiPackTable...
if Props.szTableType = szDBASE then
Result:=DbiPackTable(Table1.DBHandle, Table1.Handle, nil, szDBASE, TRUE)
else
// To wszystko działa tylko dla tabel Paradoksa i dBASE...
raise EDatabaseError.Create('Tabela musi być typu Paradox lub dBASE ' +
'aby można ją było pakować');
end;
Table1.Active:=False;
Table1.Exclusive:=False;
end;
Pozostaje jeszcze wywołać tę procedurę:
if Form1.PackTable = DbiERR_NONE then
begin
MessageDlg('Pakowanie tabeli zakończone sukcesem.',mtInformation,[mbOK],0);
end
else
MessageDlg('Pakowanie tabeli nie powiodło się.',mtWarning,[mbOK],0);
Źródło informacji: Piotr Murawski
64. Jak kompilować warunkowo dla różnych
wersji Delphi?
kompilacja warunkowa
Każda wersja ma inny symbol (ang. conditional define).
| VER80 | Delphi 1.x |
| VER90 | Delphi 2.x |
| VER93 | CBuilder 1.0 |
| VER100 | Delphi 3.x |
| VER120 | Delphi 4.x |
Można tego użyć w następujący sposób:
{$IFDEF VER90}
uses system, windows, oleaut;
{$ENDIF}
{$IFDEF VER100}
uses system,windows, comobj;
{$ENDIF}
Źródło informacji: Grzegorz Skoczylas.
65. Jak odczytać lokalny adres IP?
adres IP, internet
Na przykład przy użyciu poniższego kodu:
uses Winsock;
procedure TForm1.FormCreate(Sender:TObject);
var wVersionRequested:WORD;
wsaData:TWSAData;
begin
//Ładujemy bibliotekę Winsock
wVersionRequested := MAKEWORD(1, 1);
WSAStartup(wVersionRequested, wsaData);
end;
procedure TForm1.Button1Click(Sender:TObject);
var p:PHostEnt;
s:array[0..128] of char;
p2:pchar;
begin
//Pobieramy nazwę komputera
GetHostName(@s, 128);
p := GetHostByName(@s);
Memo1.Lines.Add(p^.h_Name);
//Pobieramy jego adres IP
p2 := iNet_ntoa(PInAddr(p^.h_addr_list^)^);
Memo1.Lines.Add(p2);
end;
procedure TForm1.FormDestroy(Sender:TObject);
begin
//Zwalniamy Winsock
WSACleanup;
end;
Na formie powinno znajdować się memo o nazwie Memo1. W podany sposób można też łatwo sprawdzić czy jesteśmy podłączeni do sieci. Gdy nie ma połączenia z Internetem to adres ma postać 0.0.0.0
66. Jak zablokować przełączanie zadań przy pomocy
Alt-Tab lub Ctrl-Tab?
Alt, Tab, zadania, przełączanie
Należy oszukać Windows tak aby myślało, że nasza aplikacja jest wygaszaczem ekranu. Poniższy sposób działa tylko w Windows 95, nie działa w NT i nie ma gwarancji, żeby działał w przyszłych wersjach Windows.
var OldValue:LongBool; begin //Włącza blokadę SystemParametersInfo(97,Word(True),@OldValue,0); //Wyłącza blokadę SystemParametersInfo(97,Word(False),@OldValue,0); end;
67. Jak odczytać nazwę zalogowanego użytkownika?
nazwa użytkownika, sieć, użytkownik, logowanie
Trzeba skorzystać z funkcji Windows API o nazwie GetUserName:
procedure TForm1.Button2Click(Sender: TObject);
var buffer:string;
buffSize:DWORD;
begin
buffSize:=128;
SetLength(buffer,BuffSize);
GetUserName(PChar(buffer), buffSize);
ShowMessage(buffer);
end;
68. Jak dodać nazwę dokumentu do listy
ostatnio otwartych dokumentów w menu Start?
lista dokumentów, recent
Trzeba skorzystać z funkcji Windows API o nazwie SHAddToRecentDocs:
uses ShlOBJ; procedure TForm1.Button1Click(Sender: TObject); var s:string; begin s:='C:\DownLoad\ntkfaq.html'; SHAddToRecentDocs(SHARD_PATH,PChar(s)); end;
69. Jak programowo włączyć lub wyłączyć monitor?
monitor, VESA
Należy wysłać komunikat wm_SysCommand z parametrem wParam ustawionym na SC_MonitorPower zaś lParam ustawionym na:
Wyłączenie monitora:
SendMessage(Application.Handle,wm_SysCommand,SC_MonitorPower,0);
Włączenie monitora:
SendMessage(Application.Handle,wm_SysCommand,SC_MonitorPower,-1);
Uśpienie maszyny:
SendMessage(Application.Handle,wm_SysCommand,SC_SCREENSAVE,-1);
Uwaga: Parametr SC_MonitorPower jest specyficzny dla Windows 95.
Uwaga: Comboy informuje, że pod WinME SC_MonitorPower też działa.
Źródło informacji: Michał Młynarczyk, Comboy.
70. Jak zmodyfikować menu systemowe formy?
menu systemowe, forma
Tak. Słyży do tego grupa funkcji: GetSystemMenu, AppendMenu, InsertMenu, ModifyMenu. Przy tworzeniu formy dodajemy do menu systemowego własny element. Jako jego identyfikator wybrałem liczbę zero zapisaną w stałej idTest.
const idTest=0; procedure TMainForm.FormCreate(Sender: TObject); var hMenu:THandle; begin hMenu:=GetSystemMenu(Handle,False); AppendMenu(hMenu,mf_String,idTest,'&Test'); end;
Tak zdefiniowaną pozycję menu trzeba jeszcze samodzielnie obsłużyć. Robi to procedura wywoływana gdy aplikacja dostanie komunikat wm_SysCommand:
procedure TMainForm.WMSysCommand(var Message:TWMSysCommand);
begin
if Message.CmdType=idTest then
begin
Message.Result:=0; //Zaznaczamy, że obsłużyliśmy komunikat
ShowMessage('Komunikat testowy');
end
else inherited;
end;
71. Jak odczytać wielkość wolnego obszaru na ekranie
biorąc pod uwagę wysokość (szerokość) paska zadań?
pasek zadań, desktop, ekran, rozmiar
Należy skorzystać z funkcji SystemParametersInfo Windows API. Wywołana z parametrem SPI_GETWORKAREA poda rozmiar wolnego miejsca na ekranie:
procedure CenterForm(AForm:TForm); var R:TRect; begin SystemParametersInfo(SPI_GETWORKAREA,0,@R,0); with AForm do begin Left := R.Left + (R.Right - R.Left - Width) div 2; Top := R.Top + (R.Bottom - T.Top - Height) div 2; end; end;
Pozostaje już tylko wywołać ją dla odpowiedniej formy:
CenterForm(Form1)
Źródło informacji: Krzysztof Szyszka.
72. Jak otworzyć bazę Access'a (*.MDB) poprzez
sterownik MSACCESS?
Access, BDE, sterownik
Otwieranie baz Accessa jest możliwe tylko dla Delphi 3.0 i powyżej, w wersji Professional lub wyższej. Oprócz zainstalowanego BDE (najlepiej w wersji >= 4.51), na komputerze musi być zainstalowane DAO (jest w pakiecie Office 97, a wersję do redystrybucji należy mieć z któregoś z produktów Microsoft'u)
Po instalacji BDE należy uruchomić BDE Administratora i w zakładce Configuration znaleźć pozycję Configuration/Drivers/Native/MSACCESS. Tam trzeba przestawić wartość pola DLL32 z IDDAO32.DLL na IDDA3532.DLL.
Aby skorzystać z bazy w formacie MDB wystarczy teraz utworzyć alias w DatabaseDesktop lub utworzyć komponent TDatabase, a następnie ustawić wartości:
Database1.DriverName:='MSACCESS';
Database1.DatabaseName:='JakasNazwaDB';
Database1.Params.Clear;
Database1.Params.Add('DATABASE NAME=C:\Ścieżka_do\Pliku_bazy.mdb');
Database1.Connected:=True;
Dla każdego użytego TQuery lub TTable należy ustawić wartość DatabaseName taką samą jak w Database1.DatabaseName.
Źródło informacji: Krzysztof Borys.
73. Jak programowo zmodyfikować ustawienia BDE sterownika MSACCESS i innych
wartości BDE?
BDE, setup
Przy instalacji BDE domyślną wartością ustawień starownika MSACCESS jest sterownik IDDAO32.DLL (DAO 3.0), który wykorzystywany był w starej wersji Office, natomiast mając zainstalowany Office97 musimy zmienić ustawienia na IDDA3532.DLL (patrz poprzednie pytanie). Aby zrobić to programowo można skorzystać z poniższej procedury:
uses BDE;
procedure SetOffice97;
var Cursor:HDBICur;
ConfigDesc:CFGDesc;
begin
DBTables.Session.Active := true;
try
Check(DbiOpenCfgInfoList(nil,dbiREADWRITE,cfgPERSISTENT,
PChar('\DRIVERS\MSACCESS\INIT'),Cursor));
try
while DbiGetNextRecord(Cursor,dbiNOLOCK,@ConfigDesc,nil)=0 do
with ConfigDesc do
begin
OemToChar(szValue,szValue);
if (AnsiCompareText(szNodeName,'DLL32')=0) and
(AnsiCompareText(szValue,'IDDAO32.DLL')=0) then
begin
StrPCopy(szValue,'IDDA3532.DLL');
CharToOem(szValue,szValue);
Check(DbiModifyRecord(Cursor,@ConfigDesc,true));
Break;
end;
end;
finally
DbiCloseCursor(Cursor);
end;
finally
DBTables.Session.Active := true;
end;
end;
W przypadku używania w programie komponentów TSession należy zamienić odwołanie z DBTables.Session na nazwę komponentu umieszczonego na formularzu
Źródło informacji: Krzysztof Borys.
74. Nie mogę stworzyć okienka MDIChild z ustawionym parametrem poDesigned.
MDIChild, MDI
Sprawdzane w Delphi 2.0.
Jeśli chcesz stworzyć okienko MDIChild o ściśle zadanej pozycji i ściśle zadanych wymiarach, to z czegoś będziesz musiał zrezygnować. Delphi zawiera błąd i wartość "poDesigned" wstawiona do pola "Position" formularza daje tyle samo co "poDefault". Użyj "poDefaultPosOnly" i ustaw pozycję ręcznie lub "poDefaultSizeOnly" i ręcznie ustaw rozmiar.
Źródło informacji: Maciej "MACiAS" Pilichowski.
75. Jak skopiować/skasować/przenieść cały katalog?
katalog, kopiowanie
Najwygodniej jest skorzystać z funkcji SHFileOperation znajdującej się w module ShellAPI:
uses ShellAPI;
procedure TForm1.Button1Click(Sender: TObject);
var FOS:TSHFileOpStructA;
begin
with FOS do
begin
Wnd:=Handle;
wFunc:=FO_COPY;
pFrom:='c:\tip\źródło\*.*';
pTo:='c:\tip\cel\';
fFlags:=FOF_ALLOWUNDO or FOF_NOCONFIRMMKDIR;
lpszProgressTitle:='Kopiowanie...';
fAnyOperationsAborted:=False;
end;
if SHFileOperation(FOS)<>0 then
ShowMessage('Wystąpił błąd podczas kopiowania')
else
if FOS.fAnyOperationsAborted then
ShowMessage('Kopiowanie zostało przerwane');
end;
76. Jak programowo zmienić rozdzielczość ekranu?
rozdzielczość ekranu, tryb graficzny
Wystarczy skorzystać z funkcji ChangeDisplaySettings:
procedure TForm1.Button2Click(Sender: TObject);
var Mode:TDeviceMode;
S:String;
begin
with Mode do
begin
dmSize:=SizeOf(Mode);
dmBitsPerPel:=16;
dmPelsWidth:=800;
dmPelsHeight:=600;
dmFields:=DM_PELSWIDTH+DM_PELSHEIGHT;
end;
case ChangeDisplaySettings(Mode,0)of
DISP_CHANGE_SUCCESSFUL:S:='Operacja przebiegła pomyślnie';
DISP_CHANGE_RESTART:S:='Aby zmiany odniosły skutek należy zrestartować systi';
DISP_CHANGE_BADFLAGS:S:='Błędne pole dmFields';
DISP_CHANGE_FAILED:S:='Błąd podczas ustawiania trybu';
DISP_CHANGE_BADMODE:S:='Ten tryb nie jest obsługiwany';
DISP_CHANGE_NOTUPDATED:S:='Rejestr nie został zaktualizowany';
else S:='Nieznany kod wyniku';
end;
ShowMessage(S);
end;
77. Jak programowo podłączyć dysk sieciowy?
sieć, dysk, podłączanie
Należy skorzystać z funkcji WNetAddConnection2:
procedure TForm1.Button3Click(Sender: TObject);
var Res:TNetResource;
begin
with Res do
begin
dwType:=RESOURCETYPE_ANY;
lpLocalName:='X:'; // podłącz jako dysk X
lpRioteName:='\\Komputer\Katalog'; // zdalny dysk
lpProvider:=Nil;
end;
if WNetAddConnection2(Res,'Hasło','Użytkownik',CONNECT_UPDATE_PROFILE)<>NO_ERROR then
ShowMessage('Błąd podczas podłączania dysku sieciowego');
end;
78. Jak ściągnąć plik z Internetu?
Internet, FTP, plik
Należy skorzystać z funkcji URLDownloadToFile z modułu URLMon:
uses URLMon;
procedure TForm1.Button4Click(Sender: TObject);
begin
if URLDownloadToFile(Nil,'http://delphi.koti.com.pl/index.html',
'c:\temp\index.html',0,Nil)<>0 then
ShowMessage('Błąd podczas ściągania pliku');
end;
79. Nie mogę ustawić ikony dla okna MDI.
MDI, ikona, forma
Oto co pisze na ten temat MACiAS:
"Tworzę aplikację z wykorzystaniem MDI i wbrew temu, co jest napisane w helpie, dla okienek MDIChild bez przypisanej ikony nie jest kopiowana ikona okna głównego.
Tak się rzeczywiście dzieje, ale tylko dla okienek ze stylem ramki bsDialog. Trudno mi powiedzieć, czyja to wina [pewnie Inprise'a :-) przyp. M.W.], ale faktycznie help na ten temat milczy. Należy po prostu zrezygnować z tego stylu ramki lub też "ręcznie" kopiować ikonę z okna głównego."
Źródło informacji: Maciej "MACiAS" Pilichowski.
80. Jak odczytać opis błędu funkcji API mając jego kod?
opis błędu, API
Należy skorzystać z funkcji Windows API o nazwie FormatMessage:
function GetErrorString(ErrorID:Integer):String;
var P:PChar;
begin
if FormatMessage(Format_Message_Allocate_Buffer+Format_Message_From_System,Nil,
ErrorId,0,@P,0,Nil)<>0 then
begin
Result:=P;
LocalFree(Integer(P));
end
else Result:=Format('Error in GetErrorString(%d) : %d',[ErrorID,GetLastError]);
end;
function GetLastErrorString:String;
begin
Result:=GetErrorString(GetLastError);
end;
81. Jak sprawdzić czy dany znak jest literą?
znak, litera
Ponieważ w systemie Windows możemy mieć do czynienia z wieloma językami to nie można na stałe wpisać reguły określającej czy znak jest literą czy nie. Ten sam kod może określać literę w jednym z języków a w drugim jakiś zupełnie inny znak. Należy więc odwołać się do jednej z funkcji API: IsCharAlpha, IsCharAlphanumeric, GetStringType.
Źródło informacji: Robin Wschód.
82. Jak programowo zmienić tapetę (tło pulpitu) Windows?
tapeta, tło, pulpit
Należy skorzystać z funkcji SystemParametersInfo podając jej parametr SPI_SETDESKWALLPAPER:
var s: String;
...
s:='plik.bmp';
SystemParametersInfo(SPI_SETDESKWALLPAPER,0,PChar(s),SPIF_UPDATEINIFILE
Or SPIF_SENDWININICHANGE);
Przy okazji widać jak najprościej przekonwertować zmienną typu String na PChar.
Źródło informacji: WiZZARD.
83. Za pomocą jakich funkcji mogę regulować głośność?
głośność, mixer
Można skorzystać z gotowych komponentów lub funkcji API:
uses MMSystem; procedure WaveSetVolume(LVol,RVol:Byte); begin waveOutSetVolume(WAVE_MAPPER,Integer(((LVol shl 8) or (RVol shl 24)))); end; procedure WaveGetVolume(var LVol:Byte;var RVol:Byte); var Vol: Integer; begin waveOutGetVolume(WAVE_MAPPER,@Vol); LVol:=Hi(Vol); RVol:=Vol shr 24; end;
Źródło informacji: WiZZARD.
84. Jak zmienić format wyświetlania i
przechowywania dat dla bazy danych?
BDE, data, format
Należy uruchomić BDE Administratora i ustawić w konfiguracji odpowiedni separator danych i żądany format (ustawia się go inaczej niż w Windows, więc warto zerknąć do helpa).
W pliku naszego projektu (.dpr) w klauzuli uses należy dopisać deklarację użycia modułu SysUtils i zaraz po instrukcji:
Application.Initialize;
należy dopisać:
Application.UpdateFormatSettings:=FALSE; DateSeparator:=... Short/Long DateFormat:=...
Źródło informacji: Adam Jastrząbek, Maciej "MACiAS" Pilichowski.
85. Jak zrobić "inteligentne" okno z atrybutem StayOnTop?
StayOnTop, zawsze na wierzchu
Czasem chcielibyśmy aby jedna z form była cały czas na wierzchu naszej formy głównej nie przykrywając jednak okien innych aplikacji gdy się na nie przełączymy. Jak to zrobić? Oto co pisze na ten temat Hopbit:
procedure TFrmTaskFilters.CreateParams( var cp : TCreateParams );
begin
inherited CreateParams( cp );
cp.Style := WS_POPUP or WS_BORDER or WS_SYSMENU or WS_CAPTION or
WS_MINIMIZEBOX or WS_SIZEBOX or WS_MAXIMIZEBOX;
cp.ExStyle := WS_EX_TOOLWINDOW;
cp.WndParent := Application.MainForm.Handle;
end;
"Wyjaśnienie:
Jeżeli okno(1) będzie parentem okna (2) i okno (2) będzie typu POPUP to
okno (2) będzie zawsze nad oknem (1). Kruczek jest w tym że to musi być
parent w rozumieniu Windows a nie Delphi.
Wadą tego rozwiązania jest to że dużą część parametrów okna trzeba ustawić samemu, choć pewnie można by modyfikować cp tak aby nie niszczyć ustawień Object Inspectora to mnie akurat tak było dużo wygodniej szczególnie że to było robione na chybcika."
Źródło informacji: Krzysztof Świątkowski.
86. Jak zamknąć inną aplikację?
zamykanie aplikacji, API
Poniższą procedurę podał Rafał Płatek:
function KillProc(const ClassName:AnsiString):Boolean;
var
hWnd,hProc:THandle;
pid:DWORD;
begin
Result:=False;
hWnd := FindWindow(PCHAR(ClassName),nil);
if IsWindow(hWnd) then begin
GetWindowThreadProcessId(hWnd, @pid);
hproc := OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if hproc<>0 then begin
Result:=TerminateProcess(hProc,0);
if Result then CloseHandle(hProc);
end;
end;
end;
Aby jej użyć należy podać nazwę klasy okna aplikacji np.:
KillProc('NOTEPAD');
Źródło informacji: podał Rafał Płatek.
87. Jak zablokować uruchamianie wygaszacza ekranu Windows?
wygaszacz ekranu, blokowanie
Aby zablokować uruchamianie wygaszacza należy skorzystać z funkcji WinAPI SystemParametersInfo:
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, Word(False), nil, 0);
Odblokowanie to:
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, Word(True), nil, 0);
Źródło informacji: Dominik Jesiołowski.
88. Jak skonwertować String na PChar?
String, PChar
Aby zrozumieć zawiłości typu String zmieniającego się poprzez kolejne wersje Delphi należałoby napisać więcej niż sam opis funkcji na nim działających. Dlatego pozwoliłem sobie wysmażyć mały artykulik opisujący budowę stringów, ich powiązania z typem PChar oraz niebezpieczeństwa czające się na młodych programistów przy konwersji jednego w drugi.
89. Jak z Delphi 1.0 odwołać się do 32-bitowej biblioteki DLL?
thunk, DLL
Czasem zachodzi potrzeba (fakt, że ostatnio coraz rzadziej) odwołania się z programu 16-bitowego do biblioteki 32-bitowej. Jest to możliwe chociaż niezalecane prze Microsoft. Jak to zrobić opisał Błażej Filimonow:
"Aby coś niecoś poczytać, należy wziąć win32.hlp z pakietu Dalphi3. Znajduje się w nim opis funkcji z 'KRNL386.EXE' :
LoadLibraryEx32W FreeLibrary32W GetProcAddress32W
Aby jednak znaleźć opis nie można korzystać z gotowego indeksu, bo jest niepełny. Trzeba sobie włączyć tworzenie słownika, co przy rozmiarze helpa może zając do 10 min. Później wyszukanie staje się banalnie proste. Problemem może byś użycie niektórych typów, a właściwie ich brak w D1 np. DWORD. Kawałek kodu który załatwia sprawę:
type TLoadLibraryEx32W=function(lpLibFileName:PChar;hFile:Pointer;
dwFlags:LongInt):Pointer;
TFreeLibrary32W=function(hInst:Pointer):Pointer;
var Kernel386Handle:THandle;
LoadLibraryEx32W:TLoadLibraryEx32W;
FreeLibrary32W:TFreeLibrary32W;
Handle_twojego_dll:Pointer;
begin
Kernel386Handle:=LoadLibrary('KRNL386.Exe');
@LoadLibraryEx32W:=GetProcAddress(Kernel386Handle,'LoadLibraryEx32W');
@FreeLibrary32W:=GetProcAddress(Kernel386Handle,'FreeLibrary32W');
Handle_twojego_dll:=LoadLibraryEx32W('twoj_dll',nil,0);
//Tu użycie twojego DLL'a}
FreeLibrary32W(Handle_twojego_dll);
FreeLibrary(Kernel386Handle);
end;
Uwaga 1:
W mojej wersji helpa D3 nie działają odnośniki ze stron do powyższych
funkcji ( wszystko trzeba wyszukiwać se słownika)
Uwaga 2:
Opisy powyższych funkcji zostały usunięte z Win32.hlp w wersji D4. ciekawe
czemu ???? Czyżby w kolejnej wersji były już zbędne ???????
Uwaga 3:
Należy pamiętac, że GetProcAddress32W i pochodne są Case Sensitive. Ja na
tym wpadłem i zmarnowałem godzinę czasu, zamnim zauważyłem, że mam jedną
literę małą, zamiast dużą w nazwie funkcji."
Źródło informacji: Błażej Filimonow.
90. Jak zrobić z projektu MDI projekt SDI?
MDI, SDI, forma
Jeśli zamienimy formę główną z MDIForm na zwykłą oraz MDIChild również na zwykłą formę to nadal będzie ona pokazywana przy uruchamianiu aplikacji. Aby tego uniknąć należy ustawić właściwość Visible formy-dziecka na False.
Właściwość Visible formy może nam się przydać jeśli chcielibyśmy aby zaraz po uruchomieniu aplikacji oprócz głównego okna były widoczne również inne formy. Warto o tym pamiętać.
Źródło informacji: Adam Żukowski.
91. Dlaczego dane wysłane poprzez TCP/IP przychodzą w częściach?
TCP/IP, internet
Protokół TCP/IP nie gwarantuje wielkości danych dostarczanych do odbiorcy. Jeśli wyślemy 100KB danych mogą one dojść do miejsca przeznaczenia w 10-ciu kawałkach po 10KB. Na pewno wiemy tylko, że dojdą w tej samej kolejności w jakiej były wysłane.
Co zatem zrobić aby odebrać całość? Najlepiej wprowadzić własny protokół. Na przykład wysyłać najpierw rozmiar pakietu (jako 4 bajtowy licznik typu Integer) a potem właściwe dane. Odbiornik najpierw odczytuje 4 bajtowy rozmiar a potem tak długo czeka na dane aż odbierze cały przekaz.
92. Jak rozpoznać typ napędu?
napęd, CDROM, dyskietka
Oby rozpoznać czy dany napęd jest kompaktem, dyskiem czy dyskiem sieciowym należy użyć funkcji API GetDriveType:
case GetDriveType('d:\') of
0:S:='Nie można rozpoznać rodzaju napędu';
1:S:='Taki katalog nadrzędny nie istnieje';
drive_Removable:S:='Dysk wymienny';
drive_Fixed:S:='Dysk stały';
drive_Remote:S:='Dysk sieciowy';
drive_CDROM:S:='Napęd CD';
drive_RamDisk:S:='Ramdysk';
end;
93. Jak ustawić akcelerator dla całej formy a nie jednego komponentu?
akcelerator, TActionList
Jeśli chcemy aby w obrębie formy działała konkretna kombinacja klawiszy nie musimy tworzyć jej obsługi ręcznie (korzystając z OnKeyDown lub OnKeyPress), można to zrobić łatwiej używając komponentu TActionList. Należy utworzyć nową akcję, podłączyć ją do formy i ustawić żądany akcelerator.
Uwaga: nie należy zrażać się jeśli na liście dostępnych akceleratorów nie będzie tego, o który nam chodzi. Wystarczy wpisać go wtedy ręcznie.
Źródło informacji: Maciej "MACiAS" Pilichowski, Marcin 'BACIK' Koteras.
94. Rysuję na canvasie obiektu TPaintBox ale wszystko znika gdy zasłonię i
odsłonię formę innym oknem. Co się dzieje?
TPaintBox
W systemie Windows obowiązuje zasada, że okno ma się odrysować na żądanie systemu. Zawartość okien nie jest zapamiętywana i gdy zasłonięte do tej pory okno nagle dostaje się na wierzch pulpitu system każe mu narysować wnętrze (wysyłając komunikat wm_Paint). Korzystając zatem z komponentu TPainBox należy wykonywać operacje rysujące na canvasie za każdym razem gdy część okna zostaje odsłonięta. Aby tak się działo należy umieścić je w obsłudze zdarzenia OnPaint tego komponentu.
Z powyższego wynika również zalecenie aby minimalizować czas potrzebny na rysowanie komponentów. Jeśli np. masz obiekt rysujący wykres funkcji to dane powinieneś wyliczać raz (bo może to trwać długo) i zapamiętać je do wyrysowywania. Przy rysowaniu wykresu (a może on być rysowany wielokrotnie dla tych samych danych - w zależności od tego co robi z programem użytkownik) korzystasz już z wyliczonych wcześniej liczb.
Wielokrotnego rysowania można uniknąć (a raczej je zakamuflować) używając komponentu TImage. Umożliwia on rysowanie na canvasie przechowywanej przez siebie bitmapy i "zapamiętuje" wykonane operacje przyspieszając odświeżanie ekranu. Minusem tego rozwiązania jest zwiększenie wymagań pamięciowych programu.
95.Jak przekazywać dane między procesami?
pamiec wspoldzielona, share, DLL, proces
"Podstawą jest zrozumienie, czym jest pamięć procesu. W przeciwieństwie do środowiska Win31, gdzie programy (procesy) mogły hasać do woli po całej pamięci komputera, w środowisku 32-bit proces jest ograniczony tylko do kawałka pamięci, przydzielonej mu przez system. To oznacza, że ta sama wartość wskaźnika w dwu różnych procesach de facto oznacza różne obszary pamięci! To jest właśnie przyczyna, dla której początkujacy programiści tak często przekazuja do innego procesu jako argument wskaźnik do pamięci głównego programu i dziwią się, że program kładzie się z komunikatem Access Violation.
Najbardziej ogólnym rozwiązaniem jest polecenie systemowi, aby stworzył dla nas pewien obszar pamięci, który będzie przechowywał nasze dane. Obszar ten jest identyfikowany nazwą. Konkretny proces (główny program, DLL, procedura hooka, etc.), który chce coś zapisać lub przeczytać z takiego obszaru, otwiera obszar, tworzy widok tego obszaru i dopiero wtedy po nim pisze (czyta). Aktualizacja danych następuje po zamknięciu widoku. Zwolnienie głównego obszaru pamięci nastepuje oczywiście po wyraźnym sygnale ze strony programisty (wymagane także jest rzecz jasna wcześniejsze zamknięcie widoków).
Oto prosty przykład (bardzo prosty i bez kontroli błędow!):
// zmienne występujace zarówno w procesie 1 i 2
var
uchwyt : THandle;
wskaznik : ^tu_nazwa_Twojej_struktury;
// 1 proces, nadrzędny
// utworzenie obszaru pamięci
uchwyt:=CreateFileMapping($FFFFFFFF,nil,Page_ReadWrite,0,
rozmiar_pamieci,'naprawde unikalna nazwa');
// dowolnie hasamy z widokami na wyżej utworzony obszar
wskaznik:=MapViewOfFile(uchwyt,File_Map_All_Access,0,0,0);
// tu Twój kod operujacy na pamięci
UnmapViewOfFile(wskaznik);
// 2 proces, podrzędny
// obszar pamieci jest tworzony przez proces nadrzędny, wiec ten proces
// tylko go otwiera
uchwyt:=OpenFileMapping(File_Map_Write,TRUE,'naprawde unikalna nazwa');
// dowolnie hasamy z widokami na wyżej utworzony obszar
wskaznik:=MapViewOfFile(uchwyt,File_Map_All_Access,0,0,0);
// tu Twoj kod operujacy na pamięci
UnmapViewOfFile(wskaznik);
// kończymy te zabawę z punktu widzenia procesu 2
CloseHandle(uchwyt);
// 1 proces, nadrzędny // kończymy tę zabawę definitywnie CloseHandle(uchwyt);
Wiecej informacji znajdziecie w helpie do powyżej użytych funkcji.
Jeśli chcemy przekazać coś na szybko z procesu do programu głównego, który posiada okno, możemy wysłać komunikat WM_COPYDATA. Przykładowo:
var copydata : TCopyDataStruct; s : string; begin s:='przyklad wysylania komunikatu WM_COPYDATA'; copydata.cbData:=length(s); copydata.lpData:=@s[1]; SendMessage(uchwyt_docelowego_okna,WM_COPYDATA,0,longint(@copydata));
I już naprawdę na koniec: o ile w Win31 można było przyjąć, iż program był tożsamy procesowi, tak w 32-bit jest to juz błędem. Proces jest wydzielonym kodem z własną pamięcią "operacyjną" - wyznacznikiem nie jest ani jego miejsce w naszym źrodle, ani powiązania, ani nasze pobożne życzenia. Najlepszym przykładem są funkcje hooka - funkcja ta jest transponowana do pamięci procesu adresata. Tj. jeśli napisaliśmy hooka systemowego, to system stworzy tyle jego bliźniakow, ilu może byc adresatów. Ma to swoje minusy (ból glowy programisty), ale też jest np. w miarę wygodnym mechanizmem wstrzykiwania własnej pamięci do zewnętrznych procesów (ale to już inna bajka)."
Źródło informacji: Maciej "MACiAS" Pilichowski.
96. W jaki sposób mogę śledzić przesyłane w systemie komunikaty?
hook, message, filtr, wiadomość
"Na drodze między nadawcą komunikatu, a jego odbiorcą, twórcy Windows
umożliwili zaistnienie różnego rodzaju filtrów, do których dochodzi wiadomość
i w zależności od decyzji takiego filtru, jest ona przekazywana dalej bądz też nie.
Oczywiscie filtr powinien elegancko koegzystowac z innymi, wcześniej
zainstalowanymi filtrami. W tym celu Twoj filtr nie powinien bezpośrednio
oddawać kontroli systemowi, ale wywoływać następny w kolejce filtr.
Piszac filtr pamiętaj, iż system przy każdej przesyłce komunikatu, będzie "wstrzykiwał" kod Twojego filtru do pamięci procesu adresata. Jeśli adresatów będzie wielu, Twoj filtr zostanie zduplikowany wiele, wiele razy. Jakie ma to konsekwencje? Nie możesz polegać na żadnych danych, które zadeklerowałeś na zewnątrz treści funkcji filtrującej w kodzie źrodłowym Twojego DLLa (filtr, który śledzi cały system musi znajdować się wewnątrz DLLa). Co wiecej - zmienne inicjowane wewnątrz Twojej funkcji zmieniają swoje tradycyjne znaczenie. Nie przechowują bowiem już wartości z poprzedniego wywołania tej funkcji, lecz przechowują poprzednią wartość z wywołania danej instancji funkcji (innymi słowy -- każdy duplikat Twojej funkcji, będzie posiadał własne wartości zmiennych inicjowanych).
Powyższe wiadomości powinny wystarczyć Ci do napisania własnego hooka (filtru). Poniżej znajduje się podstawowy schemat instalacji hooka - więcej informacji nt. argumentów i wywołań odnośnych funkcji znajdziesz w helpie:"
// nazwę mapy pamięci najlepiej przypisać stałej
const
MySharedDataMapName = 'MojaNaPewnoUnikalnaNazwaMapyPamieci ;-)';
// jeśli w funkcji hooka będziecie operować jedynie uchwytem
// następnego filtru, to nie ma potrzeby pakowania tego do rekordu
type
TSharedData = record
NextHookHandle : HHook;
end;
// nagłówek hooka będzie zawsze wyglądał jak poniżej, argument code
// jest bardzo ważny, więc na pewno zerknijcie do helpa
// znaczenie wParam i lParam zależy od typu filtra
function MyHookProc(code : longint;
wParam : longint;
lParam : longint) : longint; export; stdcall;
var
// uchwyt do mapy pamięci i wskaźnik do widoku mapy
SharedDataHandle : THandle;
SharedDataPtr : ^TSharedData;
begin
// otwieramy mapę i tworzymy jej widok
SharedDataHandle:=OpenFileMapping(File_Map_Write,TRUE,MySharedDataMapName);
SharedDataPtr:=MapViewOfFile(SharedDataHandle,File_Map_All_Access,0,0,0);
// tutaj następuje Wasz kod
...
// koniec Waszej części
// wywołanie następnego hooka w łańcuchu filtrów
CallNextHookEx(SharedDataPtr^.NextHookHandle,code,wParam,lParam);
// kasujemy widok i zamykamy mapę
UnmapViewOfFile(SharedDataPtr)
CloseHandle(SharedDataHandle);
result:=0; // przepuść meldunek
end;
var
SharedDataHandle : THandle;
SharedDataPtr : ^TSharedData;
procedure InstallFilter(LibHandle : THandle); export;
begin
SharedDataHandle:=CreateFileMapping($FFFFFFFF,nil,Page_ReadWrite,0,
sizeof(TSharedData),MySharedDataMapName);
SharedDataPtr:=MapViewOfFile(SharedDataHandle,File_Map_All_Access,0,0,0);
// drugi argument to oczywiście adres w DLLu naszej funkcji filtru
// czwarty argument to uchwyt okna, które chcemy śledzić, 0 oznacza
// wszystko co tylko znajduje się w systemie -- czyli hook systemowy
SharedDataPtr^.NextHookHandle:=SetWindowsHookEx(WH_GETMESSAGE,
GetProcAddress(LibHandle,'MyHookProc'),LibHandle,0);
end;
procedure RemoveFilter; export;
begin
UnHookWindowsHookEx(SharedDataPtr^.NextHookHandle);
UnmapViewOfFile(SharedDataPtr)
CloseHandle(SharedDataHandle);
end;
Żródło informacji: Maciej "MACiAS" Pilichowski.
97. Kompilacja mojego programu przebiega poprawnie, ale kiedy go
uruchamiam program sie wysypuje z komunikatem "klasa
nieznaleziona". Co jest tego powodem?
forma, dziedziczenie, klasa
"Kompilator Delphi jest o tyle niedopracowany, że nie porównuje plików DFM z odpowiadającymi mu plikami pas. Przyczyną kłopotów jest dziedziczenie form, a dokładniej zmiany w formie podstawowej /skasowanie jednego z elementów/, które rzutują na klasy dziedziczące. Należy po wczytaniu projektu zrobić open dla wszystkich plików PAS. IDE wykaże wszystkie zawieszone w prożni komponenty."
Żródło informacji: Maciej "MACiAS" Pilichowski.
98. Jak znaleźć wszystkie pliki w katalogu i jego podkatalogach?
katalogi
Można użyć poniższej procedury:
procedure PenetrateDirectory(dir: String; list: TStrings; mask: String);
var
SRec: TSearchRec;
res: Integer;
ec: Char;
begin
ec := ':';
if dir <> '' then ec := dir[Length(dir)];
if (ec <> '\') and (ec <> ':') then dir := dir + '\';
// dodanie '\' na koncu nazwy katalogu
res := FindFirst(dir + mask, faArchive, SRec);
while res = 0 do begin
list.Add(dir + SRec.Name);
res := FindNext(SRec);
end;
FindClose(SRec);
// petla "zbierajaca" pliki
res := FindFirst(dir + '*', faDirectory, SRec);
while res = 0 do begin
if (SRec.Attr and faDirectory <> 0)
and (SRec.Name <> '.' ) and (SRec.Name <> '..') then
PenetrateDirectory(dir + srec.Name, list, mask);
res := FindNext(SRec);
end;
FindClose(SRec);
// przeszukiwanie wglab
end;
Źródło informacji: Milosz Krajewski.
99. Jak zainstalować BDE?
BDE, instalacja
Według polskiego oddziału Borlanda jedynymi instalatorami, których powinno się używać do instalacji BDE to InstallShield i Wise. Są to jednak produkty komercyjne i drogie (lub jak w przypadku InstallShield Express Delphi Edition - mocno okrojone). Oczywiście istnieje techniczna możliwość samodzielnej instalacji BDE należy jednak pamiętać, że jest to niezgodne z licencją.
Sposób jest prosty:
cabarc x BDEINST.CAB(program cabarc jest w tym samym katalogu)
regsvr32.exe BDEINST.DLL
Źródło informacji: Radosław "Radio Erewan" Przybył.
100. Jak zidentyfikować komputer korzystając z
numeru MAC karty sieciowej?
karta sieciowa, MAC
Można skorzystać z poniższego kodu:
Uses NB30;
Type
TNBLanaResources = (lrAlloc, lrFree);
PMACAddress = ^TMACAddress;
TMACAddress = Array [0..5] Of Byte;
{Odczytuje liczbę kart sieciowych w komputerze}
Function GetLanaEnum(LanaEnum: PLanaEnum): Byte;
Var
LanaEnumNCB: PNCB;
Begin
New(LanaEnumNCB);
ZeroMemory(LanaEnumNCB, SizeOf(TNCB));
Try
With LanaEnumNCB^ Do
Begin
ncb_buffer := PChar(LanaEnum);
ncb_length := SizeOf(TLanaEnum);
ncb_command := Char(NCBENUM);
NetBios(LanaEnumNCB);
Result := Byte(ncb_cmd_cplt);
End;
Finally
Dispose(LanaEnumNCB);
End;
End;
{Odczytuje nr fizyczny karty sieciowej}
Function GetMACAddress(LanaNum: Byte; MACAddress: PMACAddress): Byte;
Var
AdapterStatus: PAdapterStatus;
StatNCB: PNCB;
Begin
New(StatNCB);
ZeroMemory(StatNCB, SizeOf(TNCB));
StatNCB.ncb_length := SizeOf(TAdapterStatus) + 255 * SizeOf(TNameBuffer);
GetMem(AdapterStatus, StatNCB.ncb_length);
try
with StatNCB^ do
begin
ZeroMemory(MACAddress, SizeOf(TMACAddress));
ncb_buffer := PChar(AdapterStatus);
ncb_callname := '* ' + #0;
ncb_lana_num := Char(LanaNum);
ncb_command := Char(NCBASTAT);
NetBios(StatNCB);
Result := Byte(ncb_cmd_cplt);
if Result = NRC_GOODRET then
MoveMemory(MACAddress, AdapterStatus, SizeOf(TMACAddress));
end;
finally
FreeMem(AdapterStatus);
Dispose(StatNCB);
end;
End;
{"Tłumaczy" numer na postać strawną dla postronnych}
Function MACAddr : String;
var
LanaNum: Byte;
MACAddress: PMACAddress;
RetCode: Byte;
begin
LanaNum := 0;
New(MACAddress);
try
RetCode := GetMACAddress(LanaNum, MACAddress);
If RetCode = NRC_GOODRET then
Begin
Result := Format('%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x',
[MACAddress[0], MACAddress[1], MACAddress[2],
MACAddress[3], MACAddress[4], MACAddress[5]]);
end
else
begin
Beep;
Result := 'Error';
ShowMessage('GetMACAddress Error! RetCode = $' + IntToHex(RetCode,2));
End;
finally
Dispose(MACAddress);
End;
End;
Źródło informacji: Paweł Trzciński.
101. Jak wykryć moment wstawienia czegoś do schowka?
schowek
Należy w sekcji uses dopisać moduł Clipbrd:
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ClipBrd;
W formie dopisać deklaracje trzech procedur i zmiennej:
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
// zmienna i trzy procedury dla kontroli schowka
FClipboardOwner: HWnd;
procedure ClipboardChanged;
procedure WMChangeCBChain(var Msg: TWMChangeCBChain); message WM_CHANGECBCHAIN;
procedure WMDrawClipboard(var Msg: TWMDrawClipboard); message WM_DRAWCLIPBOARD;
w procedurze zdarzenia FormCreate dopisać:
FClipboardOwner := SetClipboardViewer(Handle);
w procedurze zdarzenia FormDestroy dopisać:
ChangeClipboardChain(Handle, FClipboardOwner);
Napisać poniższe procedury:
procedure TForm1.WMChangeCBChain(var Msg: TWMChangeCBChain);
begin
if Msg.Remove = FClipboardOwner then FClipboardOwner := Msg.Next
else SendMessage(FClipboardOwner, WM_CHANGECBCHAIN, Msg.Remove, Msg.Next);
Msg.Result := 0;
end;
procedure TForm1.ClipboardChanged;
var
Format: Word;
begin
{
tu wpisac co ma sie dziac po wstawieniu czegos do schowka
np. sprawdzenie formatu schowka i wywolanie pozadanego
przez nas zdarzenia. Dla testu niech bedzie to zwykle 'beep' :-)
}
beep;
end;
procedure TForm1.WMDrawClipboard(var Msg: TWMDrawClipboard);
begin
SendMessage(FClipboardOwner, WM_DRAWCLIPBOARD, 0, 0);
Msg.Result := 0;
ClipboardChanged;
end;
Źródło informacji: Dafi.
102. Jak zamknąć system?
zamykanie, Windows, API
Należy użyć funkcji API:
W tej formie funkcje zadziałają pod Windows 9x. Dla Windows NT należy je zmodyfikować tak aby podawały systemowy prawa użytkownika. Nie każdy użytkownik ma bowiem prawo zamykać system w NT. Oto jak to zrobić:
function DelphiExitWindows( Flags : Word):Boolean;
var
iVersionInfo: TOSVersionInfo;
iToken : THandle;
iPriveleg : TTokenPrivileges;
iaresult : Integer;
begin
Result:=FALSE;
FillChar (iPriveleg, SizeOf (iPriveleg), #0);
iVersionInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
GetVersionEx(iVersionInfo);
if iVersionInfo.dwPlatformId <> VER_PLATFORM_WIN32_NT then
Result:=ExitWindowsEx (Flags, 0)
else
if OpenProcessToken (GetCurrentProcess,
TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, iToken) then
if LookupPrivilegeValue (NIL,'SeShutdownPrivilege',
iPriveleg.Privileges[0].Luid) then
begin
iPriveleg.PrivilegeCount := 1;
iPriveleg.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
if AdjustTokenPrivileges (iToken,False,iPriveleg,
Sizeof(iPriveleg),iPriveleg,iaresult) then
Result:=ExitWindowsEx (Flags, 0);
end;
end;
Przykład wywołania:
DelphiExitWindows( EWX_REBOOT or EWX_FORCE );
Źródło informacji: Paweł Księżyk.
103. Jak obsłużyć upuszczanie plików na formę?
Drag&Drop
Należy skorzystać z komunikatu wm_DropFiles. Ma to tą zaletę, że zadziała nawet w Delphi 1.
uses
ShellAPI; {obsługa D&D}
....
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnMessage := AppMessage;
DragAcceptFiles (Handle, True);
end; {mówimy systemowi że chcemy obsłużyć D&D}
procedure TForm1.WMDropFiles (hDrop : THandle; hWindow : HWnd);
Var
TotalNumberOfFiles,
nFileLength : Integer;
pszFileName : PChar;
i : Integer;
Begin
//liczba zrzuconych plików
TotalNumberOfFiles := DragQueryFile (hDrop , $FFFFFFFF, Nil, 0);
for i := 0 to TotalNumberOfFiles - 1 do begin
nFileLength := DragQueryFile (hDrop, i , Nil, 0) + 1;
GetMem (pszFileName, nFileLength);
DragQueryFile (hDrop , i, pszFileName, nFileLength);
//pszFileName - nazwa upuszczonego pliku
//tutaj robimy coś z nazwą pliku
FreeMem (pszFileName, nFileLength);
end;
DragFinish (hDrop);
end; //sprawdzamy co zostało przeciągnięte i obsługujemy to
procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
case Msg.Message of
WM_DROPFILES : WMDropFiles (Msg.wParam, Msg.hWnd);
end;
end; //obsługujemy komunikat WM_DROPFILES
procedure TForm1.FormClose (Sender: TObject; var Action: TCloseAction);
begin
DragAcceptFiles (Handle, False);
end; //dziękujemy
Źródło informacji: Artur Prokopiuk.
104. Co zrobić aby forma główna nie pokazywała się po starcie programu?
forma
Należy w kodzie projektu przed Application.Run dodać kod:
Application.ShowMainForm:=False
105. Jak wywołać standardowe okno podłączania dysku sieciowego?
sieć, udziały
Należy użyć kodu:
WNetConnectionDialog(Application.Handle, RESOURCETYPE_DISK);
Poniższa procedura pokazuje okno odłączenia dysku:
WNetDisconnectDialog(Application.Handle, RESOURCETYPE_DISK);
Zaś zamiana stałej RESOURCETYPE_DISK na RESOURCETYPE_PRINT pokaże okno podłączenia drukarki sieciowej.
Źródło informacji: Konrad Pawlus.
106. Jak wydobyć systemową ikonę pliku?
ikona, Explorer
Należy skorzystać z funkcji SHGetFileInfo. Wygodnie będzie użyć również klasy TImageList. W pierwszym kroku deklarujemy listy ikon:
var SmallImages, LargeImages: TImageList;
Następnie tworzymy je i wypełniamy ikonami:
uses ShellAPI; {w sekcji uses należy dodać plik ShellAPI.pas}
procedure TMainForm.CreateImages;
var sfi: TSHFileInfo;
begin
if not Assigned(SmallImages) then
begin
SmallImages := TImageList.Create(Self);
SmallImages.Handle := SHGetFileInfo('nazwapliku', 0, sfi, SizeOf(TSHFileInfo),
SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
SmallImages.ShareImages := TRUE;
end;
if not Assigned(LargeImages) then
begin
Largeimages := TImageList.Create(self);
LargeImages.Handle := SHGetFileInfo('nazwapliku', 0, sfi, SizeOf(TSHFileInfo),
SHGFI_SYSICONINDEX or SHGFI_LARGEICON);
LargeImages.ShareImages := TRUE;
end;
end;
Teraz możemy już z nich korzystać. Na przykład wypełniając TListView nazwami plików i ich ikonami. Lista musi mieć trzy kolumny (Caption, SubItems[1], SubItems[2]). Należy też poinformować ją skąd ma czerpać ikony:
Lista.SmallImages := SmallImages; Lista.LargeImages := LargeImages;Teraz wystarczy dodać do listy plik przy pomocy procedury:
procedure TMainForm.DodajDoListy(Lista: TListView; Plik: String);
var NowyPlik: TListItem;
Sfi: TSHFileInfo;
Typ: String;
begin
SHGetFileInfo(PChar(Plik), 0, Sfi, SizeOf(sfi),
SHGFI_TYPENAME or SHGFI_SYSICONINDEX or SHGFI_DISPLAYNAME)
NowyPlik := Lista.Items.Add;
NowyPlik.Caption := ExtractFileName(Plik);
NowyPlik.ImageIndex := Sfi.iIcon;
NowyPlik.SubItems.Add(Sfi.szTypeName);
NowyPlik.SubItems.Add(Typ);
end;
Źródło informacji: Konrad Pawlus.
107. Nie działa TQuery.Refresh. Co robić?
SQL, TQuery, Refresh
Close;Open;
Źródło informacji: Piotr Neil Gawronski.
108. Mam problemy z drukowaniem bitmap. Co robić?
drukowanie, bitmapa
Na niektórych drukarkach występują problemy z drukowaniem bitmap. Powinna pomóc poniższa procedura:
procedure PrintBitmap(Bitmap: TBitmap; X, Y: Integer);
var
Info: PBitmapInfo;
InfoSize: Integer;
Image: Pointer;
ImageSize: Longint;
begin
with Bitmap do
begin
GetDIBSizes(Handle, InfoSize, ImageSize);
Info := AllocMem(InfoSize); {<-- MemAlloc dla D1}
try
Image := AllocMem(ImageSize); {<-- MemAlloc dla D1}
try
GetDIB(Handle, Palette, Info^, Image^);
with Info^.bmiHeader do
StretchDIBits(Printer.Canvas.Handle, X, Y, Width,
Height, 0, 0, biWidth, biHeight, Image, Info^,
DIB_RGB_COLORS, SRCCOPY);
finally
FreeMem(Image, ImageSize);
end;
finally
FreeMem(Info, InfoSize);
end;
end;
end;
Źródło informacji: Marcin BACIK Koteras.
109. Jak zapisać poprawnie datę w SQL?
lokalny SQL, format daty
W lkalnym SQL format daty jest niezmienny i niezależny od ustawień systemowych. Należy datę zapisywać w postaci: "MM/DD/YY(YY)". Jeśli datę wpisujemy w runtime pomocna może być konstrukcja:
DataDlaSQLa:=''''+FormatDateTime('mm"/"dd"/"yyyy',d)+'''';
Źródło informacji: Maciej "MACiAS" Pilichowski.
110. W jaki sposób zasymulować kliknięcie myszy
lub klawiatury, ale w taki sposób, żeby było wykrywalne przez inne programy?
mysz, klawiatura
Do symulacji kliknięć myszą służy funkcja WinAPI mouse_event:
mouse_event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTDOWN, x, y, 0, 0); mouse_event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTUP, x, y, 0, 0);
co daje symulację kliknięcia lewym przyciskiem myszy w punkcie (x,y), natomiast do symulacji klawiatury używamy funkcji keybd_event
keybd_event(VK_UP, 0, 0, 0); keybd_event(VK_UP, 0, KEYEVENTF_KEYUP, 0);
Co powoduje symulację kliknięcia klawisza strzałki w górę.
Źródło informacji: Krzysztof Borys (Bask).
111. Jak spakować bazę danych MS Access?
baza danych, Access
Na to pytanie odpowiada Tomek Cwajda: "Należy skorzystać z obiektu TJetEngine jaki udostępnia biblioteka Microsoftu JRO (Jet and Replication Objects) będąca składnikiem pakietu MDAC (Microsoft Data Access Components). Jak dotąd najświerzszą wersją MDAC jest wersja 2.5 a jeżeli chodzi o JRO to jest to wersja 2.1. Aby skorzystać z wyżej wymienionej biblioteki należy "importować bibliotekę typów". po uruchomieniu komendy z menu "Project | Import Type Library" wskazujemy bibliotekę "Microsoft Jet and Replication Objects 2.1 Library (Version2.1)". Jeżeli tej biblioteki nie ma na liście a jesteśmy pewni, że mamy zainstalowane MDAC, to należy dodać je do listy wskazując plik "msjro.dll", który z regóły jest umiejscowiony w katalogu "C:\Program Files\Common Files\System\ado\". Po zainstalowaniu możemy powstały moduł uwzględniać w projekcie. Standardowa nazwa nadawana przez wizzard'a to JRO_TLB.
unit Unit1;
interface
uses
...,
JRO_TLB ;
type
...
implementation
...
procedure CompressRepair;
var MyJetEngine:TJetEngine;
strSourceConnection,strDestConnection,strJetType:WideString;
begin
//Dla Access 2000 Engine Type =5
strJetType:='Jet OLEDB:Engine Type=4';
strSourceConnection:='Data Source=D:\Program Files\'+
'Borland Shared\Data\dbdemos.mdb;';
strDestConnection:='Data Source=D:\Program Files\'+
'Borland Shared\Data\dbdemos_compacted.mdb;'+strJetType;
MyJetEngine:=TJetEngine.Create(nil);
try
MyJetEngine.CompactDatabase(strSourceConnection,strDestConnection);
finally
MyJetEngine.Free;
end;
end;
end.
Mogę dodać, że procedura ta powinna również realizować naprawianie bazy, jednak nie przetestowałem tego."
Źródło informacji: Tomek Cwajda.
112. Jak wykonać konwersję z systemu dziesiętnego na binarny lub szesnastkowy i na odwrót?
konwersja, binarnie, szesnastkowo
Można użyć do tego celu funkcji napisanych przez Milosza Krajewskiego:
function Chr2Int(c: Char): Integer;
begin
c := UpCase(c);
if (c >= '0') and (c <= '9') then Result := Integer(c) - Integer('0')
else Result := Integer(c) - Integer('A') + 10;
end;
function Int2Chr(i: Integer): Char;
begin
if i < 10 then Result := Char(i + Integer('0')) else
Result := Char(i - 10 + Integer('A'));
end;
function Str2Int(s: String; base: Integer): Integer;
var
i: Integer;
begin
Result := 0;
for i := 1 to Length(s) do Result := (Result * base) + Chr2Int(s[i]);
end;
function Int2Str(v: Integer; base: Integer): String;
var
m: Integer;
begin
Result := '';
while v <> 0 do begin
m := v mod base; v := v div base; Result := Int2Chr(m) + Result;
end;
if Result = '' then Result := '0';
end;
Przykład użycia:
labelHex := Int2Str(157, 16);
labelBin := Int2Str(157, 2);
labelOct := Int2Str(157, 8);
label19 := Int2Str(157, 19); // to bedzie w 19-stkowym
ValueHex := Str2Int('110101010', 2);
ValueBin := Str2Int('AF16', 16);
ValueOct := Str2Int('740', 8);
Value7 := Str2Int('666', 7); // z systemu 7-kowego
labelBin := Int2Str(Str2Int(labelHex, 16), 2); // 16 -> 2
Źródło informacji: Milosz "Krashan" Krajewski.
113. Jak uzyskać posortowane drzewo katalogów?
drzewo katalogów, dysk
Na to pytanie odpowiada Przemysław Walasek:
"Oczywiście istnieją (jak zawsze) dwie metody: falenicka i otwocka. Różnią się one tylko w sposobie uzyskania struktury posortowanego drzewka. Podstawowym błędem jest używanie metody AlphaSort kontrolki TreeView.
1. Metoda falenicka.
procedure ListujKatalogi(tnRodzic : TTreeNode; strSciezka : string);
//Metoda falenicka
var
sr : TSearchRec;
iWynik : integer;
tnDziecko : TTreeNode;
begin
iWynik := FindFirst(strSciezka + '*.*', faAnyFile, sr);
//czytamy pierwszy plik_katalog
while iWynik = 0 do{odkiedy cos jest znajdywane}
begin
if ((sr.Attr and faDirectory) = faDirectory) and
(sr.Name <> '.')and(sr.Name <> '..') then
//jezeli jest to katalog
begin{dopisujemy do drzewka}
tnDziecko := frm.trv.Items.AddChild(tnRodzic, sr.Name);
//dopisujemy katalog jako podrzedny
Licznik := Licznik + 1;
frm.Caption := 'Znalazlem : ' + IntToStr(Licznik) + '
katalog(ów).';
ListujKatalogi(tnDziecko ,strSciezka + sr.Name + '\');
//wywolujemy rekeurncyjnie metode, jako kat. nadrzedny bedzie teraz ostatnio znaleziony kat.
end;
iWynik := FindNext(sr);
//szukamy nastepnych
end;
FindClose(sr);
//zwalniamy pamiec
if tnRodzic = Nil then
else
tnRodzic.AlphaSort;
//i tu jest caly widz metody falenickiej!
//ta linia wykona sie wtedy gdy kat. symbolizowany przez tnRodzic
//ma juz znane wszystkie swoje podkatalogi, i tu nalezy wywolac
//ALphaSort, ale NIE DLA TREEVIEW ale dla TREENODE tnRodzica!
//roznica polega na tym ze AplhaSort TreeView sortuje cala zawartosc
//a tn.AlphaSort Sortuje TYLKO swoje dzieci, efekt widac...
//I co panie Lodku jest <30 lini kodu ;-)
end;
//procedura uruchamiajaca reakcje lancuchowa
procedure Tfrm.btnClick(Sender: TObject);
var
i : byte;
cDysk : Char;
tdtStart, tdtKoniec : TDateTime;
begin
trv.Items.Clear;
//czyscimy drzekw
tdtStart := Now;// *
frm.trv.Items.BeginUpdate;
for cDysk := 'C' to 'G' do
begin
ListujKatalogi(trv.Items.AddChild(nil, '(Dysk ' + cDysk + ':)'),
cDysk + ':\');
//ako pierwsze wywolanie przekazujemu NIL, co oznacza ze pierwszym
//tnRodzicem jest TreeView
end;
frm.trv.Items.EndUpdate;
tdtKoniec := Now;{*}
tdtKoniec := tdtKoniec - tdtStart; // *
btn.Caption := 'Czas listowania: ' + DateTimeToStr(tdtKoniec); // *
//oczywiscie nie bede tu pisal jak rozpoznac ktora litera to dyk itd
//bo to jest w FAQ grupy
end;
2. Metoda otwocka. Opiera się na zgoła innym założeniu, mianowicie sortowaniu ulega tylko ta ilość Node-ów, która jest niezbędna. Jak to uzyskać najprościej? Ano w obsłudze zdarzenia kontrolki TreeView - Expanding (nie Expanded), które towarzyszy rozwijaniu node-ow. Procedury tej nie powinno się umieszczać w zdażeniach OnClick lub DblClick, bo rozwijanie jest możliwe z poziomu kodu, co nie spowoduje wykonania w/w zdarzeń.
procedure ListujKatalogi(tnRodzic : TTreeNode; strSciezka : string);
//Metoda otwocka
var
sr : TSearchRec;
iWynik : integer;
tnDziecko : TTreeNode;
begin
iWynik := FindFirst(strSciezka + '*.*', faAnyFile, sr);
//czytamy pierwszy plik_katalog
while iWynik = 0 do//jezeli znalazl cos
begin
if ((sr.Attr and faDirectory) = faDirectory) and
(sr.Name <> '.')and(sr.Name <> '..') then
begin{dopisujemy do drzewka}
tnDziecko := frm.trv.Items.AddChild(tnRodzic, sr.Name);
Licznik := Licznik + 1;
frm.Caption := 'Znalazlem : ' + IntToStr(Licznik) + '
katalog(ów).';
ListujKatalogi(tnDziecko ,strSciezka + sr.Name + '\');
end;
iWynik := FindNext(sr);
end;
FindClose(sr);
end;
procedure Tfrm.trvExpanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
//Metoda otwocka
begin
trv.Items.BeginUpdate;
while Node <> nil do
//sprawdzamy czy istnieja jakies dzieci Node-a wywolujacego zdarzenie
begin
Node.AlphaSort;//jezeli tak, to sortujemy
Node := Node.GetNextSibling;//i pobieramy nastepnego w szeregu
end;
trv.Items.EndUpdate;
end;
Widać, że nie ma różnicy przy ładowanu drzewka, czasy ładowania z sortowaniem i bez są porównywalne, więc metoda falenicka wydaje sie być atrakcyjniejsza, gdyż zwalnia nas od obsługi Expanding. Można oczywiście posortować samemu, podczas stanu jałowego aplikacji, można bezproblemowo dodać do tej listy pliki itd... Można oczywiście ładowac katalogi bezpośrednio z dysku w odpowiedni sposób na żądanie rozwinięcia, napisałem kiedyś taki właśnie sposób, mogę go sprobować odnaleźć. Jego zaletą jest to, iż pozwala na wyświetlanie aktualnego stanu katalogów. Wyżej podane sposoby ładują drzewo, ale są niewrażliwe na zmiany na dysku. Ale rozwiązanie też nie jest trudne. O późno się zrobiło, czas kończyć."
Źródło informacji: Przemysław Walasek.