Vous êtes sur la page 1sur 2

delphi4-5_04 12.08.

2004 10:27 Uhr Seite 20

Delphi

Michael Starke

Plug&Play-Botschaften in Delphi
Immer wieder taucht die Frage auf, wie man den Wechsel von Plug&Play-Geräten in Windows
möglichst effizient und schnell erkennt. Dazu sollte man auf die Botschaften hören, die einem das
Betriebssystem zusendet. Denn bei einer Plug&Play-Aktion erhält man jedes Mal eine Botschaft
des Typs WM_DEVICECHANGE.

Für Geräte, die Funktionen wie den Auswurf (wie zum Bei-
spiel bei CDs) oder Sperrung anbieten, sendet das Be-
triebssystem normalerweise den Typ DBT_DEVICERE-
MOVEPENDING an die Anwendungen und den Geräte-
treiber, um ihnen ihre jeweiligen Aufgaben mitzuteilen.
Sollte der Anwender allerdings ein Gerät entfernen (für das
Betriebssystem unerwartet), wird in der Regel vor der Ak-
tion keine Botschaft mit dem Typ DBT_DEVICE-
QUERYREMOVE gesandt.
Das ist zum Beispiel der Fall, wenn der Benutzer das Gerät
ohne vorherige Anfrage physikalisch aus dem Rechner ent-
fernt. Die verwendete Struktur der DBT_-Funktionen ist
wie folgt:
Bild 1: Der Plug&Play-Manager zeichnet den
Botschaftsverkehr auf
TYPE
DEV_BROADCAST_HDR = RECORD
dbch_size : LongInt;
Jede Anwendung und auch jedes Fenster einer Anwendung dbch_devicetype : LongInt;
erhält bei einer Plug&Play-Aktion die Botschaft WM_DE- dbch_reserved : LongInt
VICECHANGE. END;
Mit Hilfe der Nachricht läßt sich deshalb feststellen, ob
sich die Gerätekonfiguration geändert hat. Über den wPa-
ram (siehe TMsg) werden die Details der Nachricht ge- In dbch_size wird wie bei solchen Strukturen üblich, des-
kennzeichnet (siehe Tabelle 1). Optional können über den sen Größe angegeben, dbch_devicetype enthält die Gerä-
Parameter lParam Zeiger zu Datenstrukturen übergeben teart, wobei die Kennungen 0 bis 2 möglich sind. 0 steht
werden. Nicht jedes Detail übergibt eine solche Struktur. dabei für ein OEM/IHV-Gerät, 1 für ein Logisches Volume
Nur die Kennzeichen DBT_DEVICEARRIVAL, und 2 für ein Portgerät. dbch_reserved ist, wie der Name
DBT_DEVICEQUERYREMOVE, DBT_DEVICE- schon beschreibt, ein reservierter Wert.
QUERYREMOVEFAILED, DBT_DEVICEREMOVE- Er ist immer 0. Mit etwas Glück erhält man sogar noch zu-
COMPLETE, DBT_DEVICEREMOVEPENDING und sätzliche Informationen.
DBT_DEVICETYPESPECIFIC enthalten eine Strukturin- Denn die Nachricht übermittelt zusätzlich ein Flag zur ge-
formation, die aber bei allen vom selben Typ ist. naueren Kennzeichnung des Ereignisses.

Parameter Beschreibung
DBT_CONFIGCHANGECANCELED Anfrage für Konfigurationswechsel gescheitert.
DBT_CONFIGCHANGED Aktuelle Konfiguration wurde geändert.
DBT_DEVICEARRIVAL Ein Gerät wurde eingesetzt und ist nun verfügbar.
DBT_DEVICEQUERYREMOVE Anwendung erhält Anfrage, ob ein Gerät entfernt werden darf.
DBT_DEVICEQUERYREMOVEFAILED Anfrage für ein Entfernen eines Geräts wurde abgelehnt.
DBT_DEVICEREMOVECOMPLETE Ein Gerät wurde entfernt.
DBT_DEVICEREMOVEPENDING Ein Gerät wird entfernt.
DBT_DEVICETYPESPECIFIC Gerätespezifisches Ereignis wurde ausgelöst.
DBT_QUERYCHANGECONFIG Anfrage auf Erlaubnis zum Entfernen eines Geräts.
DBT_USERDEFINED Benutzerdefiniert.
Tabelle 1: Die verschiedenen Parameter der Botschaft WM_DEVICECHANGE

20 Toolbox • 5/2004
delphi4-5_04 12.08.2004 10:27 Uhr Seite 21

Delphi

Auf Empfang stellen Empfang als Ereignis


