Entwickler-Ecke

Windows API - Caller einer DLL ermitteln


DerNetteNachbar - Do 18.12.08 13:27
Titel: Caller einer DLL ermitteln
Hallo erstmal, ich würde ganz gerne in einer DLL den Aufrufer bestimmen, sprich die Anwendung die meine DLL aufgerufen hat. Gibt es dafür bereits eine API Funktion oder muss man sich da was basteln?

MfG

Nachbar


jaenicke - Do 18.12.08 14:00

Natürlich gibt es die, GetModuleFileNameEx:
http://msdn.microsoft.com/en-us/library/ms683198(VS.85).aspx


DerNetteNachbar - Do 18.12.08 14:11

Ich würde aber ganz gerne das Handle der Anwendung bekommen, bzw. das Handle vom TOP Fenster der Anwendung


jaenicke - Do 18.12.08 14:27

Dann ist vermutlich das hier das richtige, GetCurrentProcessId:
http://msdn.microsoft.com/en-us/library/ms683180(VS.85).aspx

Für welches Programm soll die DLL denn sein? Ich meine, normalerweise werden doch alle nötigen Informationen direkt an ein Plugin übergeben (wenn es ein Plugin für ein anderes Programm ist). Da scheint die Schnittstelle zwischen Anwendung und DLL nicht so ganz passend zu sein, aber wenn das nicht dein eigenes Programm ist, für das die DLL ist, dann kannst du da natürlich nichts dran ändern.


DerNetteNachbar - Do 18.12.08 15:19

Und wie komme ich von der ProzessID jetzet auf das Handle des TOP Fensters.


jaenicke - Do 18.12.08 15:22

Schau mal hier (10 Sek Google):
http://www.delphipraxis.net/post4810.html


DerNetteNachbar - Do 18.12.08 15:25

Ich dachte da gibt es eine globale Funktion dafür. Das was in den Thread besprochen wird ist doch Quark.


jaenicke - Do 18.12.08 15:36

Wenn es dein eigenes Programm ist, ist die saubere Lösung ja auch über eine exportierte Prozedur die DLL zu initialisieren. Dann kannst du aus der Anwendung alle nötigen Daten übergeben und du sparst dir diese ganzen Verrenkungen. :nixweiss:

Wenn es nicht dein eigenes Programm ist, dann musst du dich eben verrenken.


Delete - Do 18.12.08 15:40

Das sag ich doch die ganze Zeit.


jaenicke - Do 18.12.08 15:51

Stimmt, ich sehs schon, im anderen Thread. Naja, wenns ihm Spaß macht sich das Leben schwer zu machen... :nixweiss:


DerNetteNachbar - Do 18.12.08 16:25

Hey Leute dank euch für euere Vorschläge es mir leichter zumachen. Da ich aber fest entschlossen bin es auf die Art(API Funktionen) zu machen um mich auch in die API Welt einzuarbeiten werde ich diesen Konzept weiterhin durchziehen.
Ich hoffe mir oder irgenteinem von euch kommt ein Geistesblitz^.

MfG


Delete - Do 18.12.08 16:28

Z.B. mit GetWindowThreadProcessId, aber das hatten wir doch neulich erst.


jaenicke - Do 18.12.08 16:33

user profile iconDeddyH hat folgendes geschrieben Zum zitierten Posting springen:
Z.B. mit GetWindowThreadProcessId, aber das hatten wir doch neulich erst.
Das wäre ja genau die umgekehrte Richtung, er braucht ja das Fenster. Und ich kenne keine direkte Möglichkeit es zu bekommen. Wie es geht steht ja in dem verlinkten Thread. Ich glaube nicht, dass es einfacher geht. :nixweiss:

Wie sollte es auch einfacher gehen, bei einem Delphi-Programm hat man das versteckte Application-Fenster, bei anderen Programmen nicht, usw., deshalb sehe ich keine andere Möglichkeit als alle Fenster des Prozesses durchzugehen und anzuschauen.


Delete - Do 18.12.08 16:45

Genau so habe ich das im anderen Thread ja auch gemacht. Ein "Durchgehen" wird sich nicht umgehen lassen, ob man das von dieser oder von der anderen Seite angeht (3 * "gehen" in einem Satz *gg*).


jaenicke - Do 18.12.08 17:07

user profile iconDerNetteNachbar hat folgendes geschrieben Zum zitierten Posting springen:
Ich dachte da gibt es eine globale Funktion dafür.
Und um dazu noch etwas zu sagen:
Warum sollte es etwas dafür geben? Du willst es auf diese Weise machen, das ist aber nicht die normale Vorgehensweise. Wenn du das so machen willst, ist das ja deine Sache, aber warum sollte es dafür etwas fertiges geben?

Das ist wie wenn du den Trampelpfad nimmst statt eines befestigten Wegs und dich dann ärgerst, wenn es keine Brücke über einen Bach gibt. ;-)


