辅助摸鱼的一个小玩具——窗口透明器
窗口透明器
退役 Oier,并非专业干工程的,做得不够优秀的地方请多指教。
由 C++ 编写,用于 Windows。随便写的小玩具,也懒得大费周章上传 Github 什么的了。公布源代码,需要在自己电脑上编译一下。编译须知见后文,请务必阅读!使用说明程序里有。
发现 BUG 可找作者私信反馈,最多大概两天会看到。
:::info[源代码]
// 禁用Windows宏冲突
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#endif
#include<bits/stdc++.h>
#include<windows.h>
// 防止Windows宏污染
#undef DELETE
#undef ERROR
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)
using namespace std;
// 按键事件类型
// 按键事件类型
enum class KeyEventType {
KEY_DOWN,
KEY_UP,
KEY_DOUBLE_CLICK
};
// 按键信息结构
struct KeyInfo {
int vkCode; // 虚拟键码
string keyName; // 按键名称
bool ctrlPressed; // Ctrl是否按下
bool altPressed; // Alt是否按下
bool shiftPressed; // Shift是否按下
bool winPressed; // Win键是否按下
KeyEventType eventType; // 事件类型
DWORD timestamp; // 时间戳
bool isHandled; // 是否已处理(用于阻止系统行为)
};
// 按键组合标识
struct KeyCombo {
int mainKey; // 主键
bool ctrl; // Ctrl是否按下
bool alt; // Alt是否按下
bool shift; // Shift是否按下
bool win; // Win键是否按下
// 用于map的比较
bool operator<(const KeyCombo& other) const {
if (mainKey != other.mainKey) return mainKey < other.mainKey;
if (ctrl != other.ctrl) return ctrl < other.ctrl;
if (alt != other.alt) return alt < other.alt;
if (shift != other.shift) return shift < other.shift;
return win < other.win;
}
bool operator==(const KeyCombo& other) const {
return mainKey == other.mainKey &&
ctrl == other.ctrl &&
alt == other.alt &&
shift == other.shift &&
win == other.win;
}
// 正确的组合键匹配逻辑
bool matches(int vk, bool c, bool a, bool s, bool w) const {
if (mainKey != vk) return false;
// 检查所有修饰键是否匹配
// 如果组合键要求Ctrl,则必须按下Ctrl
if (ctrl && !c) return false;
if (alt && !a) return false;
if (shift && !s) return false;
if (win && !w) return false;
// 如果组合键不要求某个修饰键,那么这个修饰键必须没有按下
if (!ctrl && c) return false;
if (!alt && a) return false;
if (!shift && s) return false;
if (!win && w) return false;
return true;
}
// 获取组合键的描述字符串
string toString() const {
string result;
if (ctrl) result += "Ctrl+";
if (alt) result += "Alt+";
if (shift) result += "Shift+";
if (win) result += "Win+";
// 获取按键名称
if (mainKey >= 'A' && mainKey <= 'Z') {
result += char(mainKey);
} else if (mainKey >= '0' && mainKey <= '9') {
result += char(mainKey);
} else {
// 特殊按键
char keyName[256] = {0};
UINT scanCode = MapVirtualKey(mainKey, MAPVK_VK_TO_VSC);
LONG lParam = (scanCode << 16);
if (GetKeyNameText(lParam, keyName, sizeof(keyName))) {
result += keyName;
} else {
char buffer[32];
snprintf(buffer, sizeof(buffer), "0x%02X", mainKey);
result += buffer;
}
}
return result;
}
};
// 为KeyCombo提供hash函数,用于unordered_map
struct KeyComboHash {
size_t operator()(const KeyCombo& k) const {
return ((hash<int>()(k.mainKey) << 4) ^ (hash<bool>()(k.ctrl) << 3) ^ (hash<bool>()(k.alt) << 2) ^
(hash<bool>()(k.shift) << 1) ^ hash<bool>()(k.win));
}
};
// 回调函数类型
using KeyCallback = function<void(const KeyInfo&)>;
// 回调函数和阻止标志
struct CallbackInfo {
KeyCallback callback;
bool blockSystemBehavior; // 是否阻止系统默认行为
};
class GlobalKeyListener {
private:
// 静态实例指针,用于钩子回调访问
static GlobalKeyListener* s_instance;
// 用于32位/64位兼容的线程ID存储
#if defined(_WIN64) || defined(__x86_64__) || defined(__amd64__)
DWORD_PTR m_threadId;
#else
DWORD m_threadId;
#endif
HHOOK m_keyboardHook;
atomic<bool> m_isRunning;
thread m_messageThread;
// 按键状态 - 使用GetAsyncKeyState获取实时状态,不在这里存储
// 只在钩子回调中使用,确保状态准确
// 回调函数映射
unordered_map<KeyCombo, CallbackInfo, KeyComboHash> m_keyCallbacks;
// 双击检测相关
struct KeyPressRecord {
DWORD lastPressTime;
int pressCount;
};
unordered_map<int, KeyPressRecord> m_keyPressHistory;
unordered_map<int, CallbackInfo> m_doubleClickCallbacks;
unordered_map<int, int> m_doubleClickIntervals;
// 需要阻止的组合键(用于快速检查)
unordered_set<KeyCombo, KeyComboHash> m_blockedCombos;
// 保护共享资源的互斥锁 - 使用mutable使其在const函数中可修改
mutable recursive_mutex m_mutex;
// 私有构造函数,使用单例模式
GlobalKeyListener() : m_keyboardHook(nullptr), m_isRunning(false) {
s_instance = this;
}
// 键盘钩子处理函数(必须是静态的)
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0 && s_instance) {
KBDLLHOOKSTRUCT* kbStruct = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
int vkCode = kbStruct->vkCode;
// 获取实时按键状态
bool ctrlPressed = KEY_DOWN(VK_CONTROL);
bool altPressed = KEY_DOWN(VK_MENU);
bool shiftPressed = KEY_DOWN(VK_SHIFT);
bool winPressed = KEY_DOWN(VK_LWIN) || KEY_DOWN(VK_RWIN);
// 检查是否需要阻止此按键
bool shouldBlock = false;
// 只有按下事件才检查是否需要阻止
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
shouldBlock = s_instance->ShouldBlockKey(vkCode, ctrlPressed, altPressed, shiftPressed, winPressed);
}
// 发送按键事件到消息线程
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
s_instance->PostKeyEvent(vkCode, KeyEventType::KEY_DOWN, ctrlPressed, altPressed, shiftPressed, winPressed, shouldBlock);
} else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) {
s_instance->PostKeyEvent(vkCode, KeyEventType::KEY_UP, ctrlPressed, altPressed, shiftPressed, winPressed, false);
// 双击检测(在按键释放时进行)
if (vkCode != VK_CONTROL && vkCode != VK_MENU &&
vkCode != VK_SHIFT && vkCode != VK_LWIN && vkCode != VK_RWIN) {
s_instance->PostDoubleClickCheck(vkCode, ctrlPressed, altPressed, shiftPressed, winPressed);
}
}
// 如果需要阻止,返回1表示已处理,不让系统继续处理
if (shouldBlock) {
return 1;
}
}
return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
// 检查是否需要阻止此按键
bool ShouldBlockKey(int vkCode, bool ctrlPressed, bool altPressed, bool shiftPressed, bool winPressed) {
lock_guard<recursive_mutex> lock(m_mutex);
// 检查是否有需要阻止的组合键
for (const auto& combo : m_blockedCombos) {
if (combo.matches(vkCode, ctrlPressed, altPressed, shiftPressed, winPressed)) {
return true;
}
}
return false;
}
// 发送按键事件到消息线程
void PostKeyEvent(int vkCode, KeyEventType eventType, bool ctrlPressed, bool altPressed, bool shiftPressed, bool winPressed, bool isHandled) {
if (!m_isRunning || !m_threadId) return;
// 打包数据
LPARAM lParam = MAKELPARAM(
(ctrlPressed ? 1 : 0) |
((altPressed ? 1 : 0) << 1) |
((shiftPressed ? 1 : 0) << 2) |
((winPressed ? 1 : 0) << 3) |
((isHandled ? 1 : 0) << 4),
vkCode
);
WPARAM wParam = static_cast<WPARAM>(eventType);
PostThreadMessage(m_threadId, WM_USER + 1, wParam, lParam);
}
// 发送双击检查到消息线程
void PostDoubleClickCheck(int vkCode, bool ctrlPressed, bool altPressed, bool shiftPressed, bool winPressed) {
if (!m_isRunning || !m_threadId) return;
// 打包修饰键状态
DWORD modifiers = (ctrlPressed ? 1 : 0) |
((altPressed ? 1 : 0) << 1) |
((shiftPressed ? 1 : 0) << 2) |
((winPressed ? 1 : 0) << 3);
LPARAM lParam = MAKELPARAM(modifiers, vkCode);
PostThreadMessage(m_threadId, WM_USER + 2, vkCode, lParam);
}
// 获取按键名称
string GetKeyName(int vkCode) const {
char keyName[256] = {0};
UINT scanCode = MapVirtualKey(vkCode, MAPVK_VK_TO_VSC);
// 处理扩展键
switch (vkCode) {
case VK_INSERT:
case VK_DELETE:
case VK_HOME:
case VK_END:
case VK_PRIOR:
case VK_NEXT:
case VK_LEFT:
case VK_RIGHT:
case VK_UP:
case VK_DOWN:
case VK_NUMLOCK:
case VK_CANCEL:
case VK_SNAPSHOT:
case VK_DIVIDE:
scanCode |= 0x0100; // 扩展键标志
break;
}
LONG lParam = (scanCode << 16);
if (GetKeyNameText(lParam, keyName, sizeof(keyName)) > 0) {
return string(keyName);
}
// 特殊键处理
switch (vkCode) {
case VK_CONTROL:
return "Ctrl";
case VK_LCONTROL:
return "Left Ctrl";
case VK_RCONTROL:
return "Right Ctrl";
case VK_MENU:
return "Alt";
case VK_LMENU:
return "Left Alt";
case VK_RMENU:
return "Right Alt";
case VK_SHIFT:
return "Shift";
case VK_LSHIFT:
return "Left Shift";
case VK_RSHIFT:
return "Right Shift";
case VK_LWIN:
return "Left Win";
case VK_RWIN:
return "Right Win";
case VK_ESCAPE:
return "Esc";
case VK_RETURN:
return "Enter";
case VK_SPACE:
return "Space";
case VK_BACK:
return "Backspace";
case VK_TAB:
return "Tab";
case VK_CAPITAL:
return "Caps Lock";
case VK_NUMLOCK:
return "Num Lock";
case VK_SCROLL:
return "Scroll Lock";
case VK_INSERT:
return "Insert";
case VK_DELETE:
return "Delete";
case VK_HOME:
return "Home";
case VK_END:
return "End";
case VK_PRIOR:
return "Page Up";
case VK_NEXT:
return "Page Down";
case VK_UP:
return "Up Arrow";
case VK_DOWN:
return "Down Arrow";
case VK_LEFT:
return "Left Arrow";
case VK_RIGHT:
return "Right Arrow";
case VK_F1:
return "F1";
case VK_F2:
return "F2";
case VK_F3:
return "F3";
case VK_F4:
return "F4";
case VK_F5:
return "F5";
case VK_F6:
return "F6";
case VK_F7:
return "F7";
case VK_F8:
return "F8";
case VK_F9:
return "F9";
case VK_F10:
return "F10";
case VK_F11:
return "F11";
case VK_F12:
return "F12";
case VK_OEM_PLUS:
return "Plus";
case VK_OEM_MINUS:
return "Minus";
case VK_OEM_COMMA:
return "Comma";
case VK_OEM_PERIOD:
return "Period";
case VK_ADD:
return "NumPad +";
case VK_SUBTRACT:
return "NumPad -";
case VK_MULTIPLY:
return "NumPad *";
case VK_DIVIDE:
return "NumPad /";
case VK_DECIMAL:
return "NumPad .";
case VK_NUMPAD0:
return "NumPad 0";
case VK_NUMPAD1:
return "NumPad 1";
case VK_NUMPAD2:
return "NumPad 2";
case VK_NUMPAD3:
return "NumPad 3";
case VK_NUMPAD4:
return "NumPad 4";
case VK_NUMPAD5:
return "NumPad 5";
case VK_NUMPAD6:
return "NumPad 6";
case VK_NUMPAD7:
return "NumPad 7";
case VK_NUMPAD8:
return "NumPad 8";
case VK_NUMPAD9:
return "NumPad 9";
default: {
// 尝试获取字符表示
BYTE keyboardState[256] = {0};
GetKeyboardState(keyboardState);
WCHAR charBuffer[10] = {0};
int result = ToUnicode(vkCode, scanCode, keyboardState, charBuffer, 10, 0);
if (result > 0) {
// 转换为多字节字符串
char mbBuffer[10] = {0};
WideCharToMultiByte(CP_UTF8, 0, charBuffer, -1, mbBuffer, sizeof(mbBuffer), nullptr, nullptr);
return string(mbBuffer);
}
// 返回虚拟键码
char buffer[32];
snprintf(buffer, sizeof(buffer), "Key 0x%02X", vkCode);
return string(buffer);
}
}
}
// 处理按键事件(在消息线程中执行)
void ProcessKeyEvent(WPARAM wParam, LPARAM lParam) {
KeyEventType eventType = static_cast<KeyEventType>(wParam);
int vkCode = HIWORD(lParam);
WORD modifiers = LOWORD(lParam);
bool ctrlPressed = (modifiers & 0x01) != 0;
bool altPressed = (modifiers & 0x02) != 0;
bool shiftPressed = (modifiers & 0x04) != 0;
bool winPressed = (modifiers & 0x08) != 0;
bool isHandled = (modifiers & 0x10) != 0;
KeyInfo keyInfo;
keyInfo.vkCode = vkCode;
keyInfo.keyName = GetKeyName(vkCode);
keyInfo.ctrlPressed = ctrlPressed;
keyInfo.altPressed = altPressed;
keyInfo.shiftPressed = shiftPressed;
keyInfo.winPressed = winPressed;
keyInfo.eventType = eventType;
keyInfo.timestamp = GetTickCount();
keyInfo.isHandled = isHandled;
lock_guard<recursive_mutex> lock(m_mutex);
// 查找匹配的回调函数
vector<const CallbackInfo*> matchedCallbacks;
for (const auto& pair : m_keyCallbacks) {
const KeyCombo& combo = pair.first;
if (combo.matches(vkCode, ctrlPressed, altPressed, shiftPressed, winPressed)) {
matchedCallbacks.push_back(&pair.second);
}
}
// 执行回调
for (const auto& callbackInfo : matchedCallbacks) {
if (callbackInfo->callback) {
try {
callbackInfo->callback(keyInfo);
} catch (...) {
// 防止回调异常导致程序崩溃
}
}
}
}
// 处理双击检测
void ProcessDoubleClick(WPARAM wParam, LPARAM lParam) {
int vkCode = static_cast<int>(wParam);
WORD modifiers = LOWORD(lParam);
bool ctrlPressed = (modifiers & 0x01) != 0;
bool altPressed = (modifiers & 0x02) != 0;
bool shiftPressed = (modifiers & 0x04) != 0;
bool winPressed = (modifiers & 0x08) != 0;
DWORD currentTime = GetTickCount();
lock_guard<recursive_mutex> lock(m_mutex);
auto it = m_keyPressHistory.find(vkCode);
if (it != m_keyPressHistory.end()) {
DWORD lastTime = it->second.lastPressTime;
int pressCount = it->second.pressCount;
// 检查是否在双击时间间隔内
auto intervalIt = m_doubleClickIntervals.find(vkCode);
int maxInterval = (intervalIt != m_doubleClickIntervals.end()) ? intervalIt->second : 500; // 默认500ms
if ((currentTime - lastTime) <= (DWORD)maxInterval) {
pressCount++;
if (pressCount >= 2) {
// 触发双击事件
KeyInfo keyInfo;
keyInfo.vkCode = vkCode;
keyInfo.keyName = GetKeyName(vkCode);
keyInfo.ctrlPressed = ctrlPressed;
keyInfo.altPressed = altPressed;
keyInfo.shiftPressed = shiftPressed;
keyInfo.winPressed = winPressed;
keyInfo.eventType = KeyEventType::KEY_DOUBLE_CLICK;
keyInfo.timestamp = currentTime;
keyInfo.isHandled = false;
// 执行双击回调
auto callbackIt = m_doubleClickCallbacks.find(vkCode);
if (callbackIt != m_doubleClickCallbacks.end() && callbackIt->second.callback) {
try {
callbackIt->second.callback(keyInfo);
} catch (...) {
// 防止回调异常
}
}
// 重置计数
pressCount = 0;
}
it->second.lastPressTime = currentTime;
it->second.pressCount = pressCount;
} else {
// 超时,重新计数
it->second.lastPressTime = currentTime;
it->second.pressCount = 1;
}
} else {
// 第一次按下
m_keyPressHistory[vkCode] = {currentTime, 1};
}
}
// 消息循环线程函数
void MessageLoop() {
// 存储线程ID用于跨平台兼容
#if defined(_WIN64) || defined(__x86_64__) || defined(__amd64__)
m_threadId = (DWORD_PTR)GetCurrentThreadId();
#else
m_threadId = GetCurrentThreadId();
#endif
// 安装全局键盘钩子
m_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(nullptr), 0);
if (!m_keyboardHook) {
DWORD error = GetLastError();
system("cls");
cerr << "Failed to set keyboard hook. Error: " << error << endl;
// 尝试以管理员权限运行提示
if (error == ERROR_ACCESS_DENIED) {
cerr << "Try running the program as administrator." << endl;
}
return;
}
system("cls");
cout << "Global key listener started successfully." << endl;
// 消息循环
MSG msg;
while (m_isRunning && GetMessage(&msg, nullptr, 0, 0)) {
// 处理自定义消息
if (msg.message == WM_USER + 1) {
ProcessKeyEvent(msg.wParam, msg.lParam);
} else if (msg.message == WM_USER + 2) {
ProcessDoubleClick(msg.wParam, msg.lParam);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// 卸载钩子
if (m_keyboardHook) {
UnhookWindowsHookEx(m_keyboardHook);
m_keyboardHook = nullptr;
}
system("cls");
cout << "Global key listener stopped." << endl;
}
public:
// 删除复制构造函数和赋值运算符
GlobalKeyListener(const GlobalKeyListener&) = delete;
GlobalKeyListener& operator=(const GlobalKeyListener&) = delete;
// 获取单例实例
static GlobalKeyListener& GetInstance() {
static GlobalKeyListener instance;
return instance;
}
// 启动监听
bool Start() {
if (m_isRunning) return true;
m_isRunning = true;
// 清空历史状态
{
lock_guard<recursive_mutex> lock(m_mutex);
m_keyPressHistory.clear();
}
// 创建消息循环线程
try {
m_messageThread = thread(&GlobalKeyListener::MessageLoop, this);
// 等待钩子设置完成
this_thread::sleep_for(chrono::milliseconds(100));
return m_keyboardHook != nullptr;
} catch (const exception& e) {
system("cls");
cerr << "Failed to start listener: " << e.what() << endl;
m_isRunning = false;
return false;
}
}
// 停止监听
void Stop() {
if (!m_isRunning) return;
m_isRunning = false;
// 发送退出消息
if (m_threadId) {
PostThreadMessage(m_threadId, WM_QUIT, 0, 0);
}
// 等待线程结束
if (m_messageThread.joinable()) {
try {
m_messageThread.join();
} catch (...) {
// 忽略异常
}
}
lock_guard<recursive_mutex> lock(m_mutex);
m_keyCallbacks.clear();
m_doubleClickCallbacks.clear();
m_doubleClickIntervals.clear();
m_keyPressHistory.clear();
m_blockedCombos.clear();
}
// 注册按键监听(增强版,支持阻止系统行为)
void RegisterKey(int vkCode, KeyCallback callback,
bool checkCtrl = false, bool checkAlt = false,
bool checkShift = false, bool checkWin = false,
bool blockSystemBehavior = false) {
KeyCombo combo{vkCode, checkCtrl, checkAlt, checkShift, checkWin};
CallbackInfo info{callback, blockSystemBehavior};
lock_guard<recursive_mutex> lock(m_mutex);
m_keyCallbacks[combo] = info;
// 如果需要阻止系统行为,添加到阻止列表
if (blockSystemBehavior) {
m_blockedCombos.insert(combo);
}
}
// 注册组合键监听(增强版)
void RegisterComboKey(int mainKey, bool ctrl, bool alt, bool shift, bool win,
KeyCallback callback, bool blockSystemBehavior = false) {
RegisterKey(mainKey, callback, ctrl, alt, shift, win, blockSystemBehavior);
}
// 注册双击监听
void RegisterDoubleClick(int vkCode, int maxIntervalMs = 500,
KeyCallback callback = nullptr, bool blockSystemBehavior = false) {
lock_guard<recursive_mutex> lock(m_mutex);
m_doubleClickIntervals[vkCode] = maxIntervalMs;
if (callback) {
m_doubleClickCallbacks[vkCode] = {callback, blockSystemBehavior};
}
}
// 直接阻止组合键(无需回调)
void BlockCombo(int mainKey, bool ctrl = false, bool alt = false,
bool shift = false, bool win = false) {
KeyCombo combo{mainKey, ctrl, alt, shift, win};
lock_guard<recursive_mutex> lock(m_mutex);
m_blockedCombos.insert(combo);
}
// 解除阻止组合键
void UnblockCombo(int mainKey, bool ctrl = false, bool alt = false,
bool shift = false, bool win = false) {
KeyCombo combo{mainKey, ctrl, alt, shift, win};
lock_guard<recursive_mutex> lock(m_mutex);
m_blockedCombos.erase(combo);
}
// 移除按键监听
void UnregisterKey(int vkCode, bool ctrl = false,
bool alt = false, bool shift = false, bool win = false) {
KeyCombo combo{vkCode, ctrl, alt, shift, win};
lock_guard<recursive_mutex> lock(m_mutex);
m_keyCallbacks.erase(combo);
// 同时从阻止列表中移除
m_blockedCombos.erase(combo);
}
// 移除所有监听
void UnregisterAll() {
lock_guard<recursive_mutex> lock(m_mutex);
m_keyCallbacks.clear();
m_doubleClickCallbacks.clear();
m_blockedCombos.clear();
}
// 设置双击检测间隔
void SetDoubleClickInterval(int intervalMs) {
lock_guard<recursive_mutex> lock(m_mutex);
for (auto& pair : m_doubleClickIntervals) {
pair.second = intervalMs;
}
}
// 检查是否正在运行
bool IsRunning() const {
return m_isRunning;
}
// 获取当前修饰键状态(实时)
void GetModifierState(bool& ctrl, bool& alt, bool& shift, bool& win) const {
ctrl = KEY_DOWN(VK_CONTROL);
alt = KEY_DOWN(VK_MENU);
shift = KEY_DOWN(VK_SHIFT);
win = KEY_DOWN(VK_LWIN) || KEY_DOWN(VK_RWIN);
}
// 获取已注册的快捷键列表
vector<string> GetRegisteredHotkeys() const {
vector<string> hotkeys;
lock_guard<recursive_mutex> lock(m_mutex);
for (const auto& pair : m_keyCallbacks) {
hotkeys.push_back(pair.first.toString());
}
return hotkeys;
}
// 获取已阻止的快捷键列表
vector<string> GetBlockedHotkeys() const {
vector<string> hotkeys;
lock_guard<recursive_mutex> lock(m_mutex);
for (const auto& combo : m_blockedCombos) {
hotkeys.push_back(combo.toString());
}
return hotkeys;
}
// 析构函数
~GlobalKeyListener() {
Stop();
s_instance = nullptr;
}
};
// 初始化静态成员
GlobalKeyListener* GlobalKeyListener::s_instance = nullptr;
//本程序的窗口句柄
HWND myWindow;
bool isMyWinVisible = true;
//颜色常量
const int COLOR_BLACK = 0;
const int COLOR_BLUE = 1;
const int COLOR_GREEN = 2;
const int COLOR_LIGHTGREEN = 3;
const int COLOR_RED = 4;
const int COLOR_PURPLE = 5;
const int COLOR_YELLOW = 6;
const int COLOR_WHITE = 7;
const int COLOR_GREY = 8;
const int COLOR_LIGHTBLUE = 9;
const int COLOR_LIGHTGREEN2 = 10;
const int COLOR_LIGHTGREEN3 = 11;
const int COLOR_LIGHTRED = 12;
const int COLOR_LIGHTPURPLE = 13;
const int COLOR_LIGHTYELLOW = 14;
const int COLOR_LIGHTWHITE = 15;
// 宽字符输出函数封装
void WriteConStr(const wstring& str) {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD charsWritten;
WriteConsoleW(hConsole, str.c_str(), (DWORD)str.length(), &charsWritten, NULL);
}
void WriteConStr(const wchar_t* str) {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD charsWritten;
WriteConsoleW(hConsole, str, (DWORD)wcslen(str), &charsWritten, NULL);
}
//更改颜色 文字+背景
void SetColorAndBackground(int ForgC, int BackC) {
WORD wColor = ((BackC & 0x0F) << 4) + (ForgC & 0x0F);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), wColor);
return;
}
// 设置光标位置
void SetCursorPosition(int x, int y) {
COORD coord;
coord.X = (short)x;
coord.Y = (short)y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
return;
}
// 获取光标位置
void GetCursorPosition(POINT &pt) {
GetCursorPos(&pt);
ScreenToClient(myWindow, &pt);
pt.y = pt.y / 16;
pt.x = pt.x / 8;
return;
}
//设置光标可见性
void SetCursorVisibility(int x) {
CONSOLE_CURSOR_INFO cursor_info = {1, x}; // 第二个值为 0 表示隐藏光标
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
//按钮类(使用宽字符)
class Button {
public:
int x, y;
wstring name; // 改为宽字符串
int len;
// 构造函数
Button(int _x, int _y, const wstring& _name, int _len, bool isPrint = true): x(_x), y(_y), name(_name), len(_len) {
if (isPrint) {
SetCursorPosition(x, y);
WriteConStr(name);
}
}
// 输出内容
void printText() {
SetCursorPosition(x, y);
WriteConStr(name);
}
// 点击监听
bool isClicked() {
if (!isMyWinVisible || GetForegroundWindow() != myWindow) {
return 0;
}
POINT pt;
GetCursorPosition(pt);
if (KEY_DOWN(MOUSE_MOVED)) {
if (pt.y == y && (pt.x >= x && pt.x <= x + len)) {
return 1;
}
}
return 0;
}
};
// 将宽字符串转换为UTF-8字符串
string WStringToUTF8(const wstring& wstr) {
if (wstr.empty()) return string();
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
string strTo(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
return strTo;
}
// 将系统本地编码(GBK)字符串转换为宽字符串
wstring LocalToWString(const string& local) {
if (local.empty()) return wstring();
int size_needed = MultiByteToWideChar(CP_ACP, 0, local.c_str(), (int)local.size(), NULL, 0);
wstring wstr(size_needed, 0);
MultiByteToWideChar(CP_ACP, 0, local.c_str(), (int)local.size(), &wstr[0], size_needed);
return wstr;
}
// IsWindow 检查窗口句柄是否存在
bool isHandleValid(HWND hwnd) {
return ::IsWindow(hwnd);
}
// 显示或隐藏窗口
bool SetWindowVisibility(HWND hwnd, bool visible) {
if (hwnd == NULL) return false;
if (visible) {
ShowWindow(hwnd, SW_SHOW);
SetForegroundWindow(hwnd);
} else {
ShowWindow(hwnd, SW_HIDE);
}
return true;
}
// 查询窗口透明度
int GetWindowTransparency(HWND hwnd) {
// 获取窗口扩展样式
LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
// 检查窗口是否支持分层(透明)
if (!(exStyle & WS_EX_LAYERED)) {
return 255; // 完全不透明
}
// 获取透明度值
BYTE alpha = 255;
DWORD flags;
COLORREF color;
GetLayeredWindowAttributes(hwnd, &color, &alpha, &flags);
// 如果使用Alpha值,则返回
if (flags & LWA_ALPHA) {
return (int)alpha;
}
return 255; // 没有使用Alpha透明度
}
// 设置窗口透明度 (0-255, 0=完全透明, 255=完全不透明)
bool SetWindowTransparency(HWND hwnd, int alpha) {
if (hwnd == NULL) return false;
// 设置分层窗口属性
LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
SetWindowLongPtr(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED);
// 设置透明度
return SetLayeredWindowAttributes(hwnd, 0, (BYTE)alpha, LWA_ALPHA);
}
// 获取窗口标题(返回宽字符串)
wstring GetWindowTitleW(HWND hwnd) {
wchar_t windowTitle[1024];
GetWindowTextW(hwnd, windowTitle, 1024);
wstring res = windowTitle;
if ((int)res.size() > 33) {
res = res.substr(0, 33);
res += L"...";
}
return res;
}
// 获取窗口标题(兼容旧代码,返回UTF-8字符串)
string GetWindowTitle(HWND hwnd) {
wstring wtitle = GetWindowTitleW(hwnd);
return WStringToUTF8(wtitle);
}
//查询指定窗口是否隐藏
bool isWindowHidden(HWND hwnd) {
if (hwnd == NULL) {
return 0;
}
LONG style = GetWindowLong(hwnd, GWL_STYLE);
return (style & WS_VISIBLE) == 0;
}
//查询指定窗口是否置顶
bool IsWindowTopmost(HWND hwnd) {
LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
return (exStyle & WS_EX_TOPMOST) != 0;
}
//设置指定窗口是否置顶
void SetWindowTopmost(HWND hwnd, bool topmost) {
HWND insertAfter = topmost ? HWND_TOPMOST : HWND_NOTOPMOST;
SetWindowPos(hwnd, insertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
int deltaAlpha = 20;
vector<HWND> hiddenWindow;
bool isListen = 0;
// 给按键监听器进行实例化
GlobalKeyListener& listener = GlobalKeyListener::GetInstance();
void addListener() {
// 绑定按键监听
// Ctrl+Alt+Space 藏自己
listener.RegisterComboKey(VK_SPACE, 1, 1, 0, 0, [](const KeyInfo & info) {
if (info.eventType == KeyEventType::KEY_UP) {
if (SetWindowVisibility(myWindow, !isMyWinVisible)) {
isMyWinVisible = !isMyWinVisible;
}
}
}, 1);
// Alt+VK_RIGHT 不透明
listener.RegisterComboKey(VK_RIGHT, 0, 1, 0, 0, [](const KeyInfo & info) {
if (info.eventType == KeyEventType::KEY_UP) {
HWND nowH = GetForegroundWindow();
int nowA = GetWindowTransparency(nowH);
if (nowA > 255 - deltaAlpha) {
SetWindowTransparency(nowH, 255);
} else {
SetWindowTransparency(nowH, nowA + deltaAlpha);
}
}
}, 1);
// Alt+VK_LEFT 透明
listener.RegisterComboKey(VK_LEFT, 0, 1, 0, 0, [](const KeyInfo & info) {
if (info.eventType == KeyEventType::KEY_UP) {
HWND nowH = GetForegroundWindow();
int nowA = GetWindowTransparency(nowH);
if (nowA < 2 * deltaAlpha) {
SetWindowTransparency(nowH, deltaAlpha);
} else {
SetWindowTransparency(nowH, nowA - deltaAlpha);
}
}
}, 1);
// Ctrl+Space 隐藏
listener.RegisterComboKey(VK_SPACE, 1, 0, 0, 0, [](const KeyInfo & info) {
if (info.eventType == KeyEventType::KEY_UP) {
HWND nowH = GetForegroundWindow();
if (!isWindowHidden(nowH) && SetWindowVisibility(nowH, 0)) {
if (nowH == myWindow) {
isMyWinVisible = false;
} else {
hiddenWindow.push_back(nowH);
}
}
}
}, 1);
// Alt+VK_UP 置顶
listener.RegisterComboKey(VK_UP, 0, 1, 0, 0, [](const KeyInfo & info) {
if (info.eventType == KeyEventType::KEY_UP) {
HWND nowH = GetForegroundWindow();
SetWindowTopmost(nowH, 1);
}
}, 1);
// Alt+VK_DOWN 置顶
listener.RegisterComboKey(VK_DOWN, 0, 1, 0, 0, [](const KeyInfo & info) {
if (info.eventType == KeyEventType::KEY_UP) {
HWND nowH = GetForegroundWindow();
SetWindowTopmost(nowH, 0);
}
}, 1);
}
int main() {
// 设置控制台输出编码为UTF-8(尽管我们使用WriteConsoleW,但系统命令可能需要)
// system("chcp 65001 > nul");
// 设置控制台标题为宽字符
SetConsoleTitleW(L"窗口透明器");
// 设置长宽 70 列,18 行,最好别改
system("mode con cols=70 lines=18");
system("cls");
// 尝试获取本程序的窗口句柄
try {
myWindow = GetConsoleWindow();
if (myWindow == NULL) {
// 无法获取,抛异常
throw runtime_error("无法获取窗口句柄");
}
} catch (const runtime_error& e) {
// 报错,退出程序
SetCursorPosition(0, 0);
SetColorAndBackground(COLOR_LIGHTWHITE, COLOR_RED);
WriteConStr(L"ERROR:");
SetColorAndBackground(COLOR_WHITE, COLOR_BLACK);
// 转换错误信息为宽字符
wstring errorMsg = LocalToWString(e.what());
WriteConStr(errorMsg + L"\n");
system("pause");
exit(0);
} catch (...) {
system("cls");
SetCursorPosition(0, 0);
SetColorAndBackground(COLOR_LIGHTWHITE, COLOR_RED);
WriteConStr(L"UNKNOWN ERROR");
system("pause");
exit(0);
}
hiddenWindow.clear();
// 隐藏光标
SetCursorVisibility(0);
system("cls");
// IDE 内编译警告:
SetCursorPosition(0, 0);
SetColorAndBackground(COLOR_LIGHTRED, COLOR_BLACK);
WriteConStr(L"编译完成后,请勿通过 IDE 直接运行!\n\n");
WriteConStr(L"请通过文件夹内双击程序运行!\n\n");
WriteConStr(L"如果对任务管理器等窗口不生效,请尝试用管理员身份运行!\n\n");
SetColorAndBackground(COLOR_LIGHTYELLOW, COLOR_BLACK);
WriteConStr(L"建议不要开启快速编辑模式!\n");
WriteConStr(L"否则像下面的按钮可能会无法点击!\n");
Button knowWarning(0, 9, L">我明白了", 9);
while (1) {
if (knowWarning.isClicked()) {
while (knowWarning.isClicked()) {
// TODO
}
break;
}
Sleep(5);
}
// 主页面
system("cls");
// 启动监听
addListener();
try {
// 失败:
if (!listener.Start()) {
throw runtime_error("无法监听按键");
} else {
isListen = 1;
}
} catch (const runtime_error& e) {
SetColorAndBackground(COLOR_LIGHTWHITE, COLOR_RED);
WriteConStr(L"\nERROR:");
SetColorAndBackground(COLOR_WHITE, COLOR_BLACK);
wstring errorMsg = LocalToWString(e.what());
WriteConStr(errorMsg + L"\n");
system("pause");
exit(0);
} catch (...) {
WriteConStr(L"\n");
SetColorAndBackground(COLOR_LIGHTWHITE, COLOR_RED);
WriteConStr(L"UNKNOWN ERROR\n");
system("pause");
exit(0);
}
system("cls");
SetColorAndBackground(COLOR_LIGHTPURPLE, COLOR_BLACK);
WriteConStr(L"窗口透明 v1.2.0\n");
SetColorAndBackground(COLOR_LIGHTGREEN, COLOR_BLACK);
Button help(0, 3, L">程序使用指南", 13);
SetColorAndBackground(COLOR_LIGHTGREEN2, COLOR_BLACK);
Button showList(0, 5, L">隐藏窗口列表", 13);
SetColorAndBackground(COLOR_LIGHTGREEN3, COLOR_BLACK);
Button getRest(0, 7, L">暂停软件功能", 13);
SetColorAndBackground(COLOR_WHITE, COLOR_BLACK);
Button leave(0, 9, L">退出程序并显示所有隐藏的窗口", 29);
Button goHome(0, 17, L">返回", 5, 0);
while (1) {
if (help.isClicked()) {
system("cls");
SetCursorPosition(0, 0);
SetColorAndBackground(COLOR_PURPLE, COLOR_BLACK);
WriteConStr(L"欢迎使用本程序!\n\n");
SetColorAndBackground(COLOR_LIGHTBLUE, COLOR_BLACK);
WriteConStr(L"Alt+方向键右 可以让最前方的窗口更不透明!\n");
WriteConStr(L"Alt+方向键左 可以让最前方的窗口更透明!\n\n");
SetColorAndBackground(COLOR_LIGHTGREEN2, COLOR_BLACK);
WriteConStr(L"Ctrl+Alt+空格 可以隐藏或显示本窗口!\n\n");
SetColorAndBackground(COLOR_LIGHTRED, COLOR_BLACK);
WriteConStr(L"Ctrl+空格 可以隐藏任意窗口!\n");
WriteConStr(L"隐藏的窗口可以在主页的\"隐藏窗口列表\"中恢复!\n\n");
SetColorAndBackground(COLOR_LIGHTWHITE, COLOR_BLACK);
WriteConStr(L"Alt+方向键上 可以置顶任意窗口!\n");
WriteConStr(L"Alt+方向键下 可以取消置顶任意窗口!\n\n");
SetColorAndBackground(COLOR_LIGHTPURPLE, COLOR_BLACK);
WriteConStr(L"点击主页\"暂停/恢复软件功能\"可以开启或暂停功能!\n\n");
SetColorAndBackground(COLOR_WHITE, COLOR_GREEN);
WriteConStr(L"适当的摸鱼是为了提高效率 ^_^\n");
SetColorAndBackground(COLOR_WHITE, COLOR_BLACK);
goHome.printText();
while (1) {
if (goHome.isClicked()) {
goto HomeText;
}
Sleep(2);
}
} else if (showList.isClicked()) {
while (showList.isClicked()) {
//TODO
}
int Start = 0;
vector<Button> Items;
Button Front(0, 16, L">上一页", 7, 0);
Button Next(60, 16, L">下一页", 7, 0);
Button Renew(62, 17, L">刷新", 5, 0);
while (1) {
hiddenWindow.erase(remove_if(hiddenWindow.begin(), hiddenWindow.end(), [](const HWND & nowH) {
return !isHandleValid(nowH) || !isWindowHidden(nowH);
}), hiddenWindow.end());
if (!hiddenWindow.size()) {
Start = 0;
} else {
while (Start >= (int)hiddenWindow.size()) {
Start -= 15;
}
}
system("cls");
SetCursorPosition(0, 0);
SetColorAndBackground(COLOR_YELLOW, COLOR_BLACK);
WriteConStr(L"隐藏的窗口如下,点击显示:\n");
SetColorAndBackground(COLOR_WHITE, COLOR_BLACK);
Items.clear();
for (int i = 0; i < min(15, (int)hiddenWindow.size() - Start); i++) {
wstring n = GetWindowTitleW(hiddenWindow.at(Start + i));
if (n == L"") {
n = L"[无名窗口]";
}
// 确保显示宽度不超过70个字符
// 注意:中文和英文字符宽度不同,这里简化为字符数
Button tp(0, i + 1, n, 70, 1);
Items.push_back(tp);
}
SetColorAndBackground(COLOR_LIGHTBLUE, COLOR_BLACK);
if (Start >= 15) {
Front.printText();
}
if (Start + 15 < (int)hiddenWindow.size()) {
Next.printText();
}
SetColorAndBackground(COLOR_WHITE, COLOR_BLACK);
goHome.printText();
Renew.printText();
while (1) {
if (goHome.isClicked()) {
goto HomeText;
} else if (Start >= 15 && Front.isClicked()) {
Start -= 15;
break;
} else if (Start + 15 < (int)hiddenWindow.size() && Next.isClicked()) {
Start += 15;
break;
} else if (Renew.isClicked()) {
goto ListInner;
} else {
for (int i = 0; i < min(15, (int)Items.size()); i++) {
static HWND tp;
if (Items[i].isClicked()) {
tp = hiddenWindow.at(i + Start);
if (isHandleValid(tp)) {
SetWindowVisibility(tp, 1);
}
hiddenWindow.erase(remove(hiddenWindow.begin(), hiddenWindow.end(), tp), hiddenWindow.end());
while (Items[i].isClicked()) {
// TODO
}
goto ListInner;
}
}
}
Sleep(2);
}
ListInner:
;
}
} else if (leave.isClicked()) {
listener.Stop();
hiddenWindow.erase(remove_if(hiddenWindow.begin(), hiddenWindow.end(), [](const HWND & nowH) {
return !isHandleValid(nowH);
}), hiddenWindow.end());
for (auto i : hiddenWindow) {
SetWindowVisibility(i, 1);
Sleep(2);
}
exit(0);
} else if (goHome.isClicked()) {
while (goHome.isClicked()) {
//TODO
}
HomeText:
;
system("cls");
SetColorAndBackground(COLOR_LIGHTPURPLE, COLOR_BLACK);
WriteConStr(L"窗口透明 v1.2.0\n");
SetColorAndBackground(COLOR_LIGHTGREEN, COLOR_BLACK);
help.printText();
SetColorAndBackground(COLOR_LIGHTGREEN2, COLOR_BLACK);
showList.printText();
SetColorAndBackground(COLOR_LIGHTGREEN3, COLOR_BLACK);
getRest.printText();
SetColorAndBackground(COLOR_WHITE, COLOR_BLACK);
leave.printText();
} else if (getRest.isClicked()) {
while (getRest.isClicked()) {
//TODO
}
if (isListen) {
listener.Stop();
getRest.name = L">恢复软件功能";
isListen = 0;
} else {
if (listener.Start()) {
addListener();
getRest.name = L">暂停软件功能";
isListen = 1;
}
}
getRest.printText(); // 更新按钮显示
goto HomeText;
}
Sleep(2);
}
return 0;
}
:::
编译须知
至少需要 C++14。同时,代码大量使用宽字符,确保你的 IDE 保存代码时用的是 UTF-8 格式,编译时也是用的 UTF-8。也就是说,请你在“编译时加入下列选项”中添加:
-std=c++14 -finput-charset=UTF-8
:::error[注意] 有一条与上面很像的编译命令,为:
-fexec-charset=UTF-8
除非你知道在干什么,否则不要添加。 :::
如果希望可执行文件在不同电脑上都可以运行,请在“链接时加入下列选项”中添加:
-static-libgcc
-static-libstdc++
免责 & 后记
如果您选择使用本程序,那么将默认您会承担使用本程序所造成的一切后果。
作者鼓励并倡导将此程序用于正当的学习用途。如果因使用者使用不当,由此造成的一切后果作者概不负责。
作者承诺在发布代码前已经尽可能地进行了检查,但本程序仍然不能排除有存在错误与漏洞的可能。如果您发现了错误与漏洞,请尽快与作者私信反馈。如果本程序的错误与漏洞对您的电脑造成了损害,作者不承担任何责任,但会积极与您配合减小损害。
未经作者亲自许可,不得随意修改本程序的任何部分。如果未经允许随意修改本程序,由此造成的一切后果作者概不负责。
本程序有关按键监听的部分代码由 DeepSeek 编写。
Update
- upd on 2026.1.2 上午:添加暂停监听的功能;修复了“隐藏列表”会显示程序自身的问题;修复了通过本程序隐藏的窗口后来通过其他方式显示,会在列表中残留选项的问题。
- upd on 2026.1.2 下午:修复名称过长的窗口在隐藏列表中的显示问题;修复重复按 Ctrl+Space 会重复向隐藏列表中添加同一窗口的问题。
- upd on 2026.1.2 晚上:添加置顶窗口的功能;添加按钮检测的等待时间,减少 CPU 开销;添加“管理员身份”的启动提示;修复即使程序窗口不在最上层按钮也可被点击的问题;更改了调整透明度的快捷键。
- upd on 2026.1.10 晚上:使用
snprintf代替sprintf_s,增强兼容性;更改首页按钮文案;退出按钮添加使所有隐藏的窗口显形的功能,防止关闭程序后窗口再也无法显形的情况。 - upd on 2026.1.17 下午:全面使用宽字符,尽可能修复了乱码问题;缩减了监听的等待时间,提高灵敏度。
- upd on 2026.1.18 晚上:更改了编译须知。