如何获得指定进程的主窗口

我们知道一个进程可能是没有主窗口(比如系统的服务进程)而有的进程可能又拥有不止一个的主窗口(比如OutLook软件),那么我们该如何才能取得指定进程的所有主窗口呢?

我们必须明确我们感兴趣“主窗口”这个概念,到底什么样的窗口才是主窗口,他应该具有那些特点。
首先,主窗口必须是可见的也就是拥有WS_VISABLE属性(其实也有很多不可见的主窗口,但是我们在这里暂不考虑
      这一点)
其次,主窗口应该是没有父窗口,也就是他的父窗口句柄为NULL.。

有了这两个条件我们就可以着手实现部分了。

要取得指定进程的主窗口的一般方法为
使用EnumWindows函数,枚举出所有的符合主窗口条件的上层窗口,然后通过GetWindowThreadProcessId函数获得
窗口所属的进程ID,如果所得到的进程ID和已知的进程ID相同,就说明该窗口是这个进程的一个主窗口。
上面就是基本的思路。

下面是本人设计的一个类,用来实现此项功能

#pragma once
#include <windows.h>
#include <vector>
class CProcessMainWndArray {
    DWORD m_dwProcessId;
    std::vector<HWND> m_vHwnd;
public:
    CProcessMainWndArray(DWORD dwProcessId = 0)
     : m_dwProcessId(dwProcessId) {
        m_vHwnd.clear();
        EnumWindows(enumProc, (LPARAM)this);
    }
    ~CProcessMainWndArray() {};
private:
    static BOOL __stdcall enumProc(HWND hWnd, LPARAM lParam) {
        CProcessMainWndArray* pWndIterator = (CProcessMainWndArray*)lParam;
        DWORD dwCurProcessId;
        GetWindowThreadProcessId(hWnd, &dwCurProcessId);
        if ((GetWindowLong(hWnd, GWL_STYLE) & WS_VISIBLE) &&
            GetParent(hWnd) == NULL &&
            (dwCurProcessId == pWndIterator->getCurProcessId())) {
            pWndIterator->getMainHwndArray()->push_back(hWnd);
        }
        return TRUE;
    }
private:
    DWORD getCurProcessId()
        { return m_dwProcessId; }
    std::vector<HWND>* getMainHwndArray()
        { return &m_vHwnd; }
public:
    bool isEmpty()
        { return m_vHwnd.empty(); }
    std::vector<HWND>::iterator begin()
        { return m_vHwnd.begin(); }
    std::vector<HWND>::iterator end()
        { return m_vHwnd.end(); }
    size_t size()
        { return m_vHwnd.size(); }
    HWND operator[](int nIndex)
        { return m_vHwnd[nIndex]; }
};

该类的使用非常简单,例如

#include <iostream>

int main()
{
    //0x0524为目标进程的ID
    CProcessMainWndArray wnd(0X0524);

    if (wnd.isEmpty()) {
        std::cout << "There are no main windows in target process" <<  std::endl;
        return -1;
    }
    //第一种方法
    std::cout << "The handles of this process are : " << std::endl;

    std::vector<HWND>::iterator iter = wnd.begin();
    for (iter; iter != wnd.end(); ++iter) {
        char szHwnd[MAX_PATH] = { 0 };
        sprintf(szHwnd, "0x%x", *iter);
        std::cout << szHwnd << std::endl;
    }

    //第二种方法
    std::cout << "The handles of this process are : " << std::endl;

    for (size_t i = 0; i < wnd.size(); i++) {
        char szHwnd[MAX_PATH] = { 0 };
        sprintf(szHwnd, "0x%x", wnd[i]);
        std::cout << szHwnd << std::endl;
    }
 
    return 0;
}

//类的构造函数
//参数:dwProcessId目标进程的ID
//该函数的功能主要是枚举系统的所有上层窗口,并将窗口句柄记录在vector之中
CProcessMainWndArray(DWORD dwProcessId = 0)
  : m_dwProcessId(dwProcessId) {
    m_vHwnd.clear();
    EnumWindows(enumProc, (LPARAM)this);
}

//枚举窗口的回调函数
//参数:hWnd窗口句柄
//      lParam:应用程序定义的的32位值,在这里为CProcessMainWndArray类型的指针
//函数功能:回调函数用来枚举所有的上层窗口,并将符合条件窗口句柄存入m_vHwnd之中
//
static BOOL __stdcall enumProc(HWND hWnd, LPARAM lParam) {
    CProcessMainWndArray* pWndIterator = (CProcessMainWndArray*)lParam;
    DWORD dwCurProcessId;
    GetWindowThreadProcessId(hWnd, &dwCurProcessId);
    if ((GetWindowLong(hWnd, GWL_STYLE) & WS_VISIBLE) &&
        GetParent(hWnd) == NULL &&
        (dwCurProcessId == pWndIterator->getCurProcessId())) {
        pWndIterator->getMainHwndArray()->push_back(hWnd);
    }
    return TRUE;
}

//得到进程ID
DWORD getCurProcessId()
    { return m_dwProcessId; }

//得到记录窗口句柄的m_vHwnd的指针
std::vector<HWND>* getMainHwndArray()
    { return &m_vHwnd; }

//判断数组(m_vHwnd)是否为空,如果为空,说明该进程没有主窗口
bool isEmpty()
    { return m_vHwnd.empty(); }

//得到指向m_vHwnd第一个元素的迭代器
std::vector<HWND>::iterator begin()
    { return m_vHwnd.begin(); }

//得到指向m_vHwnd最后一个元素后面位置的迭代器
std::vector<HWND>::iterator end()
    { return m_vHwnd.end(); }

//取得m_vHwnd中句柄的数量
size_t size()
    { return m_vHwnd.size(); }

//运算符重载,用于返回存在于m_vHwnd数组中指定下标的句柄
HWND operator[](int nIndex)
    { return m_vHwnd[nIndex]; }


本例是参考msdn上的一篇文章,然后根据自己的思路重写了一个,放在这里和大家共享,希望能起到一个
抛砖引玉的作用,如果大家有其他好的方法,希望不吝赐教! :)

### 回答1: 在Visual Basic中,可以通过使用Windows API函数来获取指定进程名称的窗口句柄。下面是一个实现该功能的示例代码: ```vb Imports System.Runtime.InteropServices Public Class Form1 ' 导入Windows API函数 <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr End Function <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> Private Shared Function GetWindowText(ByVal hWnd As IntPtr, ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As Integer End Function ' 定义方法来获取进程窗口句柄 Private Function GetWindowHandleByProcessName(ByVal processName As String) As IntPtr Dim processes As Process() = Process.GetProcessesByName(processName) If processes.Length > 0 Then ' 获取第一个匹配进程窗口句柄 Return processes(0).MainWindowHandle End If Return IntPtr.Zero End Function Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim processName As String = "notepad" Dim windowHandle As IntPtr = GetWindowHandleByProcessName(processName) If windowHandle <> IntPtr.Zero Then Dim windowTitle As New StringBuilder(256) GetWindowText(windowHandle, windowTitle, windowTitle.Capacity) MessageBox.Show("找到进程名为 '" + processName + "' 的窗口句柄:" + windowHandle.ToString() + vbCrLf + "窗口标题:" + windowTitle.ToString()) Else MessageBox.Show("未找到进程名为 '" + processName + "' 的窗口句柄") End If End Sub End Class ``` 在上面的示例中,`FindWindow`用于查找带有指定类名和窗口名称的顶层窗口句柄,`GetWindowText`用于获取指定窗口的标题文本。通过使用`Process.GetProcessesByName()`方法获取指定进程名称的进程列表,然后从中获取第一个进程窗口句柄,即可获得所需的窗口句柄。 ### 回答2: 在VB中,我们可以通过进程获得窗口句柄,具体步骤如下: 1. 首先,我们需要引用Windows API函数,以便使用相关的函数和结构体。在VB中,可以使用Declare语句引用API函数。 ```vb Declare Function FindWindowExA Lib "user32" (ByVal hWndParent As Long, ByVal hWndChildAfter As Long, ByVal lpszClass As String, ByVal lpszWindow As String) As Long Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long ``` 2. 接下来,我们可以通过进程名遍历系统中的进程,找到目标进程窗口句柄。我们使用FindWindowExA函数,该函数通过父窗口句柄、子窗口句柄、类名和窗口标题来查找窗口。 ```vb Dim hWnd as Long Dim hWndProcess as Long Dim procID as Long hWnd = FindWindowExA(0, 0, vbNullString, "目标进程窗口标题") ``` 3. 一旦我们找到窗口句柄,我们可以使用GetWindowThreadProcessId函数获取进程ID。 ```vb GetWindowThreadProcessId hWnd, procID ``` 4. 最后,我们可以使用进程ID和窗口句柄进行进一步的操作,例如向窗口发送消息、操纵窗口等。 需要注意的是,通过进程获得窗口句柄可能存在一些限制。因为一个进程可以创建多个窗口,而窗口标题也可能不唯一,所以在使用FindWindowExA函数时,我们需要确保目标窗口的标题是唯一的,以免出现误判的情况。 总之,通过以上步骤,我们可以在VB中通过进程获得窗口句柄,并且可以进一步使用该句柄进行相关操作。 ### 回答3: 在VB中,我们可以通过以下步骤来通过进程获取窗口句柄。 首先,我们需要使用System.Diagnostics命名空间中的Process类来获取已经运行的所有进程的详细信息。我们可以使用Process.GetProcessesByName方法来获取指定进程名的进程对象数组。 接下来,我们可以遍历这个进程对象数组,通过使用Process.MainWindowTitle属性来获取进程窗口标题,并与我们想要的进程名进行比较。 一旦找到匹配的进程,我们可以使用Process.MainWindowHandle属性来获取窗口的句柄。这个句柄可以用于后续的窗口操作。 下面是一个通过进程获取窗口句柄的示例代码: ``` Imports System.Diagnostics Imports System.Runtime.InteropServices Public Class Form1 <DllImport("user32.dll", SetLastError:=True)> Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr End Function Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim procName As String = "Notepad" ' 要查找的进程名 Dim processes() As Process = Process.GetProcessesByName(procName) If processes.Length > 0 Then Dim mainWindowHandle As IntPtr = processes(0).MainWindowHandle ' 使用窗口句柄进行后续操作 MessageBox.Show("窗口句柄:" & mainWindowHandle.ToString()) Else MessageBox.Show("未找到指定进程") End If End Sub End Class ``` 以上是一个基本的示例,你可以根据实际需求进行扩展和修改,例如通过进程名模糊匹配等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值