Zugriff auf Steuerelemente der CommonControls32

Abstract

Dieser Artikel - oder kleines Tutorial – soll zeigen, wie man auf ein Steuerelement aus den CommonControl32 zugreifen kann.

Geschichtlicher Hintergrund

Unter 16-Bit Windows (Windows 3.1 ff.) waren die Speicherbereiche der einzelnen Prozesses nicht von einander getrennt. Das heißt, der Zugriff auf einen Speicherbereich eines anderen Prozess war Problemlos möglich. Somit konnte man mittels SendMessage und WM_GETTEXT den Inhalt eines Texteingabesteuerelementes auslesen, da der zurückgelieferte Zeiger auf die Zeichenkette im Speicher in beiden Prozesses gültig war.

Unter 32-Bit Windows (Windows 95 ff., Windows NT ff.) wurden die Speicherbereich der einzelnen Prozesses strikt von einander getrennt, so dass ein direkter Zugriff nicht mehr möglich war. Dies hat natürlich die Systemstabilität wesentlich erhöht, da andere Prozesse, darunter auch die Systemprozesse, nicht mehr durch Fehler in einem Prozess zum Absturz gebracht werden konnten. Allerdings hatte dies auch zur Folge, dass Nachrichten und Funktionen, die Zeiger zurücklieferten nicht mehr funktionieren konnten, da die Adresse natürlich keine Gültigkeit mehr hatte

Viele Programme haben allerdings die Möglichkeit genutzt auf den Speicher anderer Prozesse zugreifen zu können. Da Microsoft immer sehr darauf bedacht ist eine Abwärtskompatibilität zu gewährleisten, hat Microsoft in seinen 32-Bit Betriebssystemen diese Möglichkeit erhalten, in dem bestimmte Nachrichten in einem Speicherbereich gemappt werden, auf den beide Prozesse, der Quellprozess und der Zielprozess, Zugriff haben, so das ein Zeiger in beiden Adressräumen gültig ist. Bei diesen Nachrichten handelt es sich, um Nachrichten, die auf 16-Bit Steuerelemente angewandt werden, wie zum Beispiel Texteingabefelder, Listenfelder usw

Unter 32-Bit Windows sind neue Steuerelemente dazu gekommen, unter anderem die Listenansicht (Listview), eine Baumansicht (Treeview), eine Statuszeile usw. Da bei diesen neuen Steuerelementen nicht die Notwendigkeit bestand aus Kompatibilitätsgründen einen direkten Zugriff zu ermöglichen, kann man auf eben diese Steuerelemente nicht so ohne weiteres zugreifen.

Dieser Artikel - oder kleines Tutorial – soll zeigen, wie man trotzdem auf so ein Steuerelement aus den CommonControls32 zugreifen kann.

Das Prinzip

Da, wie schon erwähnt, unter 32-Bit Windows jeder Prozess seinen eigenen Adressraum besitzt haben natürlich Zeiger nur in diesem ihre Gültigkeit. Ein SendMessage mit WM_GETTEXT liefert also einen Zeiger auf einen Adressbereich im Zielprozess, da im Quellprozess diese Adresse ungültig ist, führt diese Vorgehensweise also nicht zum Ziel. Es muss also der Adressbereich, auf den der Zeiger im Zielprozess zeigt in den Adressraum des Quellprozesses kopiert werden. Dazu sind drei Schritte nötig.

  1. Speicher im Zielprozess und Quellprozess reservieren
  2. Speicher im Zielprozess mit den gewünschten Informationen füllen
  3. Speicherbereich aus dem Zielprozess in den Speicherbereich des Quellprozess kopieren

Beispiel – Auslesen eines Listviews

Anhand eines Listviews soll die oben dargestellte Vorgehensweise beispielhaft demonstriert werden. Der Einfachheit halber benutze ich für mein Demo-Programm den Listview des Desktops und lesen dessen Icon-Beschriftungen aus.

Als erstes reservieren wir Speicher für die Listview-Item-Struktur im Quell- und Zielprozess:

Size := SizeOf(TLvItemBuffer);
MemLocal := VirtualAlloc(nil, Size, MEM_COMMIT, PAGE_READWRITE);
MemRemote := VirtualAllocEx(Process, nil, Size, MEM_COMMIT, PAGE_READWRITE);

Im Quellprozess können wir dies mit VirtualAlloc [1] tun. Für den Zielprozess benötigen wir VirtualAllocEx [2]. Allerdings steht diese Funktion erst ab Windows 2000 zur Verfügung. Soll das Programm auch zu älteren Windows Versionen kompatible sein muss man etwas tricksen. Nico Bendlin hat sich die Mühe gemacht und für mein Programm LuckieDIPS [3] (die Quellcodes in Delphi liegen dem Archiv bei) eine entsprechende Funktion geschrieben.

Als nächstes iterieren wir durch die Anzahl der Icons...

// Anzahl der Symbole ermitteln und in einer Schleife durchlaufen
IconCount := ListView_GetItemCount(hDesktopLV);
for i := 0 to IconCount - 1 do

... reservieren den Speicherbereich in beiden Prozessen und initialisieren die Listview-Item-Struktur...

ZeroMemory(MemLocal, SizeOf(TLvItemBuffer));
with MemLocal^ do
begin
  LVItem32.mask := LVIF_TEXT;
  LVItem32.iItem := i;
  LVItem32.pszText := Cardinal(MemRemote) + ItemBufferBlockSize;
  LVItem32.cchTextMax := High(MemLocal.ItemText) + 1;
end;

... kopieren die Struktur mit WriteProcessMemory [4] in den Zielprozess, füllen sie mit den gewünschten Informationen (hier mit SendMessage und LVM_GETITEM) und kopieren den Speicherbereich mit ReadProcessMemory [5] wieder zurück in unseren Adressraum:

if WriteProcessMemory(Process, MemRemote, MemLocal, Size, NumBytes)
  and
  Boolean(SendMessage(hDesktopLV, LVM_GETITEM, 0, LPARAM(MemRemote)))
  and
  ReadProcessMemory(Process, MemRemote, MemLocal, Size, NumBytes)
  then

Anschließend rufen wir unsere Callback-Funktion auf, um den Icontext auszugeben:

Callback(string(MemLocal^.ItemText));

Das vollständige Beispiel findet sich in der beilignden Demoanwendung.

Downloads

ComCtrl32_Access_Demo.zip Wednesday, 29-Dec-2010 23:45:07 CET 11K
2010-12-29T23:44:36 +0100, mail+homepage[at]michael-puff.de