通过枚举窗口,实现最小化到托盘中程序的窗口显示
我得写点东西来总结一下我这两天的郁闷后最终的收获……
目的很简单,某一特定应用程序最小化到托盘中,我需要编程实现显示它的主窗口。
1.首先想到的是通过直接操作进程,通过Findwindow找到这个窗口句柄,然后给其发送一个showwindow消息将其显示。 类“#
HWND hwnd = ::FindWindow(_T("#32770"),_T(""));
::ShowWindow(hwnd,SW_SHOWNORMAL);
但如此得到的句柄总是为空。通过实验发现,一旦程序最小化到托盘后,其窗口句柄就无法被捕获。(通过另一程序来枚举当前开的窗口:LP)
2.失败后,转向托盘编程,考虑在托盘中能获得相关信息:
标准三部曲:
// 得到任务栏窗口句柄
HWND hTaskBarWnd = FindWindow( "Shell_TrayWnd", NULL );
// 得到通知区窗口句柄
HWND hTrayNotifyWnd = FindWindowEx( hTaskBarWnd, NULL,
"TrayNotifyWnd", NULL );
// 得到系统托盒窗口句柄
HWND hTrayClockWnd = FindWindowEx( hTrayNotifyWnd,
NULL, "ToolbarWindow32", NULL );
但是在我电脑上就是无法获得第三个句柄(即托盘句柄)。而我使用TrayClockWClass,是可以正常获得。他们都是同一父亲,不知何故?
如果能获取,则可进一步使用
PostMessage(TB_HIDEBUTTON, index,(LPARAM) MAKELONG(FALSE, 0));
SendMessage(TB_PRESSBUTTON, i,(LPARAM)MAKELONG(TRUE, 0)); 来进行相关处理。当然,还有托盘的刷新操作:
void CTrayBar::Refresh()
{
NOTIFYICONDATAW Icon;
Icon.cbSize = sizeof( NOTIFYICONDATAW );
Icon.hWnd = this->m_hWnd ;
Icon.uID = 0;
Icon.uFlags = NIF_MESSAGE | NIF_TIP;
Icon.uCallbackMessage = WM_USER + 777;
Icon.hIcon = NULL;
Shell_NotifyIconW( NIM_ADD , &Icon );
Shell_NotifyIconW( NIM_DELETE , &Icon );
}
3.考虑只获取到通知窗口句柄后,再取得其位置,然后通过每个图标占16 *16 的位置,来推出我的特定程序的图标位置,得到位置后,再模拟鼠标的点击操作,从而实现窗口的显示。代码如下:
void ShowMyApp()
{
HWND hTrayWindow;
RECT rctTrayIcon;
int nIconWidth;
int nIconHeight;
CPoint CursorPos;
int nRow;
int nCol;
// Get tray window handle and bounding rectangle
hTrayWindow = ::FindWindowEx(::FindWindow(
"Shell_TrayWnd", NULL), 0, "TrayNotifyWnd", NULL);
if(!::GetWindowRect(hTrayWindow, &rctTrayIcon))
return;
//CWnd * pwnd = CWnd::FromHandle(hTrayWindow); //hTrayWindow
// Get small icon metrics
nIconWidth = GetSystemMetrics(SM_CXSMICON);
nIconHeight = GetSystemMetrics(SM_CYSMICON);
GetCursorPos(&CursorPos);
for(nRow=0; nRow<(rctTrayIcon.bottom-rctTrayIcon.top)/nIconHeight; nRow++)
{
for(nCol=0; nCol<(rctTrayIcon.right-rctTrayIcon.left)/nIconWidth; nCol++)
{
SetCursorPos(rctTrayIcon.left + nCol * nIconWidth + 10,
rctTrayIcon.top + nRow * nIconHeight + 10);
GetCursorPos(&CursorPos);
LPARAM lParam = MAKELPARAM(CursorPos.x,CursorPos.y);//鼠标点击的坐标
mouse_event(MOUSEEVENTF_LEFTDOWN,CursorPos.x,CursorPos.y,0,NULL);
mouse_event(MOUSEEVENTF_LEFTUP,CursorPos.x,CursorPos.y,0,NULL);
Sleep(0);
}
}
}
如此确实是可以实现我的目标,但是有个大缺点,就是必须知道我的应用程序在托盘中的特定位置,而每次启动Windows后程序在托盘中的位置都是可能不相同的。只有编程实现,正确确定位置,才能模拟鼠标点击。此法还是不行。
4.再次回到进程的方法上来。通过如下代码可获得程序中所有进程,包括没有窗口句柄的:
HANDLE GetProcessHandle(int nID)
{
return OpenProcess(PROCESS_ALL_ACCESS, FALSE, nID);
}
HANDLE GetProcessHandle(LPCTSTR pName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot) {
return NULL;
}
PROCESSENTRY32 pe = { sizeof(pe) };
BOOL fOk;
for (fOk = Process32First(hSnapshot, &pe); fOk; fOk = Process32Next(hSnapshot, &pe)) {
afxDump << pe.szExeFile<< " "<< pe.th32ProcessID<< "\n";
if (!_tcscmp(pe.szExeFile, pName)) {
CloseHandle(hSnapshot);
return GetProcessHandle(pe.th32ProcessID);
}
}
return NULL;
}
void CPMTimerDlg::OnBnClickedOk()
{
HANDLE hd = GetProcessHandle(_T("Bubbles.exe"));
}
如此,确实能得到我要的程序的Handle,但是无法从此Handle得到其Hwnd或CWnd,因为一个进程,可能有多个窗口,也可能没有窗口,MS在这方面没有提供更多的函数供我们参考。
5.通过枚举窗口,这次终于达到目的,至此,花费一整天加一晚上的时间:
//if (!::EnumWindows((WNDENUMPROC)enumProc, 0))
// AfxMessageBox("Error");
BOOL CALLBACK enumProc(HWND hwnd, LPARAM lParam)
{
if (hwnd == NULL)
return FALSE;
if (::IsWindow(hwnd) )//&& ::IsWindowVisible(hwnd))
{
TCHAR szCap[255] = {0};
::GetWindowText(hwnd, szCap, 255);
if (strlen(szCap) == 0)
return TRUE;
CString str ;
str.Format(_T("%s"),szCap);
if(str.Find(_T("ubbles")) != -1)
ShowWindow(hwnd,SW_SHOWNORMAL);
}
return TRUE;
}
难关攻克,后面将开始我的轻松代码之旅……
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述