点击链接加入群【ゞ***闖兲ゞ】:

问题引出:

    今天看了一下 深入解析MFC,第40页到第50页之间,有一句


  wKioL1OexYmisi79AAG7Pp4DiLM527.jpg


这里面说到了一个函数,,, GetActiveWindow,,根据直观意思很容易理解 就是获取  活动窗口的句柄,但是什么才是活动窗口。


Retrieves a pointer to the active window.

//获取一个指向活动窗口的CWnd指针。
static CWnd* PASCAL GetActiveWindow( );

Return Value //返回值

The active window or NULL if no window was active at the time of the call. The pointer may be temporary and should not be stored for later use.

//返回活动窗口的句柄,如果在调用这个函数时没有活动窗口,则返回NULL。这个指针可能是指向临时对象 因此不要 将它保存起来以备后来之用。

Remarks//说明

The active window is either the window that has the current input focus or the window explicitly made active by the SetActiveWindow member function.

//活动窗口是 拥有输入焦点的窗口。显示使用 SetActiveWindow 这个函数来指定的窗口也是活动窗口。


上面是MSDN中给出的解释 ,我做了简单的翻译。其实软件技术本身并不复杂,复杂的是我们弄不清楚概念。但是上面 说明处 也给出了概念。那么我们就来测试一下吧。


用VS2008 新建一个MFC 对话框程序。

然后再上面 对话框上放 2 个 编辑框(这样就可以有输入焦点了)。


然后 在 OnPaint 中添加如下代码

void CTestDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // device context for painting

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // Center icon in client rectangle
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // Draw the icon
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        //新添加
        HWND    hwndEdit1 = ((CEdit *)GetDlgItem(IDC_EDIT1))->m_hWnd;
        HWND    hwndEdit2 = ((CEdit *)GetDlgItem(IDC_EDIT2))->m_hWnd;
        CString    str;
        str.Format("edit1:%x  edit2:%x  dlg:%x",hwndEdit1,hwndEdit2,m_hWnd);
        CPaintDC  dc(this);
        dc.TextOut(0,0,str);
        CDialog::OnPaint();
    }
}

其实功能就是在  对话框的客户区上输出  两个编辑框和主对话框的句柄值。

运行效果如下:

wKiom1Oe0ByxAzZIAABfNXFrqU4895.jpg


下面我们应该如何获取一下 活动窗口的句柄呢,就要用那个函数了,,我们来试试,问题是在哪里使用这个函数呢?

因为要输入焦点,而我们还想要切换焦点,所以,我想到了计时器,我们启动一个计时器,比如10秒一次,在这里面来调用函数。

在   BOOL CTestDlg::OnInitDialog()  里面加上 SetTimer(1,10000,NULL);

为对话框添加 WM_TIMER 消息响应函数

void CTestDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: Add your message handler code here and/or call default
    switch (nIDEvent)
    {
    case 1:
        {
            CWnd    * pWnd = CWnd::GetActiveWindow();
            ASSERT(pWnd != NULL);
            CString  str;
            str.Format("%x",pWnd->m_hWnd);
            AfxMessageBox(str);
        }
        break;
    default: 
        break;
    }
    CDialog::OnTimer(nIDEvent);
}

这样子,我们的程序会每隔10秒钟就弹出一个值,我们来测试一下,在测试过程中,我们可以对比弹出的值与对话框中输出的三个值的关系,我们还可以点击两个编辑框,以让它们获取输入焦点。


如果不出意外,那么这个弹出的值会是 对话框的句柄,那么什么时候会出意外呢,就是你把其它窗口点开了,比如你切换到了浏览器窗口或者其它应用程序。这个时候 程序会出现 断言错误,就是

 ASSERT(pWnd != NULL);

这时候,pWnd获取的值是 NULL。

这里很奇怪。

1、为什么总是 对话框的句柄呢

2、为什么一切换到其它窗口就命中断言了呢


我们去看一下这个函数的实现吧。

_AFXWIN_INLINE CWnd* PASCAL CWnd::GetActiveWindow()
    { return CWnd::FromHandle(::GetActiveWindow()); }

这是先调用API 函数  GetActiveWindow  ,以获取 句柄值,然后通过句柄值检索对象或者构造临时对象。

那么我们就去看一下 这个API 的说明吧。


The GetActiveWindow function retrieves the window handle to the active window attached to the calling thread's message queue.

// 函数 GetActiveWindow 获取 关联到 调用线程的消息队列 的 活动窗口的句柄值。


Syntax

HWND GetActiveWindow(VOID);

Return Value

The return value is the handle to the active window attached to the calling thread's message queue. Otherwise, the return value is NULL.

//意思跟上面差不多,但这里我要说明一点知识,那就是一个线程只有一个消息队列,一个线程可以创建多个窗口,一个线程最多有一个活动窗口,也可能没有,明白这一点后你再看这点就可以理解了。


Remarks

To get the handle to the foreground window, you can use GetForegroundWindow.

Windows 98/Me and Windows NT 4.0 SP3 and later: To get the window handle to the active window in the message queue for another thread, use GetGUIThreadInfo.

//如果要获取前台窗口的句柄,你应该使用  GetForegroundWindow 。

//如果要获取其它线程中的活动窗口,你应该使用  GetGUIThreadInfo。


好了,说了这么多,我们终于有点眉目了。

虽然编辑框也是窗口,但这个函数中说明的窗口是指

,而不是

  其实窗口还有另外两种类型


好了,这样我们就明白了,编辑框是窗口不假,但是它是子窗口,而且在MFC对这个函数的说明不是太明确。


现在也就是 如果 我们再开一个工作线程,在这里来 用 GetActiveWindow  ,这是肯定不行的,因为我们的这个工作线程 自身 并没有创建窗口。当我们激活一个窗口时,就是在任务栏点击一个窗口程序,这个时候这个程序就拥有了输入焦点,它就是活动窗口。


还是之前的例子,我们在 程序的 标题栏上右击,选择关于 ,此时会再弹出一个模态对话框,如下图

wKioL1Oe10rwMACyAADXu9lpi1M613.jpg


可以看出不再是主对框了,因为在没有弹出Test 那个对话框之前,About Test 才是 这个线程 中的 活动窗口。如果再等 几十秒(不要点确定),会发现 他还会弹出 提示框,而且每次的结果都不一样,

wKioL1Oe2BKiMbNWAADtWYmvatI093.jpg


这是因为当第一个提示框弹出后,它就变成了活动窗口了,第2个弹出的就是第一个的句柄了,第3个弹出的就是第二个的句柄了。


当我们一切到其它窗口的时候,程序会因为断言宏 的问题 而中断。


还有一个问题 就是,我们可以开一个线程,来获取 其它线程中的 活动窗口。


定义一个线程


UINT __cdecl Worker( LPVOID pParam )
{
    CTestDlg  * pDlg = (CTestDlg *)pParam;
    ASSERT(pDlg != NULL);
    DWORD  tid = GetWindowThreadProcessId(pDlg->m_hWnd,NULL);
    GUITHREADINFO   guiTI = {0};
    guiTI.cbSize = sizeof(GUITHREADINFO);
    GetGUIThreadInfo(tid,&guiTI);
    CString  str;
    str.Format("%x\n",guiTI.hwndActive);
    AfxMessageBox(str);
    return 0;
}


然后将 计时器  中 换成

    switch (nIDEvent)
    {
    case 1:
        {
            AfxBeginThread(Worker,(LPVOID)this);
        }
        break;


wKioL1Oe2n2TFqduAAHVx9oPFX4044.jpg


这样子依旧可以获取 其 活动窗口,当然可以利用这种方式 来获取  其它 软件 的 活动窗口哦。


如果还有不明白的地方,那就是前台窗口 与 活动窗口 这两个概念的 区别了。


不妨来做个测试。

我们随便写个小按钮响应事件


//点击开始菜单,点击运行,,输入  notepad 回车即可

    HWND     hwndNotepad = ::FindWindow(NULL,"无标题 - 记事本");
    ASSERT(hwndNotepad != NULL);
    ::SetForegroundWindow(hwndNotepad);


功能就是将记事本  置成前台,我们发现 记事本  果然就 跑前面来了,而且也被激活了。


那么它们到底有什么区别吗?


1、设置都没有什么好说的

2、获取,一个是针对某一线程进行获取,一个是针对整个系统中的所有窗口进行获取

3、反上去,其它设置也是范围不同,一个针对线程,另一个针对系统。


什么时候应该哪个,要取决于你具体的目的是什么,然后再做一个判断。


其实说到这里应该结束了,但是其实还有一点,就是如何 在一个线程中,激活另一个线程中的 窗口,说白了就是 线程A 没有窗口,线程B有3个窗口,我想用A 激活B中的 2号窗口,怎么办。 获取是 使用的GetGUIThreadInfo ,设置 可没有  SetGUIThreadInfo 这个函数的,


这个时候 ,我们应该行看看MSDN中有没有办法


By using the AttachThreadInput function, a thread can attach its input processing to another thread. This allows a thread to call SetActiveWindow to activate a window attached to another thread's message queue.


可以看到 可以使用 AttachThreadInput 这个函数可以将 一个线程 的输入处理 关联到 另一个线程,进而 完成 设置 其它线程中的 活动窗口的 目的。


可能有的人还不清楚 一个程序到底有多少个窗口,哈哈,这个是初学者最不容易理解的地方,一个程序可以有多个窗口的,举一个例子吧,VS2008 等类似的软件有吧,,知道怎么设置行号吗,知道怎么改变字体大小吗,知道怎么新建工程吗,知道怎么添加文件吗,好吧,这些功能,每一个都是一个对话框哦,再比如,一个记事本程序,里面有搜索功能吧,这也是一个对话框,还有 最后一个例子,


直接上图

wKiom1Oe4C3jiNAyAANCwasOPU0730.jpg


好了,我想大家对活动窗口应该理解了吧。。。。