DerNetteNachbar - Do 18.12.08 17:15

Alles klar Leute, ich habs gemerkt das es zu nichts führt. Nur das Problem immernoch ich bekomme doe falsche Process ID aus der DLL. Nämlich ihre eigene und nicht die der aufrufenden Application. Versucht hab ich es mit :

Delphi-Quelltext
1:
2:
GetWindowThreadProcessID
GetCurrentProcessID


MfG


Delete - Do 18.12.08 17:20

Richtig, die DLL hat ihr eigenes Prozesshandle. Du könntest nun über Umwege versuchen, über FindWindow und GetWindowThreadProcessId das Prozesshandle Deiner Anwendung herauszufinden, bei evtl. mehreren Instanzen oder Fenstern mit gleichem Titel und gleichem Classname kann das aber arg danebengehen. U.a. aus diesen Gründen wollen wir Dir ja nahelegen, das auf anderem Wege zu machen.


jaenicke - Do 18.12.08 17:22

Es geht, ich hatte die Doku falsch verstanden was GetCurrentProcessID anging :oops:. Richtig sollte es mit

Delphi-Quelltext
1:
MyHandle := GetModuleHandle(nil);                    
gehen, wenn ich das richtig verstehe:
http://msdn.microsoft.com/en-us/library/ms683199(VS.85).aspx hat folgendes geschrieben:
If this parameter is NULL, GetModuleHandle returns a handle to the file used to create the calling process (.exe file).


DerNetteNachbar - Do 18.12.08 17:28

Daraufhin wird mir irgentein komisches Handle gesenden was ich gar nicht mit WinSpy ansehen kann. Komisch


Delete - Do 18.12.08 17:30

Das ist ja auch ein Prozess- und kein Fensterhandle.


DerNetteNachbar - Do 18.12.08 17:34

Joa und nu...den ProzessHandle konnte ich auch damit ermitteln

Delphi-Quelltext
1:
2:
 processHandle := OpenProcess(PROCESS_TERMINATE or PROCESS_QUERY_INFORMATION,
        False, hprocessID);

Ich brauche ja immernoch das TOP Fenster.


Delete - Do 18.12.08 17:40

Was soll das TOP-Fenster sein?


DerNetteNachbar - Do 18.12.08 17:43

Das erste Fenster was in der Anwendung aufgerufen wird auch das TOP Level genannt.


Delete - Do 18.12.08 17:46

Und wodurch zeichnet sich das aus? Ein Flag TOP_WINDOW oder so wäre mir neu.


DerNetteNachbar - Do 18.12.08 17:50

Deshalb auch meine Frage ob es nicht eine Kennzeichnung gibt woran man es festmachen könnte.


Delete - Do 18.12.08 17:52

Zumindest mir ist keine bekannt.


DerNetteNachbar - Do 18.12.08 18:05

Schade, ich wüsste jetzt nicht wie ich das jetzet realisieren könnte.


DerNetteNachbar - Fr 19.12.08 10:37

PUSH


Delete - Fr 19.12.08 10:58

Wo liegt denn jetzt noch Dein Problem?


jaenicke - Fr 19.12.08 11:06

user profile iconDerNetteNachbar hat folgendes geschrieben Zum zitierten Posting springen:
PUSH
Das sind aber kurze 24 Stunden, pushen ist erst dann erlaubt. :roll:

Es gibt nun einmal kaum eine andere gute Möglichkeit als alle Prozesse durchzugehen...
Den Link hast du gesehen...

Du kannst es mit Win32_Process via WMI versuchen, da gibt es ParentProcessId, ich vermute damit macht es auch der ProcessExplorer:
http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx
Aber ob das eine so gute Lösung für diesen Zweck ist bin ich mir nicht so sicher. Da bekommst du jedenfalls, wenn das klappt, die ProcessId, und kannst dann die Fenster des Prozesses durchgehen.
Hast du eins gefunden kannst du GetAncestor [http://msdn.microsoft.com/en-us/library/ms633502(VS.85).aspx] mit GA_ROOT versuchen um das oberste Parent Window zu finden.

Die schlechteste Lösung wäre, deine Botschaft an alle Fenster, die du mit EnumWindows bekommst zu schicken. Darunter wird auch das deiner Anwendung sein.
Naja, aber da du deine Software ja wohl kaum weitergeben willst (dann würdest du es ja hoffentlich sauber programmieren), kannst du ja auch das machen... Solange du das nicht bei Software machst, die du veröffentlichen willst.

user profile iconDerNetteNachbar hat folgendes geschrieben Zum zitierten Posting springen:
Schade, ich wüsste jetzt nicht wie ich das jetzet realisieren könnte.
Normal :nixweiss:


DerNetteNachbar - Fr 19.12.08 11:06

Naja das Problem ist immer noch das ich die falsche ProzessID bekomme wenn ich in meiner DLL

Delphi-Quelltext
1:
2:
GetCurrentProcess() oder
GetWindowThreadProcessID

aufrufe, nämlich die der DLL. Ich möchte aber die ProzessID vom Elternprozess in dem Fall der aufrufende EXE Datei erhalten umso dann auf das MainWindow zu kommen.


Delete - Fr 19.12.08 11:12

GetModuleHandle(nil) sollte laut MSDN das Richtige sein, hat jaenicke doch bereits gepostet.


jaenicke - Fr 19.12.08 11:16

user profile iconDerNetteNachbar hat folgendes geschrieben Zum zitierten Posting springen:
Da ich aber fest entschlossen bin es auf die Art(API Funktionen) zu machen um mich auch in die API Welt einzuarbeiten
Die einzige sinnvolle Möglichkeit dafür ist selbst in der API Referenz zu suchen, nur dadurch lernst du diese kennen. Und genau das ist das wichtige. Ich kenne nicht alle Funktionen auswendig, ich weiß aber wie ich in der Referenz etwas finde.
Und das lernst du nicht, wenn du um Funktionen zu finden jedesmal nachfragst. :nixweiss:


DerNetteNachbar - Fr 19.12.08 11:25

GetModuleHandle(nil) klappt nicht wirklich, denn es liefert mir ein unbekanntes Handle, das habe ich aber auch schon mal gepostet.


jaenicke - Fr 19.12.08 11:39

user profile iconDerNetteNachbar hat folgendes geschrieben Zum zitierten Posting springen:
GetModuleHandle(nil) klappt nicht wirklich, denn es liefert mir ein unbekanntes Handle, das habe ich aber auch schon mal gepostet.
Das kann ich nicht reproduzieren. Ich habe es gerade getestet, bei mir ist das Handle gültig. :nixweiss:

Zum Test habe ich GetModuleFileName darauf angewendet, es liefert mir Pfad und Dateiname der Exe, nicht der DLL, also wie es sein soll. Es ist also ein gültiger HInstance Wert. Wie es in der Doku auch steht.


DerNetteNachbar - Fr 19.12.08 11:56

Das ist korrekt ich kriege den Pfad zu den EXE Dateien, allerdings liefert mir diese Funktion zweimal den gleichen Handle Wert. Sprich ich rufe die DLL aus zwei verschiedenen EXE Dateien auf und mir wird immer ein und dasselbe Handle zurückgeliefert. Komischer Verlauf. Ich will aber genau diesen Wert eindeutig haben um diesen im nachhinein zu vergleichen und die Anwendung zu identifizieren.


jaenicke - Fr 19.12.08 12:04

Also ich habe gerade einen kleinen Test gemacht:

DLL
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
function EnumWindowsProc(Hnd: HWND; lParam: LPARAM): Boolean; stdcall;
begin
  Result := GetWindowLong(Hnd, GWL_HINSTANCE) <> GetModuleHandle(nil);
  if not Result then
    SendMessage(GetAncestor(Hnd, GA_ROOT), WM_USER + 10001);

  Result := True; 
    // Damit alle durchgegangen werden, eigentlich
    // gehört die Zeile nicht hier hin!!!
end;

begin
  EnumWindows(@EnumWindowsProc, 0);
end;

Programm
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TForm99.Button1Click(Sender: TObject);
begin
  FreeLibrary(LoadLibrary('Project100.dll'));
end;

procedure TForm99.WndProc(var Msg: TMessage);
begin
  if Msg.Msg = WM_TEST then
    ShowMessage(IntToStr(Msg.LParam))
  else
    inherited;
end;
Jetzt habe ich das Programm mehrfach gestartet, und die MessageBox kam immer genau in dem Programm, das die DLL aufgerufen hat, genau wie es sein soll... :nixweiss:

Und durch die Zeile Result := True; werden ja explizit alle Fenster durchgegangen.


Motzi - Sa 20.12.08 17:03

Also hier werden alle möglichen Dinge vermischt..!
Prozess-Handle != Prozess-ID != Fenster-Handle != Module-Handle



jaenicke - Sa 20.12.08 17:41

Deshalb sollten ja diese beiden Handles innerhalb des Programms gültig sein, die hInstance des Prozesses sollte dann dem (ebenfalls internen) Modulhandle entsprechen.
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:

DLL
1:
2:
3:
function EnumWindowsProc(Hnd: HWND; lParam: LPARAM): Boolean; stdcall;
begin
  Result := GetWindowLong(Hnd, GWL_HINSTANCE) <> GetModuleHandle(nil);

Ob das jetzt nur zufällig funktioniert weiß ich nicht, aber die verlinkte Lösung oder eine ganz saubere Lösung stießen ja auf kein Interesse beim Threadersteller. :nixweiss: