32. Pid2Hwnd(), czyli jak z identyfikatora instancji Pid uzyskać uchwyt okna Hwnd?

Zdarza się, że otwieramy zewnętrzny program (za pomocą Shell) i chcielibyśmy uzyskać dostęp do jego okna. Jednak funkcja Shell(...) zwraca jedynie Pid, czyli identyfikator wywołanego programu. Poniższy kod pokazuje jak z tego identyfikatora odczytać uchwyt okna?
'Kod w module głównym
Public Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function GetWindow Lib "user32" _
    (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
    (ByVal lpClassName As Long, ByVal lpWindowName As Long) As Long
Public Declare Function GetWindowThreadProcessId Lib "user32" _
    (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Public Const GW_HWNDNEXT = 2

Function Pid2Hwnd(ByVal target_pid As Long) As Long
    Dim test_hwnd As Long, test_pid As Long, test_thread_id As Long
    test_hwnd = FindWindow(ByVal 0&, ByVal 0&)
    Do While test_hwnd <> 0
        If GetParent(test_hwnd) = 0 Then
            test_thread_id = GetWindowThreadProcessId(test_hwnd, test_pid)
            If test_pid = target_pid Then
                Pid2Hwnd = test_hwnd
                Exit Do
            End If
        End If
        test_hwnd = GetWindow(test_hwnd, GW_HWNDNEXT)
    Loop
End Function
Do czego nam taki odczyt może się przydać? To już zależy tylko od naszej pomysłowości... Np można w ten sposób zamknąć okienko DOS, które wywołaliśmy do naszych celów, można też w ten sposób uzyskać dostęp do zaawansowanych elementów okna w zewnętrznej aplikacji. Poniższy przykład jest bardzo prosty i ma na celu jedynie przedstawić zasadę:
'Kod na formularzu
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
    (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" _
    (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Private Const WM_CHAR = &H102
Private Const WM_CLOSE = &H10

'Naciśnięcie przycisku wywołuje kalkulator, następnie procedura odczytuje jego uchwyt okna.
'Do tak odczytanego okna przekazujemy kilka komunikatów, a na koniec zamykamy wywołany program.
Private Sub Wylicz_Click()
Dim pid As Long, mWnd As Long, ret
    pid = Shell("Calc.exe")
    mWnd = Pid2Hwnd(pid)
    PostMessage mWnd, WM_CHAR, Asc("2"), 0
    PostMessage mWnd, WM_CHAR, Asc("*"), 0
    PostMessage mWnd, WM_CHAR, Asc("3"), 0
    PostMessage mWnd, WM_CHAR, Asc("="), 0
    MsgBox "Skopiuj wynik. Kalkulator zostanie zamknięty automatycznie."
    SendMessage mWnd, WM_CLOSE, 0, 0
End Sub

Zobacz tez  wątki 1 i 2 w archiwum grupy dyskusyjnej.