Einzelnen Beitrag anzeigen

Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#1

Einführung in die Grundlagen der Win32API Programmierung

  Alt 7. Aug 2003, 01:56
Bungeebug hatte mich gefragt, ob ich ihm mal eine persönliche Einführung in die Win32-API Programmierung unter Delphi per ICQ geben könnte. Da es auch andere interesssiert, wie es scheint, habe ich mich dazu entschlossen, mit der Genehmigung von Bungeebug, das ICQ-Log in leicht überarbeiteter Form hier zu veröffentlichen.

Es kann in dieser Form als eine erweiterete Einführung zu meinen nonVCL-Tutorials auf meiner Seite gesehen werden.

Zitat:
BungeeBug (11:28 PM) :
so dann gib mir mal saures ich brauch sowas wie nen 10 min schnell kurs ... ich hab so ungefair verstanden wie das ganze geht nur bin ich nich ganz durch gestiegen wie ich so ne Unit aufbaun muss

Luckie (11:29 PM) :
Als Grundgerüst könnte man das erste Tutorial nehmen ("Fenster").

Luckie (11:33 PM) :
Also Windows basiert auf Messages.

Luckie (11:33 PM) :
Windows kommuniziert über Messages mit den Programmen/Fenstern.

Luckie (11:34 PM) :
Beispiel:

Luckie (11:34 PM) :
Zwei Fenster. Fenster A überdeckt Fenster B.

Luckie (11:34 PM) :
Fenster A wird geschlossen oder verschoben und Fenster B wird sichtbar.

Luckie (11:35 PM) :
Jetzt muß der Bereich, der von B eingenommen wird neu gezeichnet werden.

Luckie (11:35 PM) :
Klar?

BungeeBug (11:35 PM) :
jup

BungeeBug (11:35 PM) :
klingt logisch

Luckie (11:35 PM) :
OK. Da Windows aber nicht weiß, was auf dem Fenster ist, kann es das nicht selber machen. Das weiß eben nur Fenster B.

BungeeBug (11:36 PM) :
also sendet win ne nachricht "mach neu" an Fenser B?!

Luckie (11:36 PM) :
Also schickt Windows an Fenster B eine Nachricht, die B sagt. "Sorg bitte dafür, dass der Bereich

deines Fensters wieder stimmt."

Luckie (11:37 PM) :
Die Nachricht heißt: WM_PAINT.

BungeeBug (11:37 PM) :
ok

Luckie (11:38 PM) :
Damit dieser Mechanismus funktioniert brauchen wir zwei Dinge: auf der einen Seite etwas wo Windows die Nachrichten plazieren kann und auf der anderen Seite etwas, das die Nachrichten abholt.

Luckie (11:38 PM) :
Auch klar?

BungeeBug (11:38 PM) :
sowas wie nen postkasten ?!

Luckie (11:38 PM) :
Jupp.

BungeeBug (11:39 PM) :
dann hab ichs verstanden

Luckie (11:39 PM) :
Der Postkasten heißt Messagequeue.

Luckie (11:39 PM) :
Un der Idiot der städig hinrennt und nachkuckt heißt Messageloop.

Luckie (11:40 PM) :
Die Messagequeue wird von Windows gestellt und die Messageloop vom Prozess des Fensters.

Luckie (11:40 PM) :
Die Messageloop ist eine Endlosschleife, die solange läuft bis eine bestimmte Nachricht kommt.

Luckie (11:41 PM) :
So bald in der Messagequeue ein WM_QUIT auftaucht, wird die Messageloop beendet und das Programm geschlossen.

Luckie (11:43 PM) :
So. Wie sieht die Umsetztung dieses Teils in Delphi aus?

Luckie (11:44 PM) :
Also die Messageloop befindet sich im Hauptprogramm, das ist die DPR-Datei.

Luckie (11:45 PM) :
while true do
begin
if not GetMessage(msg, 0, 0, 0) then
break;
TranslateMessage(msg);
DispatchMessage(msg);
end;

Luckie (11:46 PM) :
So das entscheidende ist GetMessage. Das PSDK sagt dazu als Rückgabewert:
If the function retrieves a message other than WM_QUIT, the return value is nonzero.
If the function retrieves the WM_QUIT message, the return value is zero.


Luckie (11:47 PM) :
Wenn also WM_QUIT kommt habe ich null als Rückgabewert und das break wird ausgelöst und damit die while-Schleife verlassen. Der Rest des Hauptprogramms wird abgearbeitet und wir landen beim end mit dem Punkt dahinter.

Luckie (11:48 PM) :
Fertig, ende, aus, tot.

Luckie (11:48 PM) :
So das war aber nur die erste Hälfte.

Luckie (11:49 PM) :
Es nützt ja alles nichts, wenn ich zwar die Nachrichten abhole habe nichts damit anfange.

Luckie (11:50 PM) :
In der while-Schleife fallen zwei Funktionen auf:
TranslateMessage(msg);
DispatchMessage(msg);

Luckie (11:50 PM) :
TranslateMessage ist erstmal unwichtig.

Luckie (11:51 PM) :
Eine Rolle spielt hier erstmal nur DispatchMessage.

Luckie (11:52 PM) :
DispatchMessage leitet die Nachrichten an eine Fensterprozedur weiter, die wir in unserem Programm deklarieren müssen.

Luckie (11:52 PM) :
Sie sieht im Grundgerüst so aus.

Luckie (11:53 PM) :
function WndProc(hWnd: HWND; uMsg: UINT; wParam: wParam; lParam: LParam):
lresult; stdcall;
begin
Result := 0;
case uMsg of
WM_CREATE:
begin
end
else
Result := DefWindowProc(hWnd, uMsg, wParam, lParam);
end;
end;

Luckie (11:53 PM) :
Im parameter uMsg steht die Nachricht drin, die wir abgefangen haben.

Luckie (11:54 PM) :
In hWnd das Fensterhandle an das die Nachricht gerichtet ist.

Luckie (11:54 PM) :
wParam und lParam sind weitere messagespezifische Parameter.

BungeeBug (11:55 PM) :
wo bekomme ich das Handle her?

Luckie (11:55 PM) :
Wird dir von Windows mitgeliefert.

BungeeBug (11:55 PM) :
also hab ich damit nix am Hut?

Luckie (11:55 PM) :
Nein.

BungeeBug (11:55 PM) :
ok

Luckie (11:56 PM) :
Richten wir nun unser Augenmerk auf den else Zweig der case-Konstruktion.

Luckie (11:56 PM) :
DefWindowproc.

Luckie (11:57 PM) :
Steht für Default Window Procedure.

Luckie (11:57 PM) :
Alle Nachrichten die mit case nicht abgearbeitet werden landen im else Zweig und werden an die

DefWindowProc weitergereicht.

BungeeBug (11:59 PM) :
wird dann immer der gleiche code ausgeführt oder hat windows da was parat?

Luckie (11:59 PM) :
Dazu komme ich jetzt.

BungeeBug (11:59 PM) :


Luckie (00:00 AM) :
Butons, Listboxen usw. sind auch nur Fenster mit einer eigenen WindowsProcedure.

Luckie (00:00 AM) :
Jetzt ist dir sicherlich aufgefallen, das man sich bei denen nicht um das Neuzeichnen kümmern muß.

BungeeBug (00:01 AM) :
also nur bei der Form?

Luckie (00:01 AM) :
Das liegt daran, weil das die Fensterprozedur von den Kontrols übernimmt.

BungeeBug (00:01 AM) :
ok

Luckie (00:01 AM) :
Zeichen ich jetzt in OnPaint auf das Form, um mal ein VCL Beispiel zu nehmen, existiert keine Fensterprozedur dafür.

Luckie (00:02 AM) :
Klar? Woher soll auch Windows wissen, was du auf deinem Fenster gemalt hast.

BungeeBug (00:02 AM) :
jo

Luckie (00:02 AM) :
Ergo mußt du das selber machen, in dem du WM_PAINT abfängst und dort entsprechend reagierst.

Luckie (00:03 AM) :
Das wären zwei drittel der Grundlagen.

Luckie (00:03 AM) :
Kucken wir uns noch mal den Rest an.

BungeeBug (00:03 AM) :
bis jetzt hab ichs verstanden

Luckie (00:03 AM) :
Guter Schüler.

BungeeBug (00:04 AM) :
ich geb mir mühe

Luckie (00:04 AM) :
Kuck die mal den Source zu dem Fensterdemo an.

BungeeBug (00:04 AM) :
hab ich

Luckie (00:04 AM) :
Hast du?

BungeeBug (00:04 AM) :
was genau?

Luckie (00:04 AM) :
Scroll ganz runter.

Luckie (00:05 AM) :
var
{Struktur der Fensterklasse}
wc: TWndClassEx = (
cbSize : SizeOf(TWndClassEx);
Style : CS_HREDRAW or CS_VREDRAW;
lpfnWndProc : @WndProc;
cbClsExtra : 0;
cbWndExtra : 0;
hbrBackground : COLOR_APPWORKSPACE;
lpszMenuName : nil;
lpszClassName : ClassName;
hIconSm : 0;
);
msg: TMsg;

begin
wc.hInstance := hInstance;
wc.hIcon := LoadIcon(hInstance, MAKEINTRESOURCE(100));
wc.hCursor := LoadCursor(0, IDC_ARROW);
{Fenster registrieren}
RegisterClassEx(wc);

BungeeBug (00:05 AM) :
hab ich

Luckie (00:05 AM) :
Dahin. das ist der einzigste bisher unklare Punkt.

Luckie (00:06 AM) :
So, was passiert da?

Luckie (00:06 AM) :
Bzw. warum machen wir das?

Luckie (00:06 AM) :
Windows unterhält für jedes Fenster eine Messagequeue.

Luckie (00:07 AM) :
Nur müssen wir Windows dazu veranlassen diese einzurichten und entsprechend Speicher für das Fenster bereitzustellen. Mal so vereinfacht gesagt.

Luckie (00:07 AM) :
Dies geschiet mit der Funktion RegisterClassEx.

Luckie (00:08 AM) :
Diese Funktion erwartet eine Zeiger auf eine Struktur vom Typ TWndClassEx.

Luckie (00:09 AM) :
Diese Struktur wird mit Werten vorbelegt, die unser Fenster charackterisieren.

Luckie (00:10 AM) :
Wie zum Beispiel, der Hintergrundfarbe, dem Icon, dem Cursor, dem Menü usw.

Luckie (00:10 AM) :
Und ganz wichtig. Über die Strukturvariable lpfnWndProc wird eine Verknüfung von DispatchMessage zu

unserer Fensterprozedur hergestell.

Luckie (00:12 AM) :
Ist dies geschehen, können wir unser Fenster mit CreateWindowEx erzeugen und anzeigen.

Luckie (00:13 AM) :
Alles klar?

BungeeBug (00:13 AM) :
muss ich die verbindung selbst herstellen oder passiert das von selbst?

Luckie (00:14 AM) :
Die stellst du selber her, in dem du bei lpfnWndProc deine Fensterprozedur angibst.

BungeeBug (00:14 AM) :
also das hier? lpfnWndProc : @WndProc;

Luckie (00:14 AM) :
Genau.

BungeeBug (00:14 AM) :
ok verstanden

Luckie (00:15 AM) :
Das war eigentlich schon alles.

Luckie (00:15 AM) :
Und das alles macht auch die VCL, nur dass sie es vor dir versteckt.

BungeeBug (00:15 AM) :
und jetzt kann ich nen leeres Forumlar erzeugen?

Luckie (00:15 AM) :
nonVCl Grundkenntinsse tragen also auch zum tieferen Verständnis von Windows bei, wie du siehst.

Luckie (00:16 AM) :
Genau. Nur das wir wohl besser von Fenster reden.

BungeeBug (00:16 AM) :
das meiste hab ich davon gewusst nur nich das es so funktioniert

Luckie (00:16 AM) :
Ok. Jetzt machen wir es mal, wie in der Schule. Ich stelle mal ein paar Fragen und du antwortest mir.



BungeeBug (00:17 AM) :
i try

Luckie (00:17 AM) :
Gut. Lange for-Schleife mit irgendwas drin.

Luckie (00:17 AM) :
Dauert 3 Minuten oder so.

Luckie (00:17 AM) :
Warum reagiert mein Fenster nicht mehr?

BungeeBug (00:18 AM) :
weil wärend der schleife keine nachrichten angenommen werden?

Luckie (00:19 AM) :
Genau. Dein Programm/Fenster hängt in der for-Schleife und kann demnach mit der Messageloop keine Nachrichten empfangen, an die Fensterprozedur weiterleiten und abarbeiten.

BungeeBug (00:20 AM) :


BungeeBug (00:20 AM) :
*freu*

Luckie (00:20 AM) :
Warum kommen WM_TIMER Nachrichten nicht immer pünktlich bzw. werden nicht immer im Intervall abgearbeitet?

BungeeBug (00:21 AM) :
ich denk mal aus dem gleichen Grund weil irgend ne Schleife mal länger braucht als der Intervall is und tada ne nette verzögerung

Luckie (00:21 AM) :
Also OnTimer wäre das in der VCL.

Luckie (00:21 AM) :
Nicht ganz.

BungeeBug (00:22 AM) :
dann beleher mich

Luckie (00:22 AM) :
Nehmen wir an es kommt ein WM_TIMER - WM_PAINT - WM_TIMER.

Luckie (00:22 AM) :
Intervall 1 Sekunde.

Luckie (00:22 AM) :
Jetzt braucht dein Programm aber 5 Sekunden um den Code in WM_PAINT auszuführen.

Luckie (00:23 AM) :
Und was ist? Käse war es mit dem Intervall von einer Sekunde.

BungeeBug (00:23 AM) :
das is das was ich meinte ...

Luckie (00:24 AM) :
OK. Ist jetzt extrem. Aber es zeigt, was ich meine. Zu dem haben WM_TIMER Messages eine sehr geringe Priorität bei Windows.

Luckie (00:24 AM) :
Nächste Frage.

Luckie (00:24 AM) :
Warum muß ich mich um das Neuzeichnen von Buttons nicht kümmern?

BungeeBug (00:26 AM) :
weil das das Fenster sprich lpfnWndProc : @WndProc; macht?!

Luckie (00:26 AM) :
Mit der Fensterprozedur hat es was zu tun.

BungeeBug (00:27 AM) :
ach ne das gibt nur an wie das Fenster aussieht nicht wo die buttons sind oder?

Luckie (00:27 AM) :
Das gibt nur an welche Fenster zu der Fensterklasse gehört, die du mit RegisterWindowsEx registriert

hast.

Luckie (00:27 AM) :
Du kuckst an der falschen Stelle.

Luckie (00:28 AM) :
Kuck dir mal die Fensterprozedur an.

Luckie (00:28 AM) :
case
...
else
???
end;

Luckie (00:29 AM) :
Der entscheidenede Code steht bei den ???

Luckie (00:30 AM) :
Noch 30 Sekunden.

BungeeBug (00:30 AM) :
hmm ... ich passe ... evl seh ich den wald gard nich

Luckie (00:31 AM) :
Jedes Fenster, auch Buttons, haben eine eigenständige Fensterprozedur.

Luckie (00:31 AM) :
Da Windows weiß, wie ein Button aussieht kann es den Button selber zeichnen.

Luckie (00:32 AM) :
Kuck dir noch mal an, was ich zu DefWindowProc geschrieben habe.

Luckie (00:32 AM) :
So Big Daddy will ins Internet.

Luckie (00:32 AM) :
Kann ich das Log veröffentlichen auf meiner HP und in der DP? Interessiert auch andere wie es scheint.

BungeeBug (00:33 AM) :
jojo
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat