伴随窗口主要功能点:
- 保持和目标窗口相同的Z序
- 随伴随窗口移动、关闭、隐藏
保持Z序
cpp
//SetOwner 设置所属关系,防止窗口可以单独激活, requires administrator privileges
SetWindowLongPtr(m_hWnd, GWLP_HWNDPARENT, (LONG_PTR)hCompanitionWnd);
随窗口移动、关闭、隐藏
cpp
SetWinEventHook 抓取窗口事件,并且处理事件
具体实现
cpp
#pragma once
#include <windows.h>
#include <string>
#include "WebWnd.h"
class CVALCompanitionWnd
{
public:
static CVALCompanitionWnd& Instance();
void Attach(const std::wstring& wurl,HWND hCompanitionWnd);
void Detach();
private:
LRESULT HandleMessage(CWebWnd* pWeb, UINT uMsg, WPARAM wParam, LPARAM lParam);
static void CALLBACK WinEventProc(HWINEVENTHOOK hook, DWORD event, HWND hWnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime);
void UpdatePos();
void UpdateWorkRect();
private:
CVALCompanitionWnd();
HWINEVENTHOOK m_hook;
HWND m_hCompanitionWnd;
HWND m_hWnd;
BOOL m_isLoadOK;
BOOL m_isCompanition;//是否伴随
BOOL m_isUserMoved;//是否用户主动移动
RECT m_rtVWrok;//工作区域
};
cpp
#include "stdafx.h"
#include "VALCompanitionWnd.h"
#include "WebWnd.h"
CVALCompanitionWnd& CVALCompanitionWnd::Instance()
{
static CVALCompanitionWnd sInstance;
return sInstance;
}
CVALCompanitionWnd::CVALCompanitionWnd()
{
m_hook = nullptr;
m_hWnd = nullptr;
m_hCompanitionWnd = nullptr;
m_isUserMoved = FALSE;
m_isCompanition = TRUE;
m_isLoadOK = FALSE;
}
void CVALCompanitionWnd::Attach(const std::wstring& wurl, HWND hCompanitionWnd)
{
XLOGI("CVALCompanitionWnd::Attach url:%s,hCompanitionWnd:%I64d", W2ACSTR(wurl), hCompanitionWnd);
m_hCompanitionWnd = hCompanitionWnd;
if (!CWebWnd::WindowFromUrl(wurl))
{
UpdateWorkRect();
CWebWnd* pWebWnd = CWebWnd::Open(_T("VAL伴随"), wurl, L"",0, 0, true, true, 0,
std::bind(&CVALCompanitionWnd::HandleMessage,this,std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
//默认隐藏,加载成功之后显示
pWebWnd->SetRoundCorner(0,0);
pWebWnd->ShowWindow(false, false);
m_hWnd = *pWebWnd;
}
if(m_isLoadOK)
::ShowWindow(m_hWnd,SW_SHOWNOACTIVATE);
//SetOwner 设置所属关系,防止窗口可以单独激活,需要管理员权限
SetWindowLongPtr(m_hWnd, GWLP_HWNDPARENT, (LONG_PTR)hCompanitionWnd);
//更新位置
UpdatePos();
if (m_hook)
UnhookWinEvent(m_hook);
m_hook = nullptr;
DWORD dwProcessId = 0;
DWORD dwThreadId = GetWindowThreadProcessId(hCompanitionWnd, &dwProcessId);
m_hook = SetWinEventHook(EVENT_MIN, EVENT_MAX, NULL, &CVALCompanitionWnd::WinEventProc, dwProcessId, dwThreadId, WINEVENT_OUTOFCONTEXT);
XLOGI("CVALCompanitionWnd::SetWinEventHook Hook:%I64d", m_hook);
}
void CVALCompanitionWnd::Detach()
{
XLOGI("CVALCompanitionWnd::Detach");
m_hCompanitionWnd = nullptr;
m_isCompanition = TRUE;
m_isUserMoved = FALSE;
if (m_hook)
UnhookWinEvent(m_hook);
m_hook = nullptr;
if(::IsWindow(m_hWnd))
::ShowWindow(m_hWnd, SW_HIDE);
}
void CVALCompanitionWnd::UpdatePos()
{
static const int W = 264;
RECT rtWindow;
//如果不伴随则不调整位置
if (!m_isCompanition || !GetWindowRect(m_hCompanitionWnd, &rtWindow)) return;
XLOGI("CVALCompanitionWnd::Attach HWND:%I64d rt{%d,%d,%d,%d}\r\n", m_hCompanitionWnd, rtWindow.left, rtWindow.top, rtWindow.right, rtWindow.bottom);
rtWindow.left = rtWindow.right;
rtWindow.right = rtWindow.left + (W*GetDpi()*1.0)/100.0;
if (rtWindow.right > m_rtVWrok.right)
{
rtWindow.right = m_rtVWrok.right;
rtWindow.left = rtWindow.right - (W * GetDpi() * 1.0) / 100.0;
}
SetWindowPos(m_hWnd, NULL, rtWindow.left, rtWindow.top, rtWindow.right - rtWindow.left, rtWindow.bottom - rtWindow.top, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW);
}
void CVALCompanitionWnd::UpdateWorkRect()
{
int totalWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN); // 总宽度
int totalHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN); // 总高度
int startX = GetSystemMetrics(SM_XVIRTUALSCREEN); // 虚拟桌面左上角X坐标(可能为负)
int startY = GetSystemMetrics(SM_YVIRTUALSCREEN); // 虚拟桌面左上角Y坐标
m_rtVWrok.left = startX;
m_rtVWrok.top = startY;
m_rtVWrok.right = m_rtVWrok.left + totalWidth;
m_rtVWrok.bottom = m_rtVWrok.top + totalHeight;
XLOGI("CVALCompanitionWnd UpdateWorkRect[l:%d,t:%d,r:%d,b:%d]",
m_rtVWrok.left, m_rtVWrok.top, m_rtVWrok.right, m_rtVWrok.bottom);
}
LRESULT CVALCompanitionWnd::HandleMessage(CWebWnd* pWeb, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == UM_UPDATEWEB_LOADSTATUS)
{
m_isLoadOK = BOOL(lParam);
XLOGI("CVALCompanitionWnd UM_UPDATEWEB_LOADSTATUS web load:%s", m_isLoadOK ? "successed" : "failed");
if (m_isLoadOK && IsWindow(m_hCompanitionWnd) && !::IsIconic(m_hCompanitionWnd))
ShowWindow(*pWeb, SW_SHOWNOACTIVATE);
}
else if (uMsg == WM_DISPLAYCHANGE || uMsg == WM_DPICHANGED)
{
UpdateWorkRect();
UpdatePos();
}
else if (uMsg == WM_SHOWWINDOW)
{
if (pWeb)
pWeb->NotifyWindowActive(BOOL(wParam), ::IsZoomed(*pWeb));
}
else if (uMsg == WM_NCLBUTTONDOWN)
{
m_isUserMoved = TRUE;
}
else if (uMsg == WM_NCLBUTTONUP)
{
m_isUserMoved = FALSE;
}
else if(uMsg == WM_MOVE)
{
if (m_isUserMoved)
{
m_isUserMoved = FALSE;
RECT rtCompanition;
RECT rtWnd;
//如果区域超过80则认为脱离伴随
if (GetWindowRect(m_hCompanitionWnd, &rtCompanition) &&
GetWindowRect(*pWeb, &rtWnd))
{
int offset_x = abs(rtWnd.left - rtCompanition.right);
int offset_y = abs(rtWnd.top - rtCompanition.top);
if (offset_x >= 50 ||
offset_y >= 50)
{
m_isCompanition = FALSE;
}
else
{
m_isCompanition = TRUE;
//如果误差大于20则重新定位
if (offset_y >= 10 || offset_x >= 10)
UpdatePos();
}
}
}
}
else if (uMsg == WM_DESTROY)
{
Instance().Detach();
}
return FALSE;
}
void CALLBACK CVALCompanitionWnd::WinEventProc(HWINEVENTHOOK hook, DWORD event, HWND hWnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
{
DWORD dwCurThreadid = GetCurrentThreadId();
if (event == EVENT_OBJECT_LOCATIONCHANGE) {
//如果窗口移动
if (idObject == OBJID_WINDOW && idChild == CHILDID_SELF)
{
if (hWnd == Instance().m_hCompanitionWnd)
{
if (::IsIconic(hWnd))
ShowWindow(Instance().m_hWnd, SW_HIDE);
else
{
static bool bfisrt = true;
if (Instance().m_isLoadOK && !IsWindowVisible(Instance().m_hWnd))
{
if (bfisrt)
{
bfisrt = false;
SetWindowPos(Instance().m_hWnd, NULL, 0, 0, 1, 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
}
else
{
SetWindowPos(Instance().m_hWnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW);
}
}
Instance().m_isUserMoved = FALSE;
Instance().UpdatePos();
}
}
}
}
else if (event == EVENT_OBJECT_HIDE)
{
XLOGI("WinEventProc EVENT_OBJECT_HIDE idObject:%I64d,idChild:%I64d", idObject, idChild);
if (idObject == OBJID_WINDOW && idChild == CHILDID_SELF)
{
if (hWnd == Instance().m_hCompanitionWnd)
::ShowWindow(Instance().m_hWnd, SW_HIDE);
}
}
else if (event == EVENT_OBJECT_SHOW)
{
XLOGI("WinEventProc EVENT_OBJECT_SHOW idObject:%I64d,idChild:%I64d", idObject, idChild);
if (idObject == OBJID_WINDOW && idChild == CHILDID_SELF)
{
if (hWnd == Instance().m_hCompanitionWnd)
::ShowWindow(Instance().m_hWnd, SW_SHOWNORMAL);
}
}
else if (event == EVENT_OBJECT_DESTROY)
{
if (idObject == OBJID_WINDOW && idChild == CHILDID_SELF)
{
//建议定时器通过IsWindow来检查窗口是否存在,如果不存在则UnhookWinEvent
if (hWnd == Instance().m_hCompanitionWnd)
{
Instance().Detach();
}
}
}
//窗口移动
else if (event == EVENT_SYSTEM_MOVESIZEEND)
{
int a = 0;
}
}