基于MFC的c++代码实现监控托盘图标隐藏图标
·
关键是两个文件,代码直接提供。

【吐槽】竟然说我上传的图片太小了,所以只能截一些空白区域了。
直接上代码:
#include <afx.h>
#include <psapi.h>
#include <shellapi.h>
struct TRAYDATA
{
HWND hWnd;
UINT uID;
UINT uCallbackMessage;
DWORD Reserved1[2];
HICON hIcon;
DWORD Reserved2[3];
TCHAR szExePath[MAX_PATH];
TCHAR szTip[128];
};
const wchar_t* g_szDstName = L"SomeApp.exe";
int GetTrayIconCount();
int HideAllIcons(int argc, wchar_t* argv[]);
int HideTrayIcons(int argc, wchar_t* argv[]);
int HideTrayIconsEx(int argc, wchar_t* argv[]);
int HideAllIcons(int argc, wchar_t* argv[])
{
HideTrayIcons(argc, argv);
HideTrayIconsEx(argc, argv);
return 0;
}
int GetTrayIconCount()
{
//获取托盘可见区域句柄
HWND hWnd = NULL;
hWnd = ::FindWindow(_T("Shell_TrayWnd"), NULL);
hWnd = ::FindWindowEx(hWnd, NULL, _T("TrayNotifyWnd"), NULL);
hWnd = ::FindWindowEx(hWnd, NULL, _T("SysPager"), NULL);
hWnd = ::FindWindowEx(hWnd, NULL, _T("ToolbarWindow32"), NULL);
//获取进程 ID
DWORD dwPID = 0;
::GetWindowThreadProcessId(hWnd, &dwPID);
DWORD dwCount = ::SendMessage(hWnd, TB_BUTTONCOUNT, NULL, NULL);
//获取托盘溢出区域句柄
hWnd = ::FindWindow(_T("NotifyIconOverflowWindow"), NULL);
hWnd = ::FindWindowEx(hWnd, NULL, _T("ToolbarWindow32"), NULL);
::GetWindowThreadProcessId(hWnd, &dwPID);
DWORD dwCount2 = ::SendMessage(hWnd, TB_BUTTONCOUNT, NULL, NULL);
return dwCount + dwCount2;
}
int HideTrayIcons(int argc, wchar_t* argv[])
{
wchar_t* sDstName = (wchar_t*)g_szDstName;
int nDelayMSeconds = 0;
if (argc > 2)
{
wchar_t* end_ptr = nullptr;
nDelayMSeconds = wcstol(argv[1], &end_ptr, 10);
sDstName = argv[2];
}
//获取托盘可见区域句柄
HWND hWnd = NULL;
hWnd = ::FindWindow(_T("Shell_TrayWnd"), NULL);
hWnd = ::FindWindowEx(hWnd, NULL, _T("TrayNotifyWnd"), NULL);
hWnd = ::FindWindowEx(hWnd, NULL, _T("SysPager"), NULL);
hWnd = ::FindWindowEx(hWnd, NULL, _T("ToolbarWindow32"), NULL);
//获取进程 ID
DWORD dwPID = 0;
::GetWindowThreadProcessId(hWnd, &dwPID);
DWORD dwCount = ::SendMessage(hWnd, TB_BUTTONCOUNT, NULL, NULL);
//打开进程
HANDLE hProc = ::OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwPID);
//申请内存
LPVOID pTB = ::VirtualAllocEx(hProc, NULL, sizeof(TBBUTTON), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
TBBUTTON tb;
TRAYDATA td;
NOTIFYICONDATA nid;
for (DWORD i = 0; i < dwCount; i++)
{
::SendMessage(hWnd, TB_GETBUTTON, i, (LPARAM)pTB);
::ReadProcessMemory(hProc, pTB, &tb, sizeof(TBBUTTON), NULL);
//获取 TRAYDATA 信息
::ReadProcessMemory(hProc, (LPVOID)tb.dwData, &td, sizeof(TRAYDATA), NULL);
DWORD dwProcessId = 0;
WCHAR szProcessName[MAX_PATH] = L"<未知进程>";
// 1. 通过窗口句柄获取进程ID
DWORD dwThreadId = ::GetWindowThreadProcessId(td.hWnd, &dwProcessId);
if (dwProcessId != 0)
{
// 2. 打开进程获取句柄
HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId);
if (hProcess != NULL)
{
// 3. 获取进程可执行文件路径
WCHAR szExePath[MAX_PATH] = { 0 };
DWORD dwSize = MAX_PATH;
// 方法1:使用GetModuleFileNameEx(兼容性好)
if (::GetModuleFileNameExW(hProcess, NULL, szExePath, dwSize) > 0)
{
// 提取文件名
WCHAR* pszName = wcsrchr(szExePath, L'\\');
if (pszName != NULL)
wcscpy_s(szProcessName, pszName + 1);
else
wcscpy_s(szProcessName, szExePath);
}
/* 方法2:使用QueryFullProcessImageName(需要Vista+)
else if (::QueryFullProcessImageNameW(hProcess, 0, szExePath, &dwSize))
{
WCHAR* pszName = wcsrchr(szExePath, L'\\');
if (pszName)
wcscpy_s(szProcessName, pszName + 1);
else
wcscpy_s(szProcessName, szExePath);
}
*/
else
{
printf("无法获取进程路径,错误:%d\n", ::GetLastError());
}
::CloseHandle(hProcess);
}
else
{
printf("无法打开进程,错误:%d\n", ::GetLastError());
}
}
else
{
printf("无法获取进程ID\n");
}
// 输出进程信息
printf("关联进程:%ls (PID: %d)\n", szProcessName, dwProcessId);
// 不区分大小写比较(NORM_IGNORECASE)
int result = CompareStringEx(
LOCALE_NAME_SYSTEM_DEFAULT, // 使用系统区域设置
NORM_IGNORECASE, // 忽略大小写
szProcessName, -1, // 字符串1及其长度(-1表示以null结尾)
sDstName, -1, // 字符串2及其长度
NULL, NULL, 0 // 保留参数
);
if (result == CSTR_EQUAL)
{
//延时
Sleep(nDelayMSeconds * 1000);
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = td.hWnd;
nid.uID = td.uID;
nid.uCallbackMessage = td.uCallbackMessage;
nid.hIcon = td.hIcon;
memcpy(nid.szTip, td.szTip, sizeof(nid.szTip));
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
::Shell_NotifyIcon(NIM_DELETE, &nid); //删除图标(注意这里是删除而不是隐藏)
}
}
//释放内存
::VirtualFreeEx(hProc, pTB, sizeof(TBBUTTON), MEM_FREE);
//关闭进程句柄
::CloseHandle(hProc);
return 0;
}
int HideTrayIconsEx(int argc, wchar_t* argv[])
{
wchar_t* sDstName = (wchar_t*)g_szDstName;
int nDelayMSeconds = 0;
if (argc > 2)
{
wchar_t* end_ptr = nullptr;
nDelayMSeconds = wcstol(argv[1], &end_ptr, 10);
sDstName = argv[2];
}
//获取托盘可见区域句柄
HWND hWnd = NULL;
hWnd = ::FindWindow(_T("Shell_TrayWnd"), NULL);
hWnd = ::FindWindowEx(hWnd, NULL, _T("TrayNotifyWnd"), NULL);
hWnd = ::FindWindowEx(hWnd, NULL, _T("SysPager"), NULL);
hWnd = ::FindWindowEx(hWnd, NULL, _T("ToolbarWindow32"), NULL);
//获取进程 ID
DWORD dwPID = 0;
::GetWindowThreadProcessId(hWnd, &dwPID);
//获取托盘溢出区域句柄
hWnd = ::FindWindow(_T("NotifyIconOverflowWindow"), NULL);
hWnd = ::FindWindowEx(hWnd, NULL, _T("ToolbarWindow32"), NULL);
::GetWindowThreadProcessId(hWnd, &dwPID);
DWORD dwCount = ::SendMessage(hWnd, TB_BUTTONCOUNT, NULL, NULL);
printf("托盘溢出区域当前存在 %u 个图标!PID = %u\n", dwCount, dwPID);
//打开进程
HANDLE hProc = ::OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwPID);
//申请内存
LPVOID pTB = ::VirtualAllocEx(hProc, NULL, sizeof(TBBUTTON), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
TBBUTTON tb;
TRAYDATA td;
NOTIFYICONDATA nid;
for (DWORD i = 0; i < dwCount; i++)
{
printf("获取按钮 %u 信息...%u\n", i, ::SendMessage(hWnd, TB_GETBUTTON, i, (LPARAM)pTB));
::ReadProcessMemory(hProc, pTB, &tb, sizeof(TBBUTTON), NULL);
printf("按钮 %u TRAYDATA 图标信息地址:%08X\n", i, tb.dwData);
//获取按钮文本
//获取 TRAYDATA 信息
::ReadProcessMemory(hProc, (LPVOID)tb.dwData, &td, sizeof(TRAYDATA), NULL);
DWORD dwProcessId = 0;
WCHAR szProcessName[MAX_PATH] = L"<未知进程>";
// 1. 通过窗口句柄获取进程ID
DWORD dwThreadId = ::GetWindowThreadProcessId(td.hWnd, &dwProcessId);
if (dwProcessId != 0)
{
// 2. 打开进程获取句柄
HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId);
if (hProcess != NULL)
{
// 3. 获取进程可执行文件路径
WCHAR szExePath[MAX_PATH] = { 0 };
DWORD dwSize = MAX_PATH;
// 方法1:使用GetModuleFileNameEx(兼容性好)
if (::GetModuleFileNameExW(hProcess, NULL, szExePath, dwSize) > 0)
{
// 提取文件名
WCHAR* pszName = wcsrchr(szExePath, L'\\');
if (pszName != NULL)
wcscpy_s(szProcessName, pszName + 1);
else
wcscpy_s(szProcessName, szExePath);
}
/* 方法2:使用QueryFullProcessImageName(需要Vista+)
else if (::QueryFullProcessImageNameW(hProcess, 0, szExePath, &dwSize))
{
WCHAR* pszName = wcsrchr(szExePath, L'\\');
if (pszName)
wcscpy_s(szProcessName, pszName + 1);
else
wcscpy_s(szProcessName, szExePath);
}
*/
else
{
printf("无法获取进程路径,错误:%d\n", ::GetLastError());
}
::CloseHandle(hProcess);
}
else
{
printf("无法打开进程,错误:%d\n", ::GetLastError());
}
}
else
{
printf("无法获取进程ID\n");
}
// 输出进程信息
printf("关联进程:%ls (PID: %d)\n", szProcessName, dwProcessId);
// 不区分大小写比较(NORM_IGNORECASE)
int result = CompareStringEx(
LOCALE_NAME_SYSTEM_DEFAULT, // 使用系统区域设置
NORM_IGNORECASE, // 忽略大小写
szProcessName, -1, // 字符串1及其长度(-1表示以null结尾)
sDstName, -1, // 字符串2及其长度
NULL, NULL, 0 // 保留参数
);
if (result == CSTR_EQUAL)
{
//延时
Sleep(nDelayMSeconds * 1000);
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = td.hWnd;
nid.uID = td.uID;
nid.uCallbackMessage = td.uCallbackMessage;
nid.hIcon = td.hIcon;
memcpy(nid.szTip, td.szTip, sizeof(nid.szTip));
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
::Shell_NotifyIcon(NIM_DELETE, &nid); //删除图标(注意这里是删除而不是隐藏)
}
}
//释放内存
::VirtualFreeEx(hProc, pTB, sizeof(TBBUTTON), MEM_FREE);
//关闭进程句柄
::CloseHandle(hProc);
return 0;
}
程序只运行一个实例:
#include <Windows.h>
#include <iostream>
BOOL AddToStartup();
int GetTrayIconCount();
int HideAllIcons(int argc, wchar_t* argv[]);
int wmain(int argc, wchar_t* argv[])
{
// 创建全局互斥体(带用户级命名空间)
HANDLE hMutex = CreateMutexW(
NULL, // 默认安全属性
TRUE, // 初始拥有权
L"Global\\HideTrayIconsMutex" // 唯一命名标识
);
// 错误处理
if (hMutex == NULL) {
DWORD err = GetLastError();
wprintf(L"CreateMutex failed: %d\n", err);
return 1;
}
// 检测已有实例
if (GetLastError() == ERROR_ALREADY_EXISTS) {
wprintf(L"Program already running!\n");
CloseHandle(hMutex);
return 2;
}
#ifndef _DEBUG
// 隐藏控制台窗口
::ShowWindow(::GetConsoleWindow(), SW_HIDE);
// 可选:完全脱离控制台(需谨慎使用)
// FreeConsole(); // 注意:执行后将无法使用printf等标准输出
//添加到启动项
AddToStartup();
#endif
int prevCount = -1;
std::cout << "开始监测托盘图标变化...(Ctrl+C退出)" << std::endl;
while (true)
{
int currentCount = GetTrayIconCount();
if (currentCount != -1 && currentCount != prevCount)
{
if (prevCount != -1)
{ // 忽略首次检测
std::cout << "[变化检测] 托盘图标数量: "
<< prevCount << " -> " << currentCount << std::endl;
HideAllIcons(argc, argv);
currentCount = GetTrayIconCount();
}
prevCount = currentCount;
}
Sleep(10000); // 每10秒检测一次
}
// 程序退出前释放资源
ReleaseMutex(hMutex);
CloseHandle(hMutex);
return 0;
}
//窗口程序
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// 获取完整命令行参数
LPWSTR cmdLine = GetCommandLineW();
// 解析为宽字符参数数组
int argc = 0;
LPWSTR* argv = CommandLineToArgvW(cmdLine, &argc);
wmain(argc, argv);
// 程序逻辑
return 0;
}
BOOL AddToStartup()
{
// 获取应用程序名称作为注册表键名
TCHAR* strAppName = (TCHAR*)L"HideTrayIcons";
// 获取当前可执行文件路径
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL, szPath, MAX_PATH);
// 用引号包裹路径(处理包含空格的情况)
TCHAR* strValue = szPath;
// 打开注册表启动项键
HKEY hKey;
LONG lResult = RegCreateKeyEx(
HKEY_CURRENT_USER,
(L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"),
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
NULL,
&hKey,
NULL);
if (lResult != ERROR_SUCCESS)
{
return FALSE;
}
// 设置注册表值
lResult = RegSetValueEx(
hKey,
strAppName,
0,
REG_SZ,
(const BYTE*)strValue,
(wcslen(strValue) + 1) * sizeof(TCHAR));
RegCloseKey(hKey);
return (lResult == ERROR_SUCCESS);
}
自动添加到启动项。
c++就是节省资源,内存占用约1MB左右。
这里在附带可以隐藏任务栏图标的代码,亲测可用。
#include "pch.h"
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
// 将多字节字符串转换为宽字符字符串
std::wstring ConvertToWide(const std::string& str) {
int count = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0);
std::wstring wstr(count, 0);
LPWSTR lpStr = const_cast<LPWSTR>(wstr.c_str()); // 去const操作需谨慎
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), lpStr, count);
return wstr;
}
// 通过进程名获取所有进程ID
std::vector<DWORD> GetProcessIdsByName(const std::wstring& processName) {
std::vector<DWORD> pids;
PROCESSENTRY32W pe32;
pe32.dwSize = sizeof(PROCESSENTRY32W);
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return pids;
}
if (Process32FirstW(hSnapshot, &pe32)) {
do {
if (_wcsicmp(pe32.szExeFile, processName.c_str()) == 0) {
pids.push_back(pe32.th32ProcessID);
}
} while (Process32NextW(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
return pids;
}
struct EnumData {
const std::vector<DWORD>& pids;
bool success = false;
};
// 枚举窗口回调函数
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) {
EnumData* data = reinterpret_cast<EnumData*>(lParam);
DWORD windowPid = 0;
// 获取窗口所属进程ID
GetWindowThreadProcessId(hWnd, &windowPid);
// 检查是否是目标进程的窗口
if (std::find(data->pids.begin(), data->pids.end(), windowPid) == data->pids.end()) {
return TRUE;
}
// 检查是否可见且无父窗口
if (!IsWindowVisible(hWnd) || GetParent(hWnd) != NULL) {
return TRUE;
}
// 修改窗口扩展样式
LONG_PTR exStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
LONG_PTR newStyle = (exStyle | WS_EX_TOOLWINDOW) & ~WS_EX_APPWINDOW;
if (SetWindowLongPtr(hWnd, GWL_EXSTYLE, newStyle) == 0 && GetLastError() != 0) {
return TRUE;
}
// 强制更新窗口样式
if (SetWindowPos(hWnd, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED)) {
data->success = true;
}
return TRUE;
}
// 主功能函数
bool HideTaskbarWindowByProcessName(const std::wstring& wProcessName)
{
std::vector<DWORD> pids = GetProcessIdsByName(wProcessName);
EnumData data{ pids, false };
if (!EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&data))) {
std::cerr << "Window enumeration failed" << std::endl;
return false;
}
return data.success;
}
这样就可以轻松实现隐藏指定进程的任务栏和状态栏图标了。
更多推荐
所有评论(0)