Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

hWnd aus TaskID?

62 views
Skip to first unread message

Wendelin Uez

unread,
Nov 20, 2018, 2:52:16 AM11/20/18
to
Wie komme ich an das hWnd einer mit Shell gestarteten Anwendung, deren
Fenstertitel unbekammt ist?
Sehell gibt die TaskID zurück, aber ich brauche das hWnd des Hauptfensters.

wuez

Christian Zimmermann

unread,
Nov 20, 2018, 3:47:00 AM11/20/18
to
Hallo Wendelin,
Das Hauptfenster ist dasjenige, welches keinen Parent hat. Da hilft dir
dies womöglich weiter:

https://www.vbarchiv.net/tipps/tipp_1068-von-der-prozess-id-zum-fenstertitel.html

Die Frage bzw. Problemstellung ergibt sich wahrscheinlich daraus, ob du
auch das "richtige" Fenster triffst. Wenn du eine Anwendung startest,
die nicht von dir entwickelt wurde bzw. kontrolliert werden kann, dann
ist es möglicherweise eine Sache des Timings, ob du den Splash-Screen
triffst oder auch nicht.

Nur mal so in den Raum geworfen :-)

Gruß,
Christian

Klaus Ketelaer

unread,
Nov 20, 2018, 4:00:08 AM11/20/18
to
Am 20.11.2018 um 08:51 schrieb Wendelin Uez:
Moin,
mir würde einfallen, mit EnumWindows die Fensterliste
zu durchlaufen und die ProcessID zu vergleichen.

Gruß Klaus

Wendelin Uez

unread,
Nov 20, 2018, 9:06:02 AM11/20/18
to
Mein Problem ist, dass sich die vom Shell-Aufruf gelieferte TaskID (TaskID=
Shell(Exefile)) nicht mit den üblichen Beispielen für
Fensterlisten-Schleifen finden läßt.

Die Beispiele durchlaufen die Fensterliste wie folgt:
hWnd = GetWindows(Me.hWnd,GW_HWNDFIRST)
While hWnd <>0
IF GetWindowLong(hWmd,GWL_STYLE) = WS_VISIBLE or WS_BORDER Then
TaskIDfound = GetWindowTaskID(hWnd)
IF TaskID = TaskIDfound then
hWndFound=hWnd <--- wird nie gefunden
End If
End If
Wend

wobei eben TaskIDfound nie die vom Shell-Aufruf gelieferte TaskID wird..

Dabei ist mir zweierlei noch unklar:

1.
Die VB6-Hilfe sagt, Shell liefere einen Double-Wert als TaskID zurück, alle
Beispiele incl. Declare-Statements gehen immer nur von Long Integern aus.
Ist das nur ein Druckfehler?

2.
Für die hWnd-Vorbelegung vor der While-Schleife gibt es je nach Beispiel
unterschiedliche Verfahren, meist wird wie oben die erste Variante
verwendet:
- hWnd = GetWindows(Me.hWnd,GW_HWNDFIRST)
- hWnd = GetTopWindow(0)
- hWnd = FindWindow(vbNullString,vbNullString)
Worin liegt der Unterschied der drei Varianten?

Ohne die Caption des von Shell geöffneten Fensters zu kennen habe ich halt
nur die Shell-TaskID, und die scheint irgendwie nicht in den
Fensterlisten-TaskIDs vorzukommen.

Im Moment bin ich ziemlich ratlos, und Kaffee ist auch alle :-)

wuez


Christian Zimmermann

unread,
Nov 20, 2018, 10:46:26 AM11/20/18
to
Hallo Wendelin,

Am 20.11.2018 um 15:05 schrieb Wendelin Uez:
> Mein Problem ist, dass sich die vom Shell-Aufruf gelieferte TaskID
> (TaskID= Shell(Exefile)) nicht mit den üblichen Beispielen für
> Fensterlisten-Schleifen finden läßt.
>
> Die Beispiele durchlaufen die Fensterliste wie folgt:
> hWnd = GetWindows(Me.hWnd,GW_HWNDFIRST)

sollte heißen: GetWindow()

oder verbirgt sich dahinter eine eigene Funktion?

> While hWnd <>0
>   IF GetWindowLong(hWmd,GWL_STYLE) = WS_VISIBLE or WS_BORDER Then
>    TaskIDfound = GetWindowTaskID(hWnd)

ich nehme an, hinter GetWindowTaskID() verbirgt sich ein Aufruf von
GetWindowThreadProcessId()?!

>    IF TaskID = TaskIDfound then
>        hWndFound=hWnd    <--- wird nie gefunden
>        End If
>    End If

hier fehlt: hwnd = GetWindow(hwnd, GW_HWNDNEXT), sonst wird das nächste
Fenster nicht ermittelt.

Du hast hier eine Endlosschleife. Auch wenn das richtige Handle gefunden
werden würde.

> Wend
>
> wobei eben TaskIDfound nie die vom Shell-Aufruf gelieferte TaskID wird..
>
> Dabei ist mir zweierlei noch unklar:
>
> 1.
> Die VB6-Hilfe sagt, Shell liefere einen Double-Wert als TaskID zurück,
> alle Beispiele incl. Declare-Statements gehen immer nur von Long
> Integern aus. Ist das nur ein Druckfehler?

den Wert kann man getrost als Long verarbeiten ;-)

Ist denn die Anwendung, welche du per Shell startest, zu dem Zeitpunkt
zu dem du das Fenster zu ermitteln versuchst, im TaskManager sichtbar?

Gruß,
Christian


Wendelin Uez

unread,
Nov 20, 2018, 11:26:52 AM11/20/18
to
Danke für deine Antwort. Du hast leider recht: :-(
- Das 's' bei GetWindows ist ein Tippfehler.
- GetWindowTaskID ist eine eigene Funktion und genau das, wie du dachtest
(GetWindowThreadProcessId)
- ich habe natürlich keine Endlosschleife, nur die Zeile mit GW_HWNDNEXT
bei der manuellen Übertragung vergessen

Ich habe bisher immer mit dem Aufruf des Filemanagers getestet - da findet
die Schleife nie die TaskID. Wenn ich den Notepad oder Calc.exe aufrufe
funktioniert mein Code wunderbar wie gewünscht.

Zwar startet der Notepad deutlich schneller als der Filemanager, aber selbst
mit einer sekundenlangen Wartezeit zwischen Shell-Aufruf und hWnd-Ermittlung
klappt es beim Filemanager nicht. Die TaskID wird von Shell immer geliefert,
daran kann's nicht liegen.

Aber woran dann?

wuez

Christian Zimmermann

unread,
Nov 20, 2018, 11:44:05 AM11/20/18
to
Am 20.11.2018 um 17:26 schrieb Wendelin Uez:
>
> Ich habe bisher immer mit dem Aufruf des Filemanagers getestet - da
> findet die Schleife nie die TaskID. Wenn ich den Notepad oder Calc.exe
> aufrufe funktioniert mein Code wunderbar wie gewünscht.
>

Meinst du evt. den TaskManager von Windows damit? Ist das Fenster denn
sichtbar (WS_VISIBLE) nach dem Start?

Gruß,
Christian

Christian Zimmermann

unread,
Nov 20, 2018, 12:05:32 PM11/20/18
to
Da du den Code leider ungenau gepostet hast, kann ich nur raten, aber
evt. liegt es auch an folgender Abfrage:

IF GetWindowLong(hWmd,GWL_STYLE) = WS_VISIBLE or WS_BORDER Then

das macht so keinen Sinn, denn ein = bindet mehr als ein OR oder ein AND

(GetWindowLong(hwnd, GWL_STYLE) And (WS_VISIBLE Or WS_BORDER)) =
(WS_VISIBLE Or WS_BORDER)

oder

(GetWindowLong(hwnd, GWL_STYLE) And WS_VISIBLE) = WS_VISIBLE

Desweiteren kommt in Frage, dass Shell(), wenn der Windows Taskmanager
schon läuft, eine andere TaskID zurückliefert, als diejenige, welche im
Taskmanager für sich selbst zu lesen ist. Möglicherweise ist es die PID
(TaskID), welche eine neue Instanz des Taskmanagers erhält, bevor sie
sich wieder beendet, da eine Instanz des Taskmanagers bereits läuft.

Hth.

Gruß,
Christian

Wendelin Uez

unread,
Nov 20, 2018, 3:24:44 PM11/20/18
to
>> Ich habe bisher immer mit dem Aufruf des Filemanagers getestet - da
>> findet die Schleife nie die TaskID. Wenn ich den Notepad oder Calc.exe
>> aufrufe funktioniert mein Code wunderbar wie gewünscht.
>
> Meinst du evt. den TaskManager von Windows damit? Ist das Fenster denn
> sichtbar (WS_VISIBLE) nach dem Start?

Nein, den Explorer. Und ja, er wird ja gestartet und ist sichtbar.

Wendelin Uez

unread,
Nov 20, 2018, 3:24:45 PM11/20/18
to

> IF GetWindowLong(hWmd,GWL_STYLE) = WS_VISIBLE or WS_BORDER Then
>
> das macht so keinen Sinn, denn ein = bindet mehr als ein OR oder ein AND
Ja, da fehlte in der (manuellen) Übertragung leider ein Satz Klammern, aber
mittlerweile habe ich folgende Auswahl aus einem funktionierenden Beispiel
übernommen, dort wird nicht der window style abgefragt, sondern
IF GetParent(hWnd) = 0 Then ...

Und so funktioniert es ja, wie gesagt, mit Notepad, Calc etc., aber eben
leider nicht mit dem Explorer..

> Desweiteren kommt in Frage, dass Shell(), wenn der Windows Taskmanager
> schon läuft, eine andere TaskID zurückliefert, als diejenige, welche im
> Taskmanager für sich selbst zu lesen ist. Möglicherweise ist es die PID
> (TaskID), welche eine neue Instanz des Taskmanagers erhält, bevor sie
> sich wieder beendet, da eine Instanz des Taskmanagers bereits läuft.

Oh, das wußte ich auch noch nicht, aber nein, der Taskmanager läuft nicht.

Ich will bloß aus meiner Anwendung heraus den Explorer starten und dessen
Fenster in Position und Größe festlegen. Dazu brauche ich sein hWnd.
Und der Explorer zeigt halt in der Titelzeile keinen Pfad, sondern nur den
Namen vom angewählten Unterverzeichnis an, den ich nicht zwingend kenne, so
dass ich nicht nach Fenstertitel sortierern/suchen kann und auf die TaskID
des Shell-Befehls zurück greifen muß.

Klaus Ketelaer

unread,
Nov 20, 2018, 7:12:40 PM11/20/18
to
Am 20.11.2018 um 21:24 schrieb Wendelin Uez:
> Ich will bloß aus meiner Anwendung heraus den Explorer starten und
> dessen Fenster in Position und Größe festlegen. Dazu brauche ich sein hWnd.
Nee, brauchst Du nicht.

Wer zwingt Dich, den Explorer mit Shell zu starten?

Mit CreateProcess ist es gar kein Problem ein Anwendung
an der gewünschten Position zu starten. Einfach die
Werte in STARTUPINFO eintragen und fertig.
(Aber ein Fensterhandle haste dann immer noch nicht...)


Ungetestet:

Private Const NORMAL_PRIORITY_CLASS = &H20
Private Const STARTF_USESIZE = &H2
Private Const STARTF_USEPOSITION = &H4

Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type

Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type

Private Declare Function CreateProcess Lib "kernel32" Alias
"CreateProcessA" (ByVal lpApplicationName&, ByVal lpCommandLine$, ByVal
lpProcessAttributes&, ByVal lpThreadAttributes&, ByVal bInheritHandles&,
ByVal dwCreationFlags&, ByVal lpEnvironment&, ByVal lpCurrentDirectory&,
lpStartupInfo As STARTUPINFO, lpProcessInformation As
PROCESS_INFORMATION) As Long

Sub Test (Byval szAllplication as string, Byval szCommandline as string)
Dim udtProcessInfo As PROCESS_INFORMATION
Dim udtStartupInfo As STARTUPINFO
Dim lResult As Long
Dim lFlags as long

udtStartupInfo.cb = Len(udtStartupInfo)
udtStartupInfo.dwX = 100
udtStartupInfo.dwY = 100
udtStartupInfo.dwXSize = 800
udtStartupInfo.dwYSize =600

lFlags = NORMAL_PRIORITY_CLASS Or STARTF_USEPOSITION or STARTF_USESIZE
lResult = CreateProcess(0&, szAllplication & " " & szCommandline, 0&,
0&, 0&, lFlags, 0&, 0&, udtStartupInfo, udtProcessInfo)
end Sub

Christian Zimmermann

unread,
Nov 21, 2018, 4:21:52 AM11/21/18
to
Hallo Wendelin,
das dürfte hierbei (zumindest unter Win10) ebenso laufen. Die vorhandene
Instanz wird von der per Shell() gestarteten neuen Instanz aufgefordert
ein neues Fenster anzuzeigen und die neue Instanz beendet sich sogleich
wieder.

Du könntest vor dem Öffnen der weiteren Explorer-Instanz per Shell() die
Prozessliste nach der vorhandenen Instanz des Explorers durchsuchen
(Kriterium: Explorer.exe oder Windows\Explorer.exe oder
Windows\SysWOW64\Explorer.exe ????? ) und anschließend ermittelst du
alle dazugehörigen Fenster. Nach der Anwendung von Shell() ermittelst du
wieder alle Fenster und vergleichst, welches neu dazugekommen ist. Ohne
dies getestet zu haben, könnten natürlich mehr als eines dazukommen. Das
Timing spielt hier bestimmt noch eine Rolle. Wenn du zum zweiten mal die
Fensterliste abfragst, könnte das neue Fenster noch garnicht existieren.

Auf diese Art also keine todsichere Sache. Sicher wäre man nur, wenn es
eine API zum Explorer gäbe, welche dir das richtige Hwnd nach Öffnung
eines neuen Fenster liefern würde. Dazu fällt mir aber nichts ein.

Gruß,
Christian

Wendelin Uez

unread,
Nov 21, 2018, 9:13:05 AM11/21/18
to
> Du könntest vor dem Öffnen der weiteren Explorer-Instanz per Shell() die
> Prozessliste nach der vorhandenen Instanz des Explorers durchsuchen
> (Kriterium: Explorer.exe oder Windows\Explorer.exe oder
> Windows\SysWOW64\Explorer.exe ????? ) und anschließend ermittelst du
> alle dazugehörigen Fenster. Nach der Anwendung von Shell() ermittelst du
> wieder alle Fenster und vergleichst, welches neu dazugekommen ist. Ohne
> dies getestet zu haben, könnten natürlich mehr als eines dazukommen. Das
> Timing spielt hier bestimmt noch eine Rolle. Wenn du zum zweiten mal die
> Fensterliste abfragst, könnte das neue Fenster noch garnicht existieren.

Ich habe das versuchsweise gemacht - Fensterliste erstellt, gestartet,
gewartet plus neue Fensterliste verglichen. Das Problem war sogleich, dass
mehrere neue Fenster existieren, je nach dem, was Windows so gerade tut.

Wendelin Uez

unread,
Nov 21, 2018, 9:13:05 AM11/21/18
to
Vielen Dank für das Beispiel!

Ist zwar sauber lesbar und verständlich, klappt aber jedenfalls nicht auf
Anhieb. Auf Google gibt es etliche Hinweise zu Problemen zu CreateProcess
unter x64-Win7. Ich arbeute auf Win8.1 x64 und vielleicht hängt's damit
zusammen, mal gucken. und reinlesen.

Ich bekomme von CreateProcess einen positiven Wert sprich successfull, aber
kein Fenster.
Der Wndows TaskManager zeigt den/die gestarteten Prozesse (Notepad u.a.)
kurioserweise als Hintergrundprozess an, d.h. es wird schon gestartet, nur
eben ohne Fenster.

wuez

Klaus Ketelaer

unread,
Nov 21, 2018, 10:38:55 AM11/21/18
to
Also, ich verwende den Code seit Windows 2000, mit dem Zusatz

Do Until WaitForSingleObject(udtProcessInfo.hProcess, 0) = 0
DoEvents
Loop

am Ende, der lediglich bewirkt, dass VB wartet, bis die Anwendung
beendet ist.

Dank meiner Windows 10 Abneigung (und 8er sowieso;-)) arbeite ich
seit Jahren mit Windows 7 prof. 64. Der Code steckt in meinen System-
Klassen und ich hätte 6 Richtige, wenn der nicht funzen würde.

Ohne es wirklich zu wissen, kann ich mir nicht vorstellen, dass solch
eine System-Funktion unter > Win 7 nicht mehr sauber funktioniert.


"Rein theoretisch" kann ich mir ebenfalls nicht vorstellen, dass man
das Fensterhandle nicht anhand des Thread-Handles ermitteln kann.


Sofern Du Einfluss auf die Nutzung des Explorers hast, würde auch die
Einstellung "Vollständiger Pfad in der Titelleiste" helfen. Du startest
den Explorer dann einfach mit einem Verzeichnis-Pfad und suchst danach.

Allzu viele Fenster würden in Zeichen 2 und 3 auch nicht den Text ":\"
tragen


Ansonsten könnte es nach dem Start des Explorers helfen, einfach das
ForegroundWindow zu ermitteln und die Klassen-Namen zu prüfen.


Diverse Fenster im Explorer haben eindeutige Klassen-Namen, über die
man das Fenster finden kann. Danach könnte man sich rückwärts zum
"Fenster ohne Parent" hangeln.

Ich habe mir bei einer ähnlichen Aufgabenstellung mal richtig einen
am Internet-Explorer abgebrochen...

Gruß Klaus


G.W.

unread,
Nov 21, 2018, 3:50:11 PM11/21/18
to
Am Mittwoch, 21. November 2018 01:12:40 UTC+1 schrieb Klaus Ketelaer:
> Am 20.11.2018 um 21:24 schrieb Wendelin Uez:
> > Ich will bloß aus meiner Anwendung heraus den Explorer starten und
> > dessen Fenster in Position und Größe festlegen. Dazu brauche ich sein hWnd.
> Nee, brauchst Du nicht.
>
> Wer zwingt Dich, den Explorer mit Shell zu starten?
>
> Mit CreateProcess ist es gar kein Problem ein Anwendung
> an der gewünschten Position zu starten. Einfach die
> Werte in STARTUPINFO eintragen und fertig.
> (Aber ein Fensterhandle haste dann immer noch nicht...)

Um das Fensterhandle (so es denn eins gibt) zu ermitteln, kann man für den Explorer nach dem Start einfach nach Fenstern mit dem Klassennamen CabinetWClass suchen. Das ist das oberste Fenster, das gesucht wird. Falls es mehrere Explorer Fenster gibt (oder einfach zur Sicherheit) kann man mit GetProcessHandleFromHwnd ( angewandt auf das ermittelte Fensterhandle) das Prozesshandle erhalten und mit dem von CreateProcess in der Process_information Struktur zurückgelieferten Prozesshandle vergleichen. Bei Übereinstimmung hab ich dann mein Fenster.

Übrigens funktioniert der Programmstart mit CreateprocessA selbstverständlich auch unter Windows 10 völlig problemlos - sowohl in der 32bit Version als auch in der 64bit Version. Setze dies unter beiden Systemen ein.

Gert

Christian Zimmermann

unread,
Nov 22, 2018, 3:39:08 AM11/22/18
to
Hallo Wendelin,
mit der Information von Gert bzgl. des Klassennamens, käme etwa folgende
Möglichkeit ins Spiel:

Du verzichtest auf die Ermittlung der PIDs und prüfst die Fenster
lediglich bzgl. der Klasse "CabinetWClass". Das neu hinzugekommene
Fenster nimmst du dann als dasjenige welche.

Die Unwägbarkeiten dabei sind, dass
- evt. nutzt nicht nur der Explorer Fenster der Klasse CabinetWClass (du
könntest natürlich die Prüfung dahingehend erweitern, indem Du via
Fenster die PID ermittelst und daraus das zugehörige Image)
- ein anderer Prozess als deiner könnte just zu der Zeit, in der du den
Explorer per Shell() startest, auch den Explorer aufrufen

Ich denke, mit diesen Unwägbarkeiten läßt es sich aber dennoch gut
auskommen.

' ******************************************
Option Explicit

Private Declare Function GetWindow Lib "user32" (ByVal hwnd _
As Long, ByVal wCmd As Long) As Long

Private Declare Function GetClassName Lib "user32.dll" Alias _
"GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName _
As String, ByVal nMaxCount As Long) As Long

Private Declare Function MoveWindow Lib "user32.dll" (ByVal hwnd _
As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth _
As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long

Private Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long)

Const GW_HWNDFIRST = 0
Const GW_HWNDNEXT = 2

Private Function EnumCabinetWClassWindows() As Collection
Dim hwnd As Long
Dim n As Long
Dim clsName As String

Set EnumCabinetWClassWindows = New Collection

hwnd = GetWindow(Me.hwnd, GW_HWNDFIRST)

Do
' Klassennamen ermitteln. Platz reicht aus, da hier nur
"CabinetWClass" ermittelt werden soll
clsName = Space$(20)
n = GetClassName(hwnd, clsName, Len(clsName))
If UCase$(Left$(clsName, n)) = "CABINETWCLASS" Then
EnumCabinetWClassWindows.Add hwnd, CStr(hwnd)
End If
hwnd = GetWindow(hwnd, GW_HWNDNEXT)
Loop Until hwnd = 0
End Function

Private Function GetNewCabinetWClassWindow(ByRef wListe As Collection)
As Long
Dim hwnd As Long
Dim n As Long
Dim clsName As String

On Error Resume Next

hwnd = GetWindow(Me.hwnd, GW_HWNDFIRST)
Do
' Klassennamen ermitteln
clsName = Space$(20)
n = GetClassName(hwnd, clsName, Len(clsName))
If UCase$(Left$(clsName, n)) = "CABINETWCLASS" Then
Err.Clear
n = wListe.Item(CStr(hwnd)) ' in der Fensterliste nachsehen
If Err.Number <> 0 Then ' nicht drin, dies ist das neue Fenster
GetNewCabinetWClassWindow = hwnd
Exit Do
End If
End If
hwnd = GetWindow(hwnd, GW_HWNDNEXT)
Loop Until hwnd = 0

End Function

Private Sub Command1_Click()
Dim wListe As New Collection
Dim hwnd As Long

Set wListe = EnumCabinetWClassWindows
Shell "Explorer.exe"
hwnd = GetNewCabinetWClassWindow(wListe)
Do While hwnd = 0
hwnd = GetNewCabinetWClassWindow(wListe)
Sleep 100
Loop
MoveWindow hwnd, 100, 100, 600, 500, True
End Sub

' ******************************************

Gruß,
Christian
0 new messages