Entwickler-Ecke

Windows API - Bezeichnungen der Desktopicons


maxk - Mo 11.08.03 12:17
Titel: Bezeichnungen der Desktopicons
Hi,
ich lese mit folgendem Code die Bezeichnung des ersten Icons auf dem Desktop aus:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
uses CommCtrl;

procedure TForm1.Button1Click(Sender: TObject);

 function GetDskLVH: THandle;
 var s: string;
 begin
   Result := FindWindow('ProgMan', nil);
   Result := GetWindow(Result, GW_CHILD);
   Result := GetWindow(Result, GW_CHILD);
   SetLength(S, 40);
   GetClassName(Result, PChar(S), 39);
   if PChar(s) <> 'SysListView32' then begin
     MessageBox(0, 'Fehler bei Ermittlung des Desktophandles!', 'Fehler', 0);
     Result := 0;
   end;
 end;

var dwSize,dwNumberOfBytes,PID,hProcess:Cardinal;
    PLocalShared,PSysShared:PlvItem;
begin
  dwSize := SizeOf(LV_ITEM) + SizeOf(CHAR) * MAX_PATH;
  pLocalShared := VirtualAlloc(nil, dwSize, MEM_RESERVE + MEM_COMMIT,
    PAGE_READWRITE);
  GetWindowThreadProcessID(GetDskLVH, @PID);
  hProcess := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or
    PROCESS_VM_WRITE, FALSE, PID);
  pSysShared := VirtualAllocEx(hProcess, nil, dwSize, MEM_RESERVE or MEM_COMMIT,
    PAGE_READWRITE);
  pLocalShared.mask := LVIF_TEXT;
  pLocalShared.iItem := 0;
  pLocalShared.iSubItem := 0;
  pLocalShared.pszText := LPTSTR(DWord(pSysShared) + SizeOf(LV_ITEM));
  pLocalShared.cchTextMax := 100;
  WriteProcessMemory(hProcess, pSysShared, pLocalShared, 1024,
    dwNumberOfBytes);
  SendMessage(GetDskLVH, LVM_GETITEM, 0, LPARAM(pSysShared));
  ReadProcessMemory(hProcess, pSysShared, pLocalShared, 1024,
    dwNumberOfBytes);

  Caption:=PChar(DWord(pLocalShared) + SizeOf(LV_ITEM));

  VirtualFree(pLocalShared, 0, MEM_RELEASE);
  VirtualFreeEx(hProcess, pSysShared, 0, MEM_RELEASE);
  CloseHandle(hProcess);
end;
// Copyright SmR 2002


Das funktioniert auch ganz gut, nur ist mir der Code zu kompliziert.
Geht es nicht auch einfacher?


AndyB - Mo 11.08.03 12:43
Titel: Re: Bezeichnungen der Desktopicons
Zitat:
Geht es nicht auch einfacher?

Das findest du kompliziert? Dann hast du noch nicht gesehen, was es alles für Dinge gibt, die wirklich kompilziert sind.

Kleine Verbesserung: Den eigenen Speicher brauchst du nicht über VirtualAlloc reservieren, das kann auch eine lokale Zeigervariable mit GetMem/FreeMem erledigen.


Motzi - Mo 11.08.03 12:45

Leider nicht, da Steuerelemnt-Botschaften nicht Prozessübergreifend funktonieren. Bei zB WM_GETTEXT mappt Windows im Hintergrund ziemlich herum. Hier musst du das eben selbst machen indem du per VirtualAllocEx Speicher im fremden Prozess reservierst in dem dann die Daten abgelegt werden, damit du sie dann per ReadProcessMemory auslesen kannst.

Eine andere Möglichkeit (aber nicht unbedingt einfachere) wäre das ganze über eine Inject-Dll zu lösen...


Motzi - Mo 11.08.03 12:46
Titel: Re: Bezeichnungen der Desktopicons
AndyB hat folgendes geschrieben:
Zitat:
Geht es nicht auch einfacher?

Das findest du kompliziert? Dann hast du noch nicht gesehen, was es alles für Dinge gibt, die wirklich kompilziert sind.

Jeder hat einmal klein angefangen... ;)


maxk - Mo 11.08.03 20:14
Titel: Re: Bezeichnungen der Desktopicons
Motzi hat folgendes geschrieben:
Jeder hat einmal klein angefangen...
:shock: Danke für die Verteidigung - aber ich bin kein Anfänger :twisted: Ich würde mich selbst schon eher als Fortgeschrittener bezeichnen. Die Frage richtete sich nur darum, ob es wirklich nötig ist, so im Speicher rumzusauen...

Windows ist noch ein Anfänger und evtl. nicht in der Lage korrekt mit dem Code zu arbeiten :mrgreen:


Motzi - Di 12.08.03 13:29

Ja, es ist nötig.. das warum hab ich oben bereits beschrieben! :roll:


RFZ - Di 12.08.03 14:11

schlagt mich, wenn ich schmarrn erzähle, aber kannst du nich einfach schaun welche Dateien in "c:\windows\desktop" (oder hald bei NT/XP im Userverzeichnis) liegen?
Vielleicht hab ich auch net ganz verstanden was du willst ;)

cu RFZ


AndyB - Di 12.08.03 16:27

Das wäre doch viel zu einfach. 8)


maxk - Di 12.08.03 21:30

:oops: Erwischt, das Programm wird später auch die Position der Icons auslesen. (Es soll ein alternativer WindowsManager werden).
:? Ich weiß, es gibt schon hundert davon, aber es ist immer schon, etwas eigenes zu haben...


Delete - Fr 15.08.03 00:44

Einfacher geht es nicht, wenn man es richtig macht, wird es nur komplizierter (für dich). BTW liest er auch gleich die Position aus.

Den Source haben Nico (90%) und ich (10%) :roll: gestern überarbeitet und hauptsächlich alle möglichen Fehlerquellen abgefangen, Datentypen korrigiert und lesbarer gemacht.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
uses CommCtrl;

////////////////////////////////////////////////////////////////////////////////
// Durch die Verwendung von VirtualAllocEx() funktioniert dieser Code nur auf NT

procedure TForm1.Button1Click(Sender: TObject);
  // Diese Funktion ist zwar einfach, aber nicht sicher genug (WinXP)
  function GetDesktopListView(): HWND;
  var
    ClassName: string;
  begin
    Result := FindWindow('ProgMan', nil);
    Result := GetWindow(Result, GW_CHILD);
    Result := GetWindow(Result, GW_CHILD);
    SetLength(ClassName, 40);
    SetLength(ClassName, GetClassName(Result, PChar(ClassName), 39));
    if (ClassName <> 'SysListView32') then
    begin
      MessageBox(Self.Handle, 'Fehler bei Ermittlung des Desktophandles!',
        'Fehler', MB_ICONERROR or MB_OK);
      Result := 0;
    end;
  end;
var
  ListView: HWND;
  ProcessId: DWORD;
  Process: THandle;
  Size: Cardinal;      // SIZE_T
  MemLocal: Pointer;
  MemRemote: Pointer;
  NumBytes: Cardinal;  // SIZE_T
  IconIndex: Integer;
  IconLabel: string;
  IconPos: TPoint;
begin
  // Fensterhandle des Desktop-ListView ermitteln und Prozess oeffnen
  ProcessId := 0;
  ListView := GetDesktopListView();
  GetWindowThreadProcessId(ListView, @ProcessId);
  Process := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or
    PROCESS_VM_WRITE, False, ProcessId);
  if (Process <> 0) then
  try
    // Lokalen und entfernten (im Zielprozess) Puffer anlegen
    Size := SizeOf(TLVItem) + SizeOf(Char) * MAX_PATH + 1;
    MemLocal := VirtualAlloc(nil, Size, MEM_RESERVE or MEM_COMMIT,
      PAGE_READWRITE);
    MemRemote := VirtualAllocEx(Process, nil, Size, MEM_RESERVE or MEM_COMMIT,
      PAGE_READWRITE);
    if Assigned(MemLocal) and Assigned(MemRemote) then
    try
      // Anzahl der Symbole ermitteln und in einer Schleife durchlaufen
      for IconIndex := 0 to SendMessage(ListView, LVM_GETITEMCOUNT, 0, 0) - 1 do
      begin
        // Symboltext auslesen
        // (es gibt zwei identische Strukturen, jeweils eine in diesem und eine
        //  im Zielprozess. Wobei die Daten zwischen den Puffern hin und her
        //  kopiert werden muessen. Dieser Aufwand ist noetig, da LVM_GETITEM
        //  eine Struktur liest und schreibt, die sich im Adressraum des
        //  Prozesses befindet, dem das entsprechende Fenster gehoert...)
        ZeroMemory(MemLocal, SizeOf(TLVItem));
        with PLVItem(MemLocal)^ do
        begin
          mask := LVIF_TEXT;
          iItem := IconIndex;
          // Der Puffer fuer den Text liegt direkt hinter der TLVItem-Struktur
          pszText := LPTSTR(Cardinal(MemRemote) + Cardinal(SizeOf(TLVItem)));
          cchTextMax := MAX_PATH;
        end;
        NumBytes := 0;
        if WriteProcessMemory(Process, MemRemote, MemLocal, Size, NumBytes) and
          Boolean(SendMessage(ListView, LVM_GETITEM, 0, LPARAM(MemRemote))) and
          ReadProcessMemory(Process, MemRemote, MemLocal, Size, NumBytes) then
        begin
          IconLabel := string(
            PChar(Cardinal(MemLocal) + Cardinal(SizeOf(TLVItem))));
          // Position auslesen
          // (-1, -1 ist nur ein Indiz fuer einen Fehlschlag, da diese Position
          //  natuerlich moeglich ist...)
          IconPos.X := -1;
          IconPos.Y := -1;
          if Boolean(SendMessage(ListView, LVM_GETITEMPOSITION, IconIndex,
            LPARAM(MemRemote))) and ReadProcessMemory(Process, MemRemote,
            MemLocal, Size, NumBytes) then
          begin
            IconPos := PPoint(MemLocal)^;
          end;
          // Anzeige ;)
          Memo1.Lines.Add(IconLabel +
            ' [' + IntToStr(IconPos.X) + ', ' + IntToStr(IconPos.Y) + ']');
        end;
      end;
    except
      // Exceptions ignorieren
    end;
    // Aufraeumen
    if Assigned(MemRemote) then
      VirtualFreeEx(Process, MemRemote, 0, MEM_RELEASE);
    if Assigned(MemLocal) then
      VirtualFree(MemLocal, 0, MEM_RELEASE);
  finally
    CloseHandle(Process);
  end;
end;


Bitte unbedingt die Kommentare beachten!!!


AndyB - Fr 15.08.03 00:49

Wobei ich es immer noch nicht verstehe, warum der eigene Speicher über
Zitat:
MemLocal := VirtualAlloc(nil, Size, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);

reserviert wird. Ein einfaches: GetMem(MemLocal, Size); hätte es auch getan.


Delete - Fr 15.08.03 00:54

Ist lesbarer denke ich. Da deutlich wird, das local das gleiche passiert wie remote. Eventuell hat es auch noch was mit ReadProcessMemory zu tun.


AndyB - Fr 15.08.03 00:57

Luckie hat folgendes geschrieben:
Ist lesbarer denke ich.

Das finde ich ganz und gar nicht.

Zitat:
Eventuell hat es auch noch was mit ReadProcessMemory zu tun.

Damit hat es überhaupt nichts zu tun. Wäre das der Fall, so würden einige meiner Funktionen nicht funktionieren. Tun sie aber, auf Win95-ME/WinNT-XP Systemen.


maxk - Fr 15.08.03 19:34

Luckie hat folgendes geschrieben:
Einfacher geht es nicht, wenn man es richtig macht, wird es nur komplizierter (für dich). BTW liest er auch gleich die Position aus.
Danke! Ich hatte eigentlich vor, den Code von SmR zu nutzen, aber der gefällt mir besser :)


Delete - Sa 16.08.03 02:07

Bitte, aber wenn du unbedingt einen Code mit solchen Schwachstellen verwenden willst, tu es, aber wunder dich nicht, wenn er dir mal um die Ohren fliegt.


Delete - Sa 16.08.03 15:37

Ups, sorry. Da habe ich was falsch verstanden, du willst Nico's Code nehmen oder?


maxk - Sa 16.08.03 19:57

Luckie hat folgendes geschrieben:
Ups, sorry. Da habe ich was falsch verstanden, du willst Nico's Code nehmen oder?
Jepp :D


Delete - Sa 16.08.03 20:20

Das beruhigt ungemein. :?


r_o_M_a_n - Sa 28.02.04 21:36

Gibt es auch eine Möglichkeit, die Handles der Icons auszulesen, um sie mit ShowWindow(Handle,SW_HIDE); auszublenden? Sind Icons überhaupt Child-Windows des Desktop-Listviews?


Delete - Sa 28.02.04 21:44

Nein. Der Desktop ist nur ein SyListview in der Icon-Ansicht.