Získání "WindowHandle" procesu   otázka

VB.NET

Hezký den,

mám problémek. Potřebuji získat u procesu, u kterého znám jeho "ProcessID", jeho "WindowHandle". Klasické:

 Dim proc As Diagnostics.Process = Process.GetProcessById(ProcessID)
 Dim Whandle as IntPtr = proc.MainWindowHandle

nefunguje, protože proces je minimalizovaný do "System Tray" (vedle hodin) a vrací to nulu.

Vím, že to není jednoduché a proto se ptám jestli má někdo s tím zkušenosti, anebo poradí jak na to?

Hledal jsem na netu, zkoušel, ale nevede to k výsledku.

Děkuji.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

OPRAVA: Obávám se, že to nepůjde. Zkoušel sem to na aplikaci Windows Messenger, která byla minimalizována v oznamovací oblasti hlavního panelu (System Tray) a v Process Exploreru nebyl příkaz Window kterým se aktivuje okno aplikace k dispozici. Takže bude nutné aby bylo skutečně okno aplikace viditelné.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Na základě znalosti "WindowHandle" potřebuji příkladně zobrazit okno dotčeného procesu. Vím, že to umí např. ProcessExplorer, ale já to chci využít ve vlastním kódu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Já taky zkoušel (MS Outlook, TotalCommander minimalizovány v oznamovací oblasti) a šlo to. Záleží na tom jestli to aplikace umožňuje. Dokonce jsem "WindowHandle" obdržel pomocí:

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Dim Whwnd As IntPtr = FindWindow(vbNullString, "Total Commander 7.02a - NOT REGISTERED")
ShowWindow(Whwnd , 3)

a maximalizoval se z oznamovací oblasti, ale já název okna neznám, znám jen ID procesu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

To protože Outlook i Total Commander mají stále hlavní okno které má právě Handle, tady vůbec nejde o to jestli to umožňují nebo ne.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Správně, blbě jsem se vyjádřil. Již jsem to vyřešil pomocí API funkcí. Kód zde uveřejním později. Samozřejmě nelze operovat s oknem, které "neexistuje".

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Zveřejňuji kód:

Do modulu:

    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    Private Declare Function GetParent Lib "user32" (ByVal hwnd As IntPtr) As IntPtr
    Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As IntPtr, ByRef lpdwProcessId As UInteger) As UInteger
    Private Declare Function GetWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal wCmd As Integer) As IntPtr
    Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As IntPtr, ByVal lpString As String, ByVal nMaxCount As Integer) As Integer
    Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hWnd As IntPtr) As Integer
    Private Const GW_HWNDNEXT = 2
    Friend Function InstanceToWnd(ByVal Target_pid As IntPtr, ByRef Caption As String) As IntPtr
        Dim test_hwnd As IntPtr, test_pid As UInteger, test_thread_id As UInteger
        'Nalezení prvního okna
        test_hwnd = FindWindow(vbNullString, vbNullString)
        'procházení oken
        Do While test_hwnd <> 0
            'Kontrola jestli okno není podoknem
            If GetParent(test_hwnd) = 0 Then
                'Obdržení vlákna okna
                test_thread_id = GetWindowThreadProcessId(test_hwnd, test_pid)
                ' souhlasí ID procesu?
                If test_pid = Target_pid Then
                    'zjištění délky nadpisu a alokace prostoru bufferu 
                    Dim Length As Integer = GetWindowTextLength(test_hwnd)
                    Dim Buff As String = Space(Length + 1)
                    'Zjištění nadpisu okna
                    Length = GetWindowText(test_hwnd, Buff, Length + 1)
                    Caption = Buff.Substring(0, Length)
                    'vrácení WindowHandle a nadpisu okna (Caption)
                    Return test_hwnd
                End If
            End If
            'Nalezení dalšího okna
            test_hwnd = GetWindow(test_hwnd, GW_HWNDNEXT)
        Loop
    End Function

Do Formu přidat tlačítko a kód:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim ProcessID As IntPtr = 2480 ' změnit dle konkrétního procesu
        Dim Caption As String = vbNullString
        Dim Whwnd As IntPtr = InstanceToWnd(ProcessID, Caption)
        If IsNothing(Caption) Then Caption = vbNullString
        If Whwnd = 0 Or Caption = vbNullString Then Exit Sub
        If MsgBox("Chcete maximalizovat okno z nadpisem: " & Caption & " ?", MsgBoxStyle.YesNo + MsgBoxStyle.Question, "Bezpečnostní dotaz") = MsgBoxResult.Yes Then
            ShowWindow(Whwnd, 3)
        End If
    End Sub

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Ještě dodatek pro deklaraci funkce ShowWindow

Friend Declare Function ShowWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal nCmdShow As Integer) As Integer

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Stačila by vám pouze API funkce ShowWindow:

Friend Declare Function ShowWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal nCmdShow As Integer) As Integer
Friend Const SW_SHOWNORMAL As Integer = 5
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
  'Identifikátor procesu
  '(při každém spuštění stejného procesu může být jiný!)
  Dim pId As Integer = 1234
  'Proces nalezený podle identifikátoru
  Dim proc As Process = Process.GetProcessById(pId)
  'Zkontrolovat, zda-li je proces platný a zda-li Handle
  'hlavního okna procesu není nulové (v tom případě by
  'proces neměl žádné okno jak jsme už řešili
  If (proc IsNot Nothing) AndAlso (proc.MainWindowHandle <> IntPtr.Zero) Then
    'Zobrazit okno pomocí API funkce
    ShowWindow(proc.MainWindowHandle, SW_SHOWNORMAL)
  End If
End Sub

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Nestačila. Jak jsem již uvedl v úvodním příspěvku, dotaz na:

proc.MainWindowHandle

vrací nulu pokud je proces je minimalizovaný do "System Tray". Přitom se jedná třeba i o Total Commander, nebo Outlook, ale taky třeba Eset Smart Security apod. i když jak píšete, že „Outlook i Total Commander mají stále hlavní okno které má právě Handle“

Můj původní kód vede správným směrem, (alespoň si to myslím) ale má jednu zásadní chybu. Nalezne první Handle okna a ukončí prohledávání. A právě to první Handle nemusí být tím od hlavního okna. Proto jsem ho předělal tak, že nalezne všechny Handle a zůstane na uživateli, aby vybral to hlavní. Dají se využít různé filtry, např., že okno musí splňovat:

- musí mít titulní proužek

- musí mít titulek okna

- musí mít systémové menu

- musí mít minimalizační a maximalizační tlačítko

- musí umožňovat změnu velikosti …

a pak již zůstane většinou jenom jedno okno s relevantním názvem, případně ikonou.

Znovu opakuji, že nalezne i Handle oken, které jsou umístěny "System Tray", ale i oken, které jsou standardně skryté.

