使用EnumWindows函数通过Windows进程ID获取窗口句柄实例(VB.NET2019)

  有时候我们需要通过窗口的进程ID获取窗口的句柄,在windows中又没有直接的函数可用,这时候我们只能曲线救国,通过EnumWindows函数枚举所有顶级窗口,然后用GetWindowThreadProcessId函数来获取已知进程ID的窗口句柄。网上有很多C和C++EnumWindows函数的使用实例和详细解释,但是VB.NET中EnumWindows函数的详细解释和实例资源非常稀少,我也是费了很长时间才把C++语言改写为VB.NET语言的,并在Windows7和vs2019中实测通过。希望能对有此困惑的人有所帮助!
  顺便说一下,本人是外科医生,编程实属业余爱好,不务正业之举,编程方面菜鸟一枚,有不对的望各位大佬指正,批评!!!
  废话不多说,开始正题。
  本文涉及以下几方面的内容:
  1、API函数EnumWindows 及GetWindowThreadProcessId在VB.NET中的使用
  2、函数中ByVal和ByRef的使用注意事项

  一、EnumWindows 函数的运行流程

  EnumWindows 函数(枚举顶级窗口)和EnumChildWindows函数(枚举指定父窗口的子窗口)运行流程都差不多,会了一个就会另外一个。
  EnumWindows 枚举窗口,将窗口句柄传递给其回调函数,回调函数根据这个句柄进行相关的判断和处理事件,并返回一个布尔值给EnumWindows,如果返回true则EnumWindows 函数继续枚举,返回false则停止枚举。
  EnumWindows 和其回调函数用第二个参数传递程序需要的值。(这句话比较难懂,也是这个函数的难点,下面会有详细的解释)

  二、函数的声明

   两个函数在vb中的声明:

Declare Sub EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Declare Function GetWindowThreadProcessId Lib "user32" Alias "GetWindowThreadProcessId" (ByVal hwnd As Long, lpdwProcessId As Long) As Long

  两个函数在vb.net中的声明:

Private Declare Ansi Function EnumWindows Lib "user32" Alias "EnumWindows"(ByVal lpEnumFunc As EnumWindowsProc, ByRef lParam As EnumWindowsArg) As Integer  '声明API函数EnumWindows
Private Declare Function GetWindowThreadProcessId Lib "user32" Alias "GetWindowThreadProcessId" (ByVal hwnd As Integer, ByRef lpdwProcessId As Integer) As Integer '第二个参数一定要用ByRef类型。

  vb.net中声明的几点说明:
   1、Function 后面的EnumWindows 只是函数的名称,你可以随便改成自己喜欢的名字,但是Alias 后面的名称不能改,那是windows函数库中的标准名称,改了程序就找不到库中的函数了。GetWindowThreadProcessId
类同。
   2、Ansi的意义:EnumWindows 声明时在Function 前要不要加Ansi ,网上查的资料说需要加,我不加测试也通过了。还请明白声明时Ansi的意义的大佬指点一下;
   3、EnumWindows 函数第二个参数lParam 的意义:
   原文是这样写的:在枚举期间,传递给dwcbkd32.ocx定制控件之EnumWindows事件的值。这个值的含义是由程序员规定的。
   关于这个参数的含义和用法查了很多资料,都是c语言的,而且也没有表达清楚到底怎么传递参数的值。vb.net的相关资料几乎没有。经过我的不断摸索,终于搞清楚这个参数的用法,在此分享一下:
   EnumWindows 函数第二个参数 “lParam” 只是变量名,你可以随便改 ,变量前面的ByVal 和ByRef 使用要小心,ByVal 的意思是只传递变量的值,ByRef 只传递变量的地址。EnumWindows函数使用第二个参数和其回调函数某个参数来回传递变量的地址,也就是说回调函数某个参数的值发生改变,EnumWindows函数的第二个参数的值也随之而改变,这是函数的要求,所以你如果用第二个参数,必须用传递地址的方式使用第二个参数,当然你也可以不用第二个参数。还有一点,EnumWindows函数第二个参数 “lParam” 可以是你自己定义的变量类型,本例就是我自己定义的变量类型:EnumWindowsArg,具体定义如下:

Structure EnumWindowsArg
        Dim HwndWindow As Integer
        Dim ProcessID As Integer
End Structure

   此变量包含有窗口句柄和窗口进程ID两个属性。你也可以把第二个参数定义成你自己需要的数据类型。
   4、声明到此,还没完,你还需用Delegate声明一个委托函数,因为VB.NET中,EnumWindows函数不能直接调用你的回调函数,不声明为委托函数,你用AddressOf是无效的,无法获取回调函数的地址。
   什么是委托?
   简单的理解,委托就是对一个方法映射,把方法名当参数来传递,类似JS中的在某个function的参数中把另一个function的名称当参数来传递。也就是把函数(一种方法的定义)名当作另一个函数的参数使用。委托可以理解为一种接口,具体的方法实现这个接口,在开发阶段只需要关心委托的定义就可以调用,而不用关心它如何实现的或者在哪里实现的。
   vb.net中委托函数调用时要先实例化,然后通过AddressOf调用实例化的指针来执行委托函数的方法。
   是不是有点云山雾罩的感觉了,那就对了,看个实例就全都理解了。

   定义委托函数(即回调函数的方法,相当于类,class):

Private Delegate Function EnumWindowsProc(ByVal Hwnd As Integer, ByRef ewa As EnumWindowsArg) As Boolean '声明一个委托函数

  三、具体代码

   1、回调函数代码:

 '自定义回调函数,EnumWindows函数接收其返回值,返回true则继续enum,返回false则停止enum。
    '参数Hwnd为EnumWindows函数枚举窗口的句柄
    '参数ewa为按址传值的自定义参数,其参数名称可以和EnumWindows函数里第二个参数不一样,但参数类型必须与EnumWindows函数第二个参数相同。
    '相当于和EnumWindows共用的一块地址,两个函数都可以更改和读取这个地址里的值。
    '注意:这个回调函数要与定义的委托函数的参数类型要兼容,最好相同。
    Private Function EnumWindowsProc_GetHwndByPID(ByVal Hwnd As Integer, ByRef ewa1 As EnumWindowsArg) As Boolean
        Dim pArg As EnumWindowsArg = ewa1
        Dim ProcessID As Integer

   '通过枚举的窗口句柄用windows api 函数 GetWindowThreadProcessId取得窗口进程ID
        '与要查找的窗口进程比较,相同,则找到了该进程的窗口句柄。
        Dim threadID = GetWindowThreadProcessId(Hwnd, ProcessID)
        If ProcessID = pArg.ProcessID Then
            pArg.HwndWindow = Hwnd
            '找到了返回FALSE
            ' Return False
        End If
        If threadID = pArg.ProcessID Then
            pArg.HwndWindow = Hwnd
            Return False
        End If
        '没找到,继续找,返回TRUE
        Return True

   End Function

   2、主函数

 Public Function GetWindowhandleByProcessID(ByVal ProcessID As Integer) As Integer '通过窗口的进程ID获取窗口句柄
        Dim Hwndrtn As Integer '返回窗口句柄的变量
        Dim ewa As EnumWindowsArg '自定义的变量,用来传递窗口进程和窗口句柄
        ewa.ProcessID = ProcessID '
        ewa.HwndWindow = 0
        Dim Prc As EnumWindowsProc = New EnumWindowsProc(AddressOf EnumWindowsProc_GetHwndByPID) '实例化一个回调函数

        'EnumWindows枚举窗口,prc为实例化的回调函数指针(地址),EnumWindows将枚举窗口的句柄传递给回调函数prc处理。 
        'ewa为向回调函数按址传递的一个参数, 就是可以向回调函数传值, 也可以接收从回调函数返回的值。
        EnumWindows(Prc, ewa)
        If ewa.HwndWindow <> 0 Then
            Hwndrtn = ewa.HwndWindow
        End If
        Return Hwndrtn
    End Function
    原文作者:mengxiangde
    原文地址: https://blog.csdn.net/mengxiangde/article/details/102672087
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