|
Alles was sie schon immer über Handles wissen wollte, aber nie zu fragen wagten (frei nach Woody Allen)
In diesm Tutorial soll es also um das Schreckgespenst Handles gehen. Aber wie wir sehr bald sehen werden ist das Schreckgespenst ein recht armloser, alter Hausgeist, der uns bei Zeiten recht nützlich werden kann. "Wo simmer denn dran? Aha, heute kriejen mer de Handles. Also, wat is en Handle? Da stelle wir uns janz dumm. Und da sage mer so: En Handle, dat is ene Zahl, die hat nen Datentyp und ne Funktion. Der Datenty ist vom Typ DWORD und de Funktion bekommen wer später." (So mehr können wir erst mal nicht aus Heinrich Spoerls "Feuerzangenbowle" raus holen, für den Rest müßte man Bill Gates bemühen .) Gut, ich denke, es ist klar geworden um was es hie rgehen soll: Handles und was man mit ihnen anstellen kann. Ich werde hier erst mal etwas Theorie machen und dann die Theorie in die Praxis umsetzten, damit man auch ein Ergebnis auf der Hand hat. 1. Theorie Um mit Handles um gehen zu können, muß man erst mal verstehen, wie Windows grundsätzlich funktioniert. Windows ist ein multitasking System, dass heißt, der Benutzer kann mehrere Programme gleichzeitig gestartet haben und mit ihnen arbeiten, zum Beispiel kann er wärend ein Grafikprogramm eine große Grafik bearbeitet mit einem Textverarbeitungsprogramm seinen ersten Roman fertigstellen. Dies Stellt gewisse Anforderungen an das System, zum einen was die Speicherverwaltung betrifft (darum wollen wir uns aber hier nicht kümmer) und zum anderen was die Kommunikation mit den Programmen angeht und das ist schon eher Gegenstand diese Tutorials. Kann in einem System immer nur ein Programm in den Speicher geladen werden, bedeutet das für die Kommunikation kein großes Problem. Nehem wir ein Textverarbeitungsprogramm unter DOS: Stellt es eine Anfrage, um eine Datei zu öffnen, leitet es die Anfrage an das System, welches die Anfrage verarbeitet und die gewünschten / benötigten Informationen zurückliefert. Da es nur ein aktives Programm gibt, hat das System kein Problem damit, von welchen Programm die Anfrage kommt bzw. an welches es die Informationen liefern muß. Unter einem multitasking System funktioniert das nun nicht mehr, da muß man sich was anderes einfallen lassen und das haben auch die Entwickler getan. Windows-Programme kommunizieren mit dem Betriebssystem und gegebenenfalls auch mit anderen Programmen über Nachrichten (Messages). Und der Rest funktioniert auch wie im wirklichen Leben mit der Post: Wenn ich einen Brief verschicke muß ich zu mindest dem Empfänger angeben und dieser sollte nach Möglichkeit eindeutig sein und nichts anderes ist ein Handle, ein eindeutiger Bezeichner für ein Fenster. Hier müssen wir uns von der Vorstellung lösen, dass nur es sich nur um das Hauptfenster um ein Fenster handelt. Windows versteht unter Fenster eigentlich alles, was sich auf unserem Programmfenster befindet, also Schaltflächen (Buttons), Texteingabefelder (Edits), Bezeichner (Lables) und alles andere, also auch ein Listview, Treeview oder eine Listbox usw. Und bei jedem Start eine Programmes vergibt Windows an alle Fenster des Programmes eindeutige Bezeichner und unter Windows nennt man diese Bezeichner - ihr dürft raten - genau, Handles. Windows verwaltet diese Handles in einer Tabelle und kann daher zu jedem Zeitpunkt ein Objekt in einem Programm genau identifizieren. Beispiel:
Code:
Ich will jetzt diese Win32-API Funktion nicht zerlegen. Nur so viel, sie schreibt den Inhalt eines Textfeldes in eine Variable mit den Namen buffer. Damit Windows jetzt weiß, von welchem Textfeld es den Text holen soll, muß als erster Parameter der Handle des Fensters angegeben werden. In unserem Fall handelt es sich um die Variable hEdit. Dieses Handle muß man sich vorher holen - wie auch immer - bzw. man kennt es schon von vorherigen Aktionen, beispielsweise, wenn man das Edit mit der Win32-API-Funktion CreateWindow erstellt hat liefert es den Handle zurück. Klevere Programmierer merken sich dieses Handle gleich in einer globalen Variable zu späteren Verwendungszwecken.
SendMessage(hEdit, WM_GETTEXT, sizeof(buffer), Integer(@buffer));
Ich denke, dies sollte genug Theorie sein und für das Grundverständnis reichen. Zusammen fassend kann man sich merken: Ein Handle ist ein eindeutiger Bezeichner für ein Fenster. Hat man erst mal dieses Handle kann man alles Windows mögliche damit, besser mit dem Fenster machen. 2. Praxis Eine beliebte Frage ist immer wieder: Wie kann ich ein bestimmtes Fenster minimieren oder schließen? Wer wissen will, wie beliebt diese Frage ist braucht nur mal die Suchfunktion des Forums zu bemühen und wird feststellen, dass regelmäßig alle 14 Tage diese Frage auftaucht. Dies ist auch ein gutes Beispiel, um mal zu demonstrieren, wie man mit Handles umgeht und was man mit ihnen machen kann. Weitaus besser eigent sich hierzu natürlich die nonVCl oder direkte Win32-Api Programmierung, da man da ohne Handles nicht sehr weit kommt, aber das will ich euch nicht auch noch zu muten, deswegen ein Beispiel mit VCL. Also was wollen wir machen: Wir wollen ein anderes Fenster schließen. (Wichtig: Wir wollen keinen Prozess beenden, denn ein Prozess kann mehrere Fenster haben und wir würden nur den Prozess beenden, wenn wir zufälligerweise das Hauptfenster erwischen.) Ein Fenster schließen wir mit der Windows-Nachricht WM_CLOSE. Diese müssen wir an das Fenster senden. Senden tuen wir eine Nachricht mit SendMessage(). SendMessage erwartet als ersten Paramter den Empfänger der Nachricht, also um es mal fachmännisch auszudrücken und wer alles bisher gelesen und verstanden hat, sollte dazu in der Lage sein, den Handle. Wenn wir den Handle haben, können wir unsere Nachricht an dan betreffend Fenster schicken und das stellt sich dann wie folgt dar:
Code:
hWnd ist in diesem Fall das gewünschte Handle.
SendMessage(hWnd, WM_CLOSE, 0, 0);
So jetzt wollen wir mal sehen, wie sich das programmtechnisch um setzen läßt. Dazu ein kleines Beispiel-Programm von mir: Wir benötigen einen Listview mit zwei Spalten: 1. Spalte: "Handle", 2. Spalte: "Fenstertext" und zwei Buttons Button1: "Handles holen", Button2: "Fenster schließen". Das war schon alles. Ich habe die Komponenten nicht umbenannt und stelle hier nur den für uns interessante Programmteil dar, nämlich die Prozeduren für die Buttons.
Code:
So das war eigentlich schon alles. Ich denke, ich muß dazu nicht mehr sagen, die Kommentare sollten für sich sprechen.
procedure TForm1.Button1Click(Sender: TObject);
var buffer: array[0..255] of Char; ListItem: TListItem; hWnd: DWORD; begin { Inhalt des Listviews löschen } Listview1.Clear; { mit diesem Handle starten wir } hWnd := Application.Handle; { Listitem hinzufügen } ListItem := Listview1.Items.Add; { Listviewitem mit Handle beschriften } ListItem.Caption := IntToStr(hWnd); { Fenstertext in das Subitem schreiben } ListItem.SubItems.Add(Form1.Caption); { alle Fenster durchgehen } while hWnd > 0 do begin { das Handle des nächsten Fensters in der Z-Order holen, unseres ist das } { oberste, da aktiv } hWnd := GetNextWindow(hWnd, GW_HWNDNEXT); { buffer leeren } ZeroMemory(@buffer, sizeof(buffer)); { Fenstertext holen, hier brauchen wir das Handle } GetWindowText(hWnd, buffer, sizeof(buffer)); { uns interessieren nur die Fenster mit Text und die sichtbar sind } if (buffer <> '') AND (IsWindowVisible(hWnd) = TRUE) then begin { Listitem hinzufügen } ListItem := Listview1.Items.Add; { mit Handle beschriften } ListItem.Caption := IntToStr(hWnd); { Und den Fenstertext in das Subitem schreiben } ListItem.SubItems.Add(buffer); end; end; end; procedure TForm1.Button2Click(Sender: TObject); var hWnd: DWORD; begin { Handle auslesen } hWnd := StrToInt(Listview1.Selected.Caption); { Nachricht schicken } SendMessage(hWnd, WM_CLOSE, 0, 0); { Listview aktualisieren } Form1.Button1Click(Form1); end; Ich hoffe, ich konnte hier ein paar Unklarheiten beseitigen und euch die Angst vor diese doch so geheimnisvollen Handles nehmen. Nur weil es sich um ziemlich systeminterne Dinge handelt braucht man keine Angst davor zu haben. Man sollte allerdings genau wissen, was man tut. Aber alles in allem, kann man wohl sagen, dass alles nur halb so wild ist. Auch Windows kocht nur mit Wasser, um mal meinen alten Fußballtrainer zu zitiern. Also Jungs und Mädels viel Spaß beim Programmieren, Ball flach halten und immer dran denken: "Und immer eine Hand voll Handles." (<- stammt glaube ich von unserem NIcoDE) Das Tutorial dürfte fürs erste von meiner Seite als abgeschlossen betrachtet werden. Wer Ergänzungen hat oder meint ich hätte hier den totalen Schrott erzählt, kann ja posten.
Ein Teil meines Codes würde euch verunsichern.
|
Delphi 2006 Professional |
#2
Auch HDC's sind nur Handles
Dem ein oder anderen von euch ist bestimmt schon mal das Buchstabenkürzel DC oder HDC oder der Begriff Device Context untergekommen und hat sich dann gefragt, was es damit auf sich hat. Nun da das Hauptthema dieses Kurses Handels sind und wie man schon an der Überschrift erkennen kann, handlet es sich bei einem Device Context auch nur um einen Handle. Was ist nun ein Device Context (DC)? Na ja, da fragen wir doch mal die, die es wissen müßten oder sollten: die Leute von Microsoft:
Zitat:
Device Contexts
A device context is a structure that defines a set of graphic objects and their associated attributes, as well as the graphic modes that affect output. [...] Also ein DC hat was mit zeichnen zu tun. Und was soll das Gefasel von einer Struktur? In dieser Struktur sind einfach die Eigenschaften von diversen Zeichenoperationen festgelegt. Die man ändern kann aber nicht muß, da die Struktur mit vordefinierten Weten besetzt wird. In den meisten Fällen wird man sie aber doch ändern, da die standard Werte meist unbrauchbar sind. Und ich will noch mal das PSDK bemühen:
Zitat:
About Device Contexts
Device independence is one of the chief features of Microsoft® Windows®. Win32®-based applications can draw and print output on a variety of devices. The software that supports this device independence is contained in two dynamic-link libraries. The first, Gdi.dll, is referred to as the graphics device interface (GDI); the second is referred to as a device driver. The name of the second depends on the device where the application draws output. For example, if the application draws output in the client area of its window on a VGA display, this library is Vga.dll; if the application prints output on an Epson FX-80 printer, this library is Epson9.dll. "Ein Hauptmerkmal von Windows ist die Geräteunabhängigkeit. Windows-Anwendungen können ihre Ausgabe auf vielen verschiedenen Geräten stattfinden lassen. Die benötigten Funktionen werden von der GDI.dll (GDI: grafic device interface) bzw von speziellen Gerätetreibern, wie Druckertreiber dll's bereitgestellt." Soll so viel heißen wie: Wenn ich mit Rectangle() ein Rechteck auf den Bildschirm zeichne, kann ich mit der selben Funktion auch das Rechteck auf den Drucker zeichnen oder sonst wohin schmieren. Und noch mal das PSDK, weil es so schön funktioniert:
Zitat:
An application must inform GDI to load a particular device driver and, once the driver is loaded, to prepare the device for drawing operations (such as selecting a line color and width, a brush pattern and color, a font typeface, a clipping region, and so on). These tasks are accomplished by creating and maintaining a device context (DC).
Man merke sich: Aus den ertenen Teil wissen wir, dass ein Handle nichts weiter als eine Kennziffer ist, die Windows intern als Querverweis auf Objekte benutzt. Ein Handle für einen Gerätekontext stellt so zusagen den Schlüssel eines Programmes für sämtliche GDI-Funktionen dar. Wie funktioniert das nun mit den DC's? Nun, wenn wir ein Handle zu einem entsprechenden Gerätekontext anfordern, reagiert Windows auf diese Anforderung mit der Initialisierung einer internen Datenstruktur, die erst mal mit standard Werten besetzt wird und einem Handle, welches den Zugriff auf diese Struktur für diese Gerät ermöglicht. Wichtig: An dieser Stelle sei noch angemerkt, dass wann immer man einen DC anfordert diesen auch wieder freizugeben hat, wenn man mit im fertig ist. Die Praxis Da ich ja weiß, was ihr hier für böse Buben und Mädels seit und nur blödsinn im Kopf habt ) gleich mal was, um euren Spieltrieb zu befriedigen und was, wo ihr so richtig böse sein könnt, denn was ich in dem folgenden kleinen Programm demonstriere macht man aus Höflichkeit normalerweise nicht - nämlich in fremden Fenster rumschmieren. Normaler weise kommt man, wenn man mit der VCl programmiert recht gut ohne DC aus, wenn man auf dem eigenen Fenster rumpinseln will. Einfach
Code:
Den ganzen DC-Kram erledigt hier das Canvas-Objekt von der Form. Was aber nun wenn ich wo ganz anders rumschmieren will, als auf dem Fenster meiner Anwedung? Wenn wir so etwas links oder rechts an unserem Anwendungsfenster vorbei schielen, kann man doch ganz schnell auf dumme Gedanken kommen... Und man stellt sich die Frage: "Kann ich nicht irgend wie auf den Desktop malen?" Antwort: "Kann man. Und in genau diesem Fall braucht man den DC vom Desktop.
Form1.Canvas.Textout();
Das Beispiel-Programm besteht einfach nur aus zwei Buttons, die ich auch nicht umbannt habe. Hier nur der Implementaion-teil des Programmes:
Code:
Die GDI-Funktionen will ich hier jetzt nicht näher erläutern, die könnt ihr in der Windows SDK Hilfe von Delphi nachschlagen, es ging mir hier halt nur um den DC. Und die Kommentare sollten wieder für sich sprechen
procedure TForm1.Button1Click(Sender: TObject);
var DC: HDC; // Handle für Device Context Brush: HBRUSH; // Handle für Brush Pen: HPEN; // Handle für Zeichenstift s: String; // variable für den Text begin { Device Context holen, 0 für den De3sktop, Application.Handle für eigene Anwendung} DC := GetDC(0); { Frabe wählen } Brush := CreateSolidBrush(RGB(255,0,0)); { in DC einsetzen } SelectObject(DC, Brush); { Zeichenstift erzeugen } Pen := CreatePen(PS_SOLID, 3, RGB(75,0,255)); { in DC einsetzen } SelectObject(DC, Pen); { Rechteck zeichnen } Rectangle(DC, 10, 10, 375, 100); s := 'In fremden Fenstern rumschmieren macht man nicht.'; { Hintergrundmodus auf transparent setzen, sonst hat der Text einen weißen Hintergrund } SetBkMode(DC, Transparent); { Text ausgeben } TextOut(DC, 20, 50, PChar(s), length(s));; { und aufräumen, DC wieder freigeben } ReleaseDC(0, DC); end; procedure TForm1.Button2Click(Sender: TObject); begin { Rechteck für ungültig erklären: 0 für Desktop, nil für gesamtes Fenster, TRUE um den Hintergrund neu zu zeichnen } InvalidateRect(0, nil, TRUE); end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin InvalidateRect(0, nil, TRUE); end; So, ich hoffe, ich habe euch wieder mal etwas über die Interna von Windows aufklären können und euch die interne Funktionsweise von Windows etwas näher bringen können. Viel Spaß mit den DC wünscht euch noch euer Luckie. Bis zum nächsten mal in diesen Lichtspieltheater.
Michael
|
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |