键盘重映射禁用 CtrlAltDel 键的利弊

2024-02-14 01:36

本文主要是介绍键盘重映射禁用 CtrlAltDel 键的利弊,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

前言

一、Scancode Map 的规范

二、禁用 CtrlAltDel 的方法及其缺陷

三、编程实现和测试

3.1 C++ 实现的简易修改工具

3.2 C# 实现的窗口工具

四、总结


本文属于原创文章,转载请注明出处:

https://blog.csdn.net/qq_59075481/article/details/136104444。

前言

在 Ndr-LRPC Hook 和 WMsg Hook 等方法完善前,网络上公开的禁用 CtrlAltDel 键的方法是使用 "Scancode Map" 键盘扫描码映射表这个方法,本质上是利用微软提供的注册表设置来达到屏蔽的效果。这确实在前一阶段是较好的解决方案,所以在更新完前两种方案后,我不打算对这个方法避而不谈,相反,我觉得该方法可以用于更广泛的方面,甚至许多键盘快捷键修改程序就利用了类似的方法。本文将就具体的实现细节给出通用修改工具以及谈谈这种方法存在优缺点。工具有两个,一个是我重写的 C++ 简化实现,另一个是基于 C# 的开源工具(查阅资料时偶然发现)。

一、Scancode Map 的规范

Scancode Map 注册表项位于注册表如下路径:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout

它是 Keyboard Layout 子键下名为"Scancode Map"的二进制值项,如果没有你可以新建一个。

这个值项可实现对键盘按键的映射。这里映射的意思可理解为“替换”,可将任意一键替换成其它键,或者禁用。

Scancode Map 注册表项具有类似下面的格式:

以 16 进制表示,可分为五个部分,为了方便讲解,相邻字节之间用逗号隔开。

"Scancode Map" = 00,00,00,00,00,00,00,00,02,00,00,00,01,00, 02,00,00,00,00,00

前 8 个字节: 表示版本信息号,一般为 0。
紧接着的 4 个字节: 表示映射键的总数 + 1。按照二进制的读写规则,低位在左,高位在右。02 00 00 00 这个数实际就是:00 00 00 02 ,该用例表示当前修改 1 个键。
紧接着的 2 个字节: 表示替换后按键的“扫描码”。如:ESC 键的扫描码是 01 ,所以就表示 01 00 。再比如左 Ctrl 键扫描码是 1D 00, 而右 Ctrl 键是 1D E0 。
紧接着的 2 个字节:表示原按键的“扫描码”,格式同上。
最后以四个 00 结束,(8个也行)。

键盘的按键扫描码可以参考下面这幅图(已经按照书写格式排列):

按键名称-扫描码对照表

图片来源:https://www.bilibili.com/read/cv8001022/

二、禁用 CtrlAltDel 的方法及其缺陷

根据上面提供的信息,我们了解到需要禁用 Ctrl + Alt + Del 键,只禁用 Del 就可以了,不需要修改左键,原因是 Ctrl / Alt 需要经常用,而 Del 用的频率较少。

经查表,Del 键对应的扫描码是 53 E0,所以按照规则,我们只需要将 53 E0 映射为其他按键或者映射为空即可,比如下图:
 

注册表按键映射表

这张图是注册表中修改过后的结果,圈出来的就是需要关注的的位置, 02 表示总数加一,连同后面 3 个 00,一共四个字节;后面两字节是替换的扫描码,我这里替换成数字 1(4F 00)。原键的扫描码是 53 E0。

这个方法优点就是实现简单。 

当然这个方法缺陷很明显,不可避免的按键要被无差别禁用,Del 键却还有其他功能,所以会影响使用体验。

三、编程实现和测试

那么如何通过代码来实现修改呢?很简单,就是读取注册表和修改注册表,只不过需要按照字节序构建数据。

3.1 C++ 实现的简易修改工具

为了方便制作一个简易的测试工具,我为工具定义了 config.cfg 配置文件的规则,样例如下:

# 此处填写要修改的总数,当然程序写入注册表时,按照规则注册表的记录总数比用户实际修改的个数多加1,这在之前也介绍过。
[ReMapKeyNum]
3
# 修改的键的顺序
[MapKeyRank]
1
# 修改的键的原始扫描码,格式:十六进制,两个字符。
[MapKeyOriCode]
1d00
# 修改的键的新扫描码,格式:十六进制,两个字符。
[MapKeyNewCode]
1de0
[MapKeyRank]
2
[MapKeyOriCode]
3800
[MapKeyNewCode]
38e0
[MapKeyRank]
3
[MapKeyOriCode]
53e0
[MapKeyNewCode]
0000

配置文件放在任意位置均可,目前已经实现功能的参数列表如下:

  • /enumerate :解析注册表中重映射按键的数据,该方法会逆编码原始按键名称(不过目前未实现区分左右按键)
  • /setremap <configFilePath>:指定一个路径作为配置文件路径,配置文件按照规范书写,程序将解析配置文件并用该配置文件覆盖注册表按键映射表
  • /queryActiveKey:动态查询键盘输入的按键的扫描码(不区分左右按键,这点需要改进)
  • /deleteAllMap:删除整个注册表按键映射表,恢复系统默认配置(需要二次确认操作)。注意:该操作无法撤销删除
  • /modifyDelKey:删除注册表按键映射表中指定的条目。该命令首先解析所有条目信息,随后通过输入要删除的 Item 编号即可实现删除
  • /modifyInsertKey:在指定的 Item 编号之后添加新的数据条目。需要手动输入三个参数,第一个为原始按键扫描码,第二个为修改后的按键扫描码,第三个为 Item 编号
  • /modifyAddKey:不需要指定位置编号,在结尾追加新的条目。
  • /statusMap <bEnable>:启用或禁用重映射键注册表项(该操作通过将映射表标记为 DISABLED 来禁用整张表,所以操作可以恢复),status 参数为 0 表示禁用,为 1 表示启用
  • /makebackup:备份注册表当前重映射表数据。该命令拷贝一个按照时间记录的注册表副本。
  • /recoverMap <backupTime>:恢复按照时间标签备份的副本,该操作将覆盖当前的注册表按键映射表。
  • /readbackup:读取所有注册表备份(按照时间标签列表显示)

完整代码如下(注意所有的修改除了备份数据其他操作对于大部分应用需要重启计算机生效):

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <Windows.h>
#include <iomanip>// 定义宏
#define REGISTRY_KEY L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout"
#define REGISTRY_VALUE_NAME L"Scancode Map"
#define CONFIG_FILE_SECTION_REMAP_KEY_NUM "[ReMapKeyNum]"
#define CONFIG_FILE_SECTION_MAP_KEY_RANK "[MapKeyRank]"
#define CONFIG_FILE_SECTION_MAP_KEY_ORI_CODE "[MapKeyOriCode]"
#define CONFIG_FILE_SECTION_MAP_KEY_NEW_CODE "[MapKeyNewCode]"
#define BACKUP_KEY_PREFIX L"Scancode_Map_Back_"
#define DESABLED_KEY_PREFIX L"_DISABLED"// 全局变量用于记录按键状态
int remainingKeys = 0;
int remainingRounds = 0;
int numKeys = 0;
std::vector<std::pair<std::string, DWORD>> keyNameValuePairs;// 解析注册表中的重映射信息
bool ParseRemapInfo(std::vector<std::pair<WORD, WORD>>& remapInfo) {HKEY hKey;if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {DWORD bufferSize = 0;DWORD dataType;if (RegQueryValueEx(hKey, REGISTRY_VALUE_NAME, NULL, &dataType, NULL, &bufferSize) == ERROR_SUCCESS && dataType == REG_BINARY) {std::vector<BYTE> buffer(bufferSize);if (RegQueryValueEx(hKey, REGISTRY_VALUE_NAME, NULL, NULL, buffer.data(), &bufferSize) == ERROR_SUCCESS) {DWORD totalRemappedKeys = *(DWORD*)(buffer.data() + 8) - 1;for (DWORD i = 0; i < totalRemappedKeys; ++i) {WORD remappedScanCode = *(WORD*)(buffer.data() + 12 + i * 4);WORD originalScanCode = *(WORD*)(buffer.data() + 14 + i * 4);remapInfo.emplace_back(originalScanCode, remappedScanCode);}}RegCloseKey(hKey);return true;}else {RegCloseKey(hKey);return false;}}return false;
}// 枚举当前重映射的按键并逆向转换扫描码为键名
void EnumerateRemappedKeys(std::vector<std::pair<WORD, WORD>> remapInfo) {std::cout << "Remapped keys:\n";CHAR remappedKeyName[30] = { 0 }, originalKeyName[30] = { 0 };WORD remappedScanCode = 0, originalScanCode = 0;int newKeyret = 0, oldKeyret = 0;for (size_t i = 0; i < remapInfo.size(); ++i) {newKeyret = 0, oldKeyret = 0;memset(remappedKeyName, 0, sizeof(remappedKeyName));memset(originalKeyName, 0, sizeof(originalKeyName));originalScanCode = remapInfo[i].first;remappedScanCode = remapInfo[i].second;newKeyret = GetKeyNameTextA(remappedScanCode << 16,remappedKeyName, sizeof(remappedKeyName) / sizeof(*remappedKeyName));oldKeyret = GetKeyNameTextA(originalScanCode << 16,originalKeyName, sizeof(originalKeyName) / sizeof(*originalKeyName));// 逆向转换扫描码为键名std::cout << "Item " << i + 1 << ":\n";std::cout << "  Remapped Scan Code: 0x" << std::hex << remappedScanCode << "\n";std::cout << "  Remapped Key Name: " << (newKeyret > 0  ? remappedKeyName : "(null)") << "\n";std::cout << "  Original Scan Code: 0x" << std::hex << originalScanCode << "\n";std::cout << "  Original Key Name: " << (oldKeyret > 0 ? originalKeyName : "(null)") << "\n";}
}// 根据配置文件修改或添加重映射的按键
void ModifyRemappedKeys(const std::string& configFile) {std::ifstream file(configFile);if (!file.is_open()) {std::cerr << "Failed to open config file: " << configFile << std::endl;return;}std::string line;int totalRemappedKeys = 0;WORD tempCode = 0, theCode = 0;std::vector<WORD> originalScanCodes, newScanCodes;while (std::getline(file, line)) {if (line.empty() || line[0] == '#') // 绕过空行和注释continue;if (line.find(CONFIG_FILE_SECTION_REMAP_KEY_NUM) != std::string::npos) {// 循环绕过多行注释或空行do{std::getline(file, line); // 读取包含总重映射按键数的下一行}while (line.empty() || line[0] == '#');std::istringstream iss(line);iss >> totalRemappedKeys;originalScanCodes.reserve(totalRemappedKeys);newScanCodes.reserve(totalRemappedKeys);}else if (line.find(CONFIG_FILE_SECTION_MAP_KEY_ORI_CODE) != std::string::npos) {do {std::getline(file, line); // 读取包含原始扫描码的行} while (line.empty() || line[0] == '#');std::istringstream iss(line);std::string hexCode;iss >> hexCode;tempCode = static_cast<WORD>(std::stoi(hexCode, nullptr, 16));// stoi 读取时默认小端序,会导致数据颠倒,需要恢复一下theCode = ((tempCode & 0xFF) << 8) | ((tempCode >> 8) & 0xFF);originalScanCodes.push_back(theCode);}else if (line.find(CONFIG_FILE_SECTION_MAP_KEY_NEW_CODE) != std::string::npos) {do {std::getline(file, line); // 读取包含新扫描码的行} while (line.empty() || line[0] == '#');std::istringstream iss(line);std::string hexCode;iss >> hexCode;tempCode = static_cast<WORD>(std::stoi(hexCode, nullptr, 16));// stoi 读取时默认小端序,会导致数据颠倒,需要恢复一下theCode = ((tempCode & 0xFF) << 8) | ((tempCode >> 8) & 0xFF);newScanCodes.push_back(theCode);}}if (originalScanCodes.size() != totalRemappedKeys || newScanCodes.size() != totalRemappedKeys) {std::cerr << "Invalid configuration file format." << std::endl;return;}// 创建基于配置数据的缓冲区std::vector<BYTE> buffer(20 + totalRemappedKeys * 4, 0); // 头部(20 字节)+ 重映射按键数据*(DWORD*)(buffer.data() + 8) = totalRemappedKeys + 1; // 记录个数 = 实际个数 + 1(规则)for (int i = 0; i < totalRemappedKeys; ++i) {std::cout << "OriKeyCode: 0x" << std::hex << originalScanCodes[i] <<"\t NewKeyCode: 0x" << std::hex << newScanCodes[i] << "\n";// 写入数据到向量结构中*(WORD*)(buffer.data() + 12 + i * 4) = newScanCodes[i];*(WORD*)(buffer.data() + 14 + i * 4) = originalScanCodes[i];}// 修改注册表中的重映射按键HKEY hKey;if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_KEY, 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) {if (RegSetValueEx(hKey, REGISTRY_VALUE_NAME, 0, REG_BINARY, buffer.data(), buffer.size()) == ERROR_SUCCESS) {std::cout << "Scancode Map updated successfully.\n";}else {std::cerr << "Failed to update Scancode Map.\n";}RegCloseKey(hKey);}else {std::cerr << "Failed to open registry key.\n";}
}// 钩子过程函数,处理键盘输入消息
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) {if (nCode == HC_ACTION) {KBDLLHOOKSTRUCT* kbStruct = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {CHAR keyName[256];if (GetKeyNameTextA(kbStruct->scanCode << 16, keyName, sizeof(keyName)) > 0) {keyNameValuePairs.emplace_back(keyName, kbStruct->scanCode);if (--remainingKeys == 0) {std::cout << "Round completed.\n";if (--remainingRounds == 0) {std::cout << "Maximum rounds reached.\n";std::cout << "Continuous Input:\n";for (const auto& pair : keyNameValuePairs) {std::cout << pair.first << ": 0x" << std::hex << pair.second << "\n";}PostQuitMessage(0); // 退出消息循环}else {std::cout << "Continuous Input so far:\n";for (const auto& pair : keyNameValuePairs) {std::cout << pair.first << ": 0x" << std::hex << pair.second << "\n";}keyNameValuePairs.clear();remainingKeys = numKeys;std::cout << "\nPlease press " << numKeys << " keys per round.\n";}}}}}return CallNextHookEx(NULL, nCode, wParam, lParam);
}// 监视键盘输入以查询给定键的扫描码
void QueryScanCodeForKey() {// 键入循环参数do {std::cout << "Please press numbers of keys per round, " <<"and a maximum of rounds (for at least 1 key for 1 round)." << std::endl;std::cin >> numKeys >> remainingRounds;} while (numKeys < 1 || remainingRounds < 1);std::cout << "Monitoring " << numKeys << " keys per round, for a maximum of " << remainingRounds << " rounds.\n";remainingKeys = numKeys;// 安装键盘输入的钩子HHOOK keyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, KeyboardHookProc, NULL, 0);// 消息循环以保持钩子活动MSG msg;while (GetMessage(&msg, NULL, 0, 0)) {TranslateMessage(&msg);DispatchMessage(&msg);}// 卸载钩子UnhookWindowsHookEx(keyboardHook);
}// 从注册表中删除所有重映射按键
void DeleteAllRemappedKeys() {// 二次确认std::cout << "Are you sure you want to delete all remapped keys? This action cannot be undone. (yes/no)\n";std::string confirmation;std::cin >> confirmation;if (confirmation != "yes") {std::cout << "Action cancelled.\n";return;}// 删除注册表中的重映射按键HKEY hKey;if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_KEY, 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) {LONG result = RegDeleteValue(hKey, REGISTRY_VALUE_NAME);if (result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND) {std::cout << "All remapped keys deleted successfully.\n";}else {std::cerr << "Failed to delete remapped keys.\n";}RegCloseKey(hKey);}else {std::cerr << "Failed to open registry key.\n";}
}void BackupScancodeMap() {// 打开注册表键HKEY hKey;if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_KEY, 0, KEY_WRITE | KEY_READ, &hKey) == ERROR_SUCCESS) {// 获取当前 Scancode Map 值的大小DWORD dataSize = 0;DWORD dataType;if (RegQueryValueEx(hKey, REGISTRY_VALUE_NAME, NULL, &dataType, NULL, &dataSize) == ERROR_SUCCESS && dataType == REG_BINARY) {// 获取当前时间作为备份文件名的一部分SYSTEMTIME sysTime;GetLocalTime(&sysTime);std::wstringstream backupKeyNameStream;backupKeyNameStream << BACKUP_KEY_PREFIX << std::setw(4) << std::setfill(L'0') << sysTime.wYear << L"_"<< std::setw(2) << std::setfill(L'0') << sysTime.wMonth << L"_"<< std::setw(2) << std::setfill(L'0') << sysTime.wDay << L"_"<< std::setw(2) << std::setfill(L'0') << sysTime.wHour << L"_"<< std::setw(2) << std::setfill(L'0') << sysTime.wMinute << L"_"<< std::setw(2) << std::setfill(L'0') << sysTime.wSecond;std::wstring backupKeyName = backupKeyNameStream.str();std::vector<BYTE> buffer(dataSize);if (RegQueryValueEx(hKey, REGISTRY_VALUE_NAME, NULL, NULL, buffer.data(), &dataSize) == ERROR_SUCCESS) {// 创建新的值项来存储备份数据LONG result = RegSetValueEx(hKey, backupKeyName.c_str(), 0, REG_BINARY, buffer.data(), dataSize);if (result == ERROR_SUCCESS) {std::wcout << L"Backup of Scancode Map value successful. Backup key name: " << backupKeyName << std::endl;// 询问用户是否删除原始键值项std::wcout << L"Do you want to delete the original registry key value? (Y/N): ";wchar_t response;std::wcin >> response;if (response == L'Y' || response == L'y') {if (RegDeleteValue(hKey, REGISTRY_VALUE_NAME) == ERROR_SUCCESS) {std::wcout << L"Original registry key value deleted successfully." << std::endl;}else {std::wcerr << L"Failed to delete original registry key value." << std::endl;}}else {std::wcout << L"Original registry key value not deleted." << std::endl;}}else {std::cerr << "Failed to write Scancode Map value to backup key: " << result << std::endl;}}else {std::cerr << "Failed to read registry value." << std::endl;}}else {std::cerr << "No Scancode Map value found in the registry." << std::endl;}// 关闭键RegCloseKey(hKey);}else {std::cerr << "Failed to open registry key." << std::endl;}
}// 枚举并读取所有备份的 Scancode Map 值的键名称
void EnumerateAndPrintBackupKeys() {// 打开注册表键HKEY hKey;if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_KEY, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {// 枚举所有值项的名称DWORD index = 0;WCHAR subKeyName[MAX_PATH];DWORD subKeyNameSize = MAX_PATH;while (RegEnumValue(hKey, index, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {// 检查值项的名称的前缀,如果是备份值项则输出时间信息if (wcsncmp(subKeyName, BACKUP_KEY_PREFIX, wcslen(BACKUP_KEY_PREFIX)) == 0) {std::wcout << L"Backup time: " << subKeyName << std::endl;}// 重置键名缓冲区大小subKeyNameSize = MAX_PATH;// 移动到下一个值项index++;}RegCloseKey(hKey);}else {std::cerr << "Failed to open registry key." << std::endl;}
}// 还原指定时间的 Scancode Map 值备份
void RestoreBackupScancodeMap(const std::wstring& backupTime) {// 打开注册表键HKEY hKey;if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_KEY, 0, KEY_SET_VALUE | KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {// 构造备份值项的名称std::wstring backupKeyName = BACKUP_KEY_PREFIX + backupTime;DWORD dataSize = 0;DWORD dataType;// 获取备份值项的大小if (RegQueryValueEx(hKey, backupKeyName.c_str(), NULL, &dataType, NULL, &dataSize) == ERROR_SUCCESS && dataType == REG_BINARY) {// 读取备份值项std::vector<BYTE> buffer(dataSize);if (RegQueryValueEx(hKey, backupKeyName.c_str(), NULL, NULL, buffer.data(), &dataSize) == ERROR_SUCCESS) {// 将备份值项的数据写入 Scancode Mapif (RegSetValueEx(hKey, REGISTRY_VALUE_NAME, 0, REG_BINARY, buffer.data(), dataSize) == ERROR_SUCCESS) {std::wcout << L"Restored Scancode Map from backup created at: " << backupTime << std::endl;}else {std::cerr << "Failed to restore Scancode Map from backup." << std::endl;}}}else {std::cerr << "Backup key not found or not a valid REG_BINARY value." << std::endl;}RegCloseKey(hKey);}else {std::cerr << "Failed to open registry key." << std::endl;}
}// 更新重映射信息到注册表
bool UpdateRemapInfo(const std::vector<std::pair<WORD, WORD>>& remapInfo) {std::vector<BYTE> buffer(20 + remapInfo.size() * 4, 0);*(DWORD*)(buffer.data() + 8) = remapInfo.size() + 1;for (size_t i = 0; i < remapInfo.size(); ++i) {*(WORD*)(buffer.data() + 12 + i * 4) = remapInfo[i].second;*(WORD*)(buffer.data() + 14 + i * 4) = remapInfo[i].first;}HKEY hKey;if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_KEY, 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) {if (RegSetValueEx(hKey, REGISTRY_VALUE_NAME, 0, REG_BINARY, buffer.data(), buffer.size()) == ERROR_SUCCESS) {RegCloseKey(hKey);return true;}RegCloseKey(hKey);}return false;
}// 删除重映射信息中的指定项
void DeleteRemapItemByIndex(std::vector<std::pair<WORD, WORD>>& remapInfo) {// 枚举原有数据EnumerateRemappedKeys(remapInfo);std::cout << "Enter the numbers of items you want to delete (e.g., 1 2 3), separated by spaces: ";std::string input;std::getline(std::cin, input);std::istringstream iss(input);std::vector<int> deleteIndices;int index;while (iss >> index) {deleteIndices.push_back(index - 1); // 索引从1开始,转换为从0开始}std::vector<std::pair<WORD, WORD>> updatedRemapInfo;for (size_t i = 0; i < remapInfo.size(); ++i) {if (std::find(deleteIndices.begin(), deleteIndices.end(), i) == deleteIndices.end()) {updatedRemapInfo.push_back(remapInfo[i]);}}if (UpdateRemapInfo(updatedRemapInfo)) {std::cout << "Remapped items deleted successfully.\n";}else {std::cerr << "Failed to delete remapped items.\n";}
}// 添加重映射按键
void AddRemapItemByIndex(std::vector<std::pair<WORD, WORD>>& remapInfo,std::pair<WORD, WORD> NewMapNode, size_t dwIdToInsert) {// 参数范围校验if (dwIdToInsert < 1 || dwIdToInsert > remapInfo.size() ){std::cerr << "Error invalid parameters.\n";return;}// 检查是否有重复的映射for (const auto& pair : remapInfo) {if (pair.first == NewMapNode.first || pair.second == NewMapNode.second) {std::cerr << "Error Duplicate remapped item.\n";return;}}// 向 remapInfo 中索引为 dwIdToInsert 前面插入元素 NewMapNode// 索引为 dwIdToInsert-1 后面插入等同于在索引为 dwIdToInsert 前插入 auto it = remapInfo.begin() + dwIdToInsert;remapInfo.insert(it, NewMapNode);// 利用新的结构信息更新注册表if (UpdateRemapInfo(remapInfo)) {std::cout << "Remapped items add successfully.\n";}else {std::cerr << "Failed to insert remapped items.\n";}
}// 禁用 Remap 注册表项
void ChangeScancodeMapStatus(BOOL bEnable) {// 打开注册表键HKEY hKey;if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_KEY, 0, KEY_WRITE | KEY_READ, &hKey) == ERROR_SUCCESS) {// 获取当前 Scancode Map 值的大小DWORD dataSize = 0;DWORD dataType;// 构造新的名称std::wstringstream disabledKeyNameStream;std::wstringstream checkKeyNameStream;disabledKeyNameStream << REGISTRY_VALUE_NAME << (bEnable ? L"" : DESABLED_KEY_PREFIX);std::wstring disabledKeyName = disabledKeyNameStream.str();checkKeyNameStream << REGISTRY_VALUE_NAME << (bEnable ? DESABLED_KEY_PREFIX : L"");std::wstring checkKeyName = checkKeyNameStream.str();if (RegQueryValueEx(hKey, checkKeyName.c_str(), NULL, &dataType, NULL, &dataSize) == ERROR_SUCCESS && dataType == REG_BINARY) {std::vector<BYTE> buffer(dataSize);if (RegQueryValueEx(hKey, checkKeyName.c_str(), NULL, NULL, buffer.data(), &dataSize) == ERROR_SUCCESS) {// 创建新的值项来存储备份数据LONG result = RegSetValueEx(hKey, disabledKeyName.c_str(), 0, REG_BINARY, buffer.data(), dataSize);if (result == ERROR_SUCCESS) {std::wcout << (bEnable ? L"Enable" : L"Disable")<< L" Scancode Map value successful. New key name: " << disabledKeyName << std::endl;// 删除原始键值项if (RegDeleteValue(hKey, checkKeyName.c_str()) == ERROR_SUCCESS) {std::wcout << L"Original registry key value deleted successfully." << std::endl;}else {std::wcerr << L"Failed to delete original registry key value." << std::endl;RegDeleteValue(hKey, disabledKeyName.c_str());}}else {std::cerr << "Failed to write Scancode Map value to disabled key: " << result << std::endl;}}else {std::cerr << "Failed to read registry value." << std::endl;}}else {if (RegQueryValueEx(hKey, disabledKeyName.c_str(), NULL, &dataType, NULL, &dataSize) == ERROR_SUCCESS && dataType == REG_BINARY) {std::wcout << L"Remapping keys were already "<< (bEnable ? L"enabled" : L"disabled")<< L" earlier." << std::endl;}else {std::cerr << "No Scancode Map value found in the registry." << std::endl;}}// 关闭键RegCloseKey(hKey);}else {std::cerr << "Failed to open registry key." << std::endl;}
}// 将 char* 类型的字符串转换为 std::wstring
std::wstring ConvertToWideString(const char* str) {int size = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);if (size == 0) {// 转换失败return L"";}std::wstring result(size, L'\0');MultiByteToWideChar(CP_UTF8, 0, str, -1, &result[0], size);return result;
}int main(int argc, char* argv[]) {if (argc < 2) {std::cerr << "Usage: " << argv[0] << " <option> [configFile]\n";return 1;}std::string option = argv[1];if (option == "/enumerate") {   // 解析目前注册表中重映射键值的数据std::vector<std::pair<WORD, WORD>> remapInfo;if (ParseRemapInfo(remapInfo)) {EnumerateRemappedKeys(remapInfo);}else {std::cerr << "Failed to parse remap info.\n";}}else if (option == "/setremap") {  // 向注册表中覆盖写入重映射键值的数据if (argc != 3) {std::cerr << "Usage: " << argv[0] << " /setremap <configFilePath>\n";return 1;}std::string configFile = argv[2];ModifyRemappedKeys(configFile);}else if (option == "/queryActiveKey") {   // 动态查询按键的扫描码QueryScanCodeForKey();}else if (option == "/deleteAllMap")   // 删除所有重映射键数据{DeleteAllRemappedKeys();}else if (option == "/modifyDelKey") {    // 删除指定的重映射键数据std::vector<std::pair<WORD, WORD>> remapInfo;if (ParseRemapInfo(remapInfo)) {DeleteRemapItemByIndex(remapInfo);}else {std::cerr << "Failed to parse remap info.\n";}}else if (option == "/modifyInsertKey")      // 在指定的元素后面添加新的按键映射{std::vector<std::pair<WORD, WORD>> remapInfo;WORD oldScanCode = 0, newScanCode = 0;size_t index = 0;if (ParseRemapInfo(remapInfo)) {  // 解析注册表参数// 枚举原有数据EnumerateRemappedKeys(remapInfo);// 选择插入的位置和数据std::cout << "Enter the Original Key scan code: ";std::cin >> std::hex >> oldScanCode;std::cout << "Enter the Key scan code you wannt to replace to: ";std::cin >> std::hex >> newScanCode;std::cout << "Enter the number of item you wannt to insert after: ";std::cin >> index;// 调整字节顺序oldScanCode = ((oldScanCode & 0xFF00) >> 8) | ((oldScanCode & 0x00FF) << 8);newScanCode = ((newScanCode & 0xFF00) >> 8) | ((newScanCode & 0x00FF) << 8);// 写入数据AddRemapItemByIndex(remapInfo,std::make_pair(oldScanCode, newScanCode), index);}else {std::cerr << "Failed to parse remap info.\n";}}else if (option == "/modifyAddKey")     // 在映射表的末尾追加按键映射{std::vector<std::pair<WORD, WORD>> remapInfo;WORD oldScanCode = 0, newScanCode = 0;size_t index = 0;if (ParseRemapInfo(remapInfo)) {  // 解析注册表参数// 选择插入的位置和数据std::cout << "Enter the Original Key scan code: ";std::cin >> std::hex >> oldScanCode;std::cout << "Enter the Key scan code you wannt to replace to: ";std::cin >> std::hex >> newScanCode;index = remapInfo.size(); // 在结尾插入// 调整字节顺序oldScanCode = ((oldScanCode & 0xFF00) >> 8) | ((oldScanCode & 0x00FF) << 8);newScanCode = ((newScanCode & 0xFF00) >> 8) | ((newScanCode & 0x00FF) << 8);// 写入数据AddRemapItemByIndex(remapInfo,std::make_pair(oldScanCode, newScanCode), index);}else {std::cerr << "Failed to parse remap info.\n";}}else if (option == "/statusMap")   // 启用或者禁用重映射键(可恢复,1表示启用,0表示禁用){if (argc != 3) {std::cerr << "Usage: " << argv[0] << " /statusMap <bEnable>\n";return 1;}if (std::string(argv[2]) == "1"){std::cout << "Try to enable ReMap Key.\n";ChangeScancodeMapStatus(TRUE);}else if (std::string(argv[2]) == "0"){std::cout << "Try to disable ReMap Key.\n";ChangeScancodeMapStatus(FALSE);}}else if (option == "/makebackup") {  // 备份当前注册表重映射键数据BackupScancodeMap();}else if (option == "/recoverMap") {  // 按照备份日期还原数据if (argc != 3) {std::cerr << "Usage: " << argv[0] << " /recoverMap <backupTime>\n";return 1;}std::wstring backupTime = ConvertToWideString(argv[2]);if (backupTime == L"")return 1;RestoreBackupScancodeMap(backupTime);}else if (option == "/readbackup") {   // 读取备份文件列表EnumerateAndPrintBackupKeys();}else {std::cerr << "Invalid option.\n";return 1;}return 0;
}

程序需要管理员权限,设置清单文件即可:

标题

测试截图如下:

标题

3.2 C# 实现的窗口工具

Medo 制作的开源工具 Scancode Map 是一个不错的选择,它具有完备的查询、修改、删除等功能,我的程序一定程度上参考了他的思路。

这是该工具的界面视图:

Scancode Map 截图 1
Scancode Map 截图 2

原文链接为:Scancode Map 1.11 – Medo's Home Page。

网盘下载链接:https://pan.baidu.com/s/1Dxn8R3GEdol59ObYAMTLHw?pwd=vtq3

提取码:vtq3 (里面有我重新编译好的文件在 /source/bin 内,并且包含我写的控制台程序)

官网程序下载链接:https://www.medo64.com/download/scancodemap111.exe。

源代码 Github 链接:Release Most recent build · medo64/ScancodeMap · GitHub。

四、总结

本文简单介绍了使用 "Scancode Map" 键盘扫描码映射表禁用 CtrlAltDel 键的方法。浅谈了该方法的利弊。修改注册表屏蔽该按键的方法还有另外一个稳定的方法,就是利用 Disable 项隐藏相应的选项,这个方法也是以前讨论最多的方法,比按键扫描码用的还广泛,还有一个就是进程冻结法。但是都有缺点,比如可以注册表方法是可以轻易恢复的;进程冻结存在不稳定性,冻结后容易让进程陷入 RPC 死锁导致无法恢复。


【保留备用】

本文属于原创文章,转载请注明出处:

https://blog.csdn.net/qq_59075481/article/details/136104444。

发布于:2024.02.13,更新于:2024.02.13.

这篇关于键盘重映射禁用 CtrlAltDel 键的利弊的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/707174

相关文章

键盘快捷键:提高工作效率与电脑操作的利器

键盘快捷键:提高工作效率与电脑操作的利器 在数字化时代,键盘快捷键成为了提高工作效率和优化电脑操作的重要工具。无论是日常办公、图像编辑、编程开发,还是游戏娱乐,掌握键盘快捷键都能带来极大的便利。本文将详细介绍键盘快捷键的概念、重要性、以及在不同应用场景中的具体应用。 什么是键盘快捷键? 键盘快捷键,也称为热键或快捷键,是指通过按下键盘上的一组键来完成特定命令或操作的方式。这些快捷键通常涉及同

如何快速练习键盘盲打

盲打是指在不看键盘的情况下进行打字,这样可以显著提高打字速度和效率。以下是一些练习盲打的方法: 熟悉键盘布局:首先,你需要熟悉键盘上的字母和符号的位置。可以通过键盘图或者键盘贴纸来帮助记忆。 使用在线打字练习工具:有许多在线的打字练习网站,如Typing.com、10FastFingers等,它们提供了不同难度的练习和测试。 练习基本键位:先从学习手指放在键盘上的“家位”开始,通常是左手的

推荐练习键盘盲打的网站

对于初学者来说,以下是一些推荐的在线打字练习网站: 打字侠:这是一个专业的在线打字练习平台,提供科学合理的课程设置和个性化学习计划,适合各个水平的用户。它还提供实时反馈和数据分析,帮助你提升打字速度和准确度。 dazidazi.com:这个网站提供了基础的打字练习,适合初学者从零开始学习打字。 Type.fun打字星球:提供了丰富的盲打课程和科学的打字课程设计,还有诗词歌赋、经典名著等多样

docker 重启容器且修改服务映射端口

要重启 Docker 容器并修改服务的映射端口,可以按照以下步骤进行操作: 1. 停止当前运行的容器 如果你想重新配置端口,通常需要先停止当前运行的容器。你可以使用以下命令停止容器: docker stop <container_name_or_id> 2. 删除现有容器 为了修改端口映射,你需要删除旧的容器并重新创建一个新的容器。首先运行以下命令删除停止的容器: docker rm

Android 进入页面后默认不弹出系统键盘

在onCreate中加入一行代码getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

解决TMP_InputField 在WebGL(抖音)上不能唤起虚拟键盘,不能使用手机内置输入法的问题

整整花费了一天时间测试和解决。试验了多个方法,花了不少美刀,最终才发现抖音这个官方文档,哭了: https://partner.open-douyin.com/docs/resource/zh-CN/mini-game/develop/guide/game-engine/rd-to-SCgame/open-capacity/capability-adaptation/sc_webgl_keyboa

maven项目中程序运行编译的时候出现:编码GBK的不可映射字符

由于JDK是国际版的,我们在用javac.exe编译时,编译程序首先会获得我们操作系统默认采用的编码格式(也即在编译java程序时,若我们不指定源程序文件的编码格式,JDK首先获得操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式,如WIN2k,它的值为GBK),然后JDK就把我们的java源程序从file.encoding编码格式转化为JAVA内部默认的UNICODE格

ManyToMany双向外键关联(基于注解)的映射案例(简单版)

学生和老师就是多对多的关系,一个学生有多个学生,一个老师也有多个学生,这里的多对多映射采用中间表连接的映射策略,建立中间表的映射策略,建立中间表分别引入俩边的主键作为外键。通过中间表映射俩个表之间的关系。 下面就以学生类和老师类为例介绍多对多的映射关系的实例 Students类 package mtm_bfk;import java.io.Serializable;import java.

Mybatis查询到多条记录但是映射到list集合只有一条或者条数不对

如果你出现了这个问题。 *. 原因:mybatis是基于当前实体对象的id进行映射的 *.所以在查询字段中加上查询id即可。 搞定!!!!

【高等数学学习记录】映射

【高等数学&学习记录】映射 从事测绘工作多年,深刻感受到基础知识的重要及自身在这方面的短板。 为此,打算重温测绘工作所需基础知识。练好基本功,为测绘工作赋能。 1 知识点 1.1 映射 映射 设 X X X、 Y Y Y是非空集合,若存在法则 f f f,使 X X X中每个元素 x x x,在 Y Y Y中有唯一确定的元素 y y y与之对应,则称 f f f为从 X X X到