Doch zum Empfang dieser Nachricht muß man die Nach- Die Methode selbst muß nun abfragen, ob eine entspre-
richt auch erst einmal entgegennehmen. Dazu dient das Er- chende Nachricht geschickt wurde. Gleichzeitig wird eine
eignis OnMessage der Anwendungsinstanz Application: Komponente des Typs TProgressbar entsprechend gesetzt,
um den generellen Verkehr zu kennzeichnen:

PROPERTY TApplication.OnMessage: TMessageEvent;


PROCEDURE TForm1.DoOnMessage(VAR Msg: TMsg;
TYPE VAR Handled: BOOLEAN);
TMessageEvent = Procedure( VAR
VAR Msg: TMsg; aPChar: ARRAY[0..999] OF CHAR;
VAR Handled: BOOLEAN) OF Object; i : INTEGER;
TMsg = Packed RECORD BEGIN
hwnd : HWND; IF (Msg.message = WM_DEVICECHANGE) THEN BEGIN
message : UINT; FillChar(aPChar, SizeOf(aPChar), #0);
wParam : WPARAM; GetWindowText(Msg.hwnd, aPChar, Sizeof(aPChar));
lParam : LPARAM; ListBox1.Items.Add(DateTimeToStr(Now)
time : DWord; + ' Gerätewechsel erkannt.');
pt : TPoint; Self.ListBox1.Items.Add('--- '
END; + GetWMDeviceChangeMsg(Msg.wParam));
Self.ListBox1.Items.Add('--- Empfänger: ' + aPChar);
Handled := TRUE;
Die Methode enthält also einen Parameter Msg des Typs END;
TMsg und einen Parameter Handled des Typs Boolean. Die i := Self.ProgressBar1.Position;
Bedeutung der Felder von TMsg ist in Tabelle 2 aufgeführt. Inc(i);
Der Parameter Handled gibt die Möglichkeit, den Emp- Self.ProgressBar1.Position := i;
fang der Botschaft zu bestätigen, damit diese Nachricht als END;
abgearbeitet gilt. Empfängt man also eine entsprechende
Botschaft, setzt man den Wert von Handled auf True.
Die Definition des Ereignisses besagt, daß eine entspre- Hierbei wird aus dem Parameter Msg.hwnd der Name des
Empfängerfensters extrahiert. Man sieht im Ergebnis, daß
man diese Nachricht innerhalb eines Ereignisses mehrfach
Feld Bedeutung und jeweils für die Anwendung und für alle Fenster der An-
hwnd Handle des Empfängers. wendung erhält.
message Botschaftsbezeichner.
wParam Optionaler Hauptparameter der Botschaft.
Detail einer Nachricht
lParam Optionaler Nebenparameter der Botschaft.
time Sendezeitpunkt. Danach wird der Parameter der Nachricht aufgelöst, um
weitere Details zu ermitteln. Abschließend wird diese Bot-
pt Position der Maus zum Zeitpunkt des Sendestarts.
schaft als abgearbeitet gekennzeichnet. Allein dieser Quell-
Tabelle 2: Die Bedeutung der Botschaftsstruktur TMsg text reicht aber nicht aus, um die Nachricht zu empfangen.
Man muß sich noch mit der Instanz Application verbinden:

chende Methode (man beachte das Schlüsselwort Of Ob-


ject) in einer der Klassen definiert werden muß, um an das PROCEDURE TForm1.DoOnCreate(Sender: TObject);
Ereignis zu binden. Dazu wählt man am besten die Klasse BEGIN
des Fensters: Application.OnMessage := Self.DoOnMessage;
END;

TForm1 = CLASS(TForm)
ListBox1: TListBox; Mit Hilfe dieser Codezeilen ist man jetzt in der Lage, ab-
Panel1: TPanel; zufangen, wenn ein Gerät eingesetzt oder herausgenom-
Button1: TButton; men wird, was unter Umständen sehr wichtig sein kann.
ProgressBar1: TProgressBar; Man denke daran, daß man eine PCMCIA-UMTS-Steck-
Label1: TLabel; karte einstecken und dann als Modem nutzen kann. Mit
PROCEDURE DoOnCreate(Sender: TObject); Hilfe der Botschaft läßt sich nachprüfen, ob das Modem
PROCEDURE ExitApp(Sender: TObject); vorhanden ist beziehungsweise ob es herausgenommen
PUBLIC wurde.
PROCEDURE DoOnMessage(VAR Msg: TMsg; Das ist von Vorteil, denn fragt man ohne Verdacht ständig
VAR Handled: boolean); den Gerätemanager ab, treibt man die CPU-Last unnötig in
END; die Höhe.

Toolbox • 5/2004 21