Kód:

    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    Private Declare Function GetParent Lib "user32" (ByVal hwnd As IntPtr) As IntPtr
    Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As IntPtr, ByRef lpdwProcessId As UInteger) As UInteger
    Private Declare Function GetWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal wCmd As Integer) As IntPtr
    Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As IntPtr, ByVal lpString As String, ByVal nMaxCount As Integer) As Integer
    Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hWnd As IntPtr) As Integer
    Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As IntPtr, ByVal nIndex As Integer) As Integer
    Private Declare Function GetClassLong Lib "user32" Alias "GetClassLongA" (ByVal hwnd As IntPtr, ByVal nIndex As Integer) As IntPtr
    Friend Declare Function ShowWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal nCmdShow As Integer) As Integer
    Private Const GW_HWNDNEXT = 2
    Private Const GWL_STYLE = (-16)
    Public Const WS_SYSMENU = &H80000
    Public Const WS_OVERLAPPED = &H0
    Public Const WS_CAPTION = &HC00000
    Public Const WS_THICKFRAME = &H40000
    Public Const WS_MINIMIZEBOX = &H20000
    Public Const WS_MAXIMIZEBOX = &H10000
    Private Const WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED Or WS_CAPTION Or WS_SYSMENU Or WS_THICKFRAME Or WS_MINIMIZEBOX Or WS_MAXIMIZEBOX)
    Private Const GCL_HICON = (-14)
    Private Const GCL_HICONSM = (-34)
    Public SEARCHCONST As Integer = WS_OVERLAPPEDWINDOW
    Public IsCaption As Boolean = True
    Public Const SW_HIDE = 0
    Public Const SW_MINIMIZE = 6
    Public Const SW_MAXIMIZE = 3
    Public Const SW_RESTORE = 9
    Public Const SW_SHOWMAXIMIZED = 3
    Public Const SW_SHOW = 5
    Public Const SW_SHOWMINIMIZED = 2
    Public Const SW_SHOWMINNOACTIVE = 7
    Public Const SW_SHOWNA = 8
    Public Const SW_SHOWNOACTIVATE = 4
    Public Const SW_SHOWNORMAL = 1
    Public Structure WinStructure
        Dim Caption As String
        Dim MaxBox As Boolean
        Dim MinBox As Boolean
        Dim Sizing As Boolean
        Dim SysMenu As Boolean
        Dim CaptBar As Boolean
        Dim Whwnd As IntPtr
        Dim Icon As Icon
    End Structure
    Friend Function SearchWnd(ByVal ProcessID As IntPtr) As WinStructure()
        Dim WStrct() As WinStructure = Nothing, Caption As String
        Dim test_hwnd As IntPtr, test_pid As UInteger, test_thread_id As UInteger
        Dim i As Integer, CP As Boolean = True
        'Nalezení prvního okna
        test_hwnd = FindWindow(vbNullString, vbNullString)
        'procházení oken
        Do While test_hwnd <> 0
            'Kontrola jestli okno není podoknem
            If GetParent(test_hwnd) = 0 Then
                'Obdržení vlákna okna
                test_thread_id = GetWindowThreadProcessId(test_hwnd, test_pid)
                ' souhlasí ID procesu?
                If test_pid = ProcessID Then
                    'zjištění délky nadpisu a alokace prostoru bufferu 
                    Dim Length As Integer = GetWindowTextLength(test_hwnd)
                    Dim Buff As String = Space(Length + 1)
                    'Zjištění nadpisu okna
                    Length = GetWindowText(test_hwnd, Buff, Length + 1)
                    Caption = Buff.Substring(0, Length)
                    'Pokud chceme filtrovat i podle přítomnosti titulku okna
                    If IsCaption Then CP = CBool(Length)
                    'Zjištění stylu okna
                    Dim WStyle As Integer = GetWindowLong(test_hwnd, GWL_STYLE)
                    'Filtr Stylu a Titulku
                    If (WStyle Or SEARCHCONST) = WStyle And CP Then
                        ReDim Preserve WStrct(i)
                        WStrct(i).Caption = Caption
                        WStrct(i).MaxBox = CBool(WStyle And WS_MAXIMIZEBOX)
                        WStrct(i).MinBox = CBool(WStyle And WS_MINIMIZEBOX)
                        WStrct(i).Sizing = CBool(WStyle And WS_THICKFRAME)
                        WStrct(i).SysMenu = CBool(WStyle And WS_SYSMENU)
                        WStrct(i).CaptBar = CBool(WStyle And WS_CAPTION)
                        WStrct(i).Whwnd = test_hwnd
                        Dim IcHanlde As IntPtr = GetClassLong(test_hwnd, GCL_HICONSM)
                        If IcHanlde = 0 Then
                            IcHanlde = GetClassLong(test_hwnd, GCL_HICON)
                        End If
                        If IcHanlde = 0 Then
                            WStrct(i).Icon = Nothing
                        Else
                            WStrct(i).Icon = System.Drawing.Icon.FromHandle(IcHanlde)
                        End If
                        i += 1
                    End If
                End If
            End If
            'Nalezení dalšího okna
            test_hwnd = GetWindow(test_hwnd, GW_HWNDNEXT)
        Loop
        Return WStrct
    End Function

Jako příklad uvedu okno Eset Smart Security. Process Explorer neumožňuje operaci s jeho oknem, ale výše uvedený kód nalezne jeho Handle a je na uživateli jakou operaci s oknem provede.

Kód lze samozřejmě měnit a upravovat dle vlastní potřeby. (Určitě obsahuje i zbytečné deklarace, konstanty apod. – nutno vyzkoušet)

Na závěr podotýkám, že byl odzkoušen s VB2005 EE s .NET Framework 2 ve WindowsXP.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback