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

这篇具有很好参考价值的文章主要介绍了键盘重映射禁用 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个也行)。

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

键盘重映射禁用 CtrlAltDel 键的利弊,快捷键机制系列文章,windows,微软,c++
按键名称-扫描码对照表

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

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

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

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

键盘重映射禁用 CtrlAltDel 键的利弊,快捷键机制系列文章,windows,微软,c++
注册表按键映射表

这张图是注册表中修改过后的结果,圈出来的就是需要关注的的位置, 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 Map
                if (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;
}

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

键盘重映射禁用 CtrlAltDel 键的利弊,快捷键机制系列文章,windows,微软,c++
标题

测试截图如下:

键盘重映射禁用 CtrlAltDel 键的利弊,快捷键机制系列文章,windows,微软,c++
标题

3.2 C# 实现的窗口工具

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

这是该工具的界面视图:

键盘重映射禁用 CtrlAltDel 键的利弊,快捷键机制系列文章,windows,微软,c++
Scancode Map 截图 1
键盘重映射禁用 CtrlAltDel 键的利弊,快捷键机制系列文章,windows,微软,c++
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.文章来源地址https://www.toymoban.com/news/detail-827671.html

到了这里,关于键盘重映射禁用 CtrlAltDel 键的利弊的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue 键盘快捷键标注

    参考:用html加css画个新拟态键盘_html 模拟键盘_背着行囊去远方的博客-CSDN博客

    2024年02月15日
    浏览(51)
  • RKH81 键盘快捷键

    ⌨️ 旋钮功能篇: ⚡ 电量提示篇: FN + ENTER — 查看电池用量情况 🌟 背光控制篇: FN + ↑ — 背光亮度 + FN + ↓ — 背光亮度 - FN + ← — 背光速度 - FN + → — 背光速度 + FN + — 切换背光颜色 FN + | — 支持多种背光效果切换 FN + — — 长按三秒,恢复出厂设置 FN + windo

    2024年02月11日
    浏览(67)
  • Blender 所有常用快捷键速查表:掌握 Blender 键盘快捷键

    Blender 是一款功能强大的开源 3D 和 2D 动画程序或计算机图形软件,任何人都可以免费使用。 无论您是动画师、建模师、VFX 艺术家还是游戏开发者,Blender 都是最佳选择。2000 年,Blender 2.0 推出。 二十年后,Blender 3.0 到来,标志着开源 2D 和 3D 内容创作时代的新篇章。 无论您

    2024年02月08日
    浏览(57)
  • Python | PyCharm 键盘快捷键

    创建和编辑 Alt  +  Enter 显示意图动作 Ctrl  +  Space 基本代码完成 Ctrl  +  Shift  +  Space 智能代码完成 Ctrl  +  Alt  +  Space 类型名称完成 Ctrl  +  Shift  +  Enter 完整声明 Ctrl  +  P  /  Alt  +  Q 参数信息/上下文信息 Ctrl  +  Shift  +  I 快速定义 Ctrl  +  Q  /  Shift  +  F1 快速

    2024年04月10日
    浏览(52)
  • mpv播放器键盘快捷键

    作为个人认为最好用的播放器之一的MPV播放器,其开源,体积小,支持的格式多样等等非常优秀的功能。但其中也有部分麻烦的地方,因为功能太过强大,导致不知道某些功能的快捷键是什么,这就非常需要一个可以查阅的表了。但是官方给的说明太过长了,我还是自己整理

    2024年02月11日
    浏览(56)
  • Modelsim键盘快捷键和鼠标操作

    Modelsim键盘快捷键和鼠标操作 使用Modelsim查看波形,大多数的操作是键盘和鼠标配合,通过熟悉快捷键操作可以帮助提操作效率。 内容来自Modelsim的官方指导手册,在GUI界面中可以这样查询: Help - SE Documentation - InfoHub(HTML Brower Required),如下图所示: 也可以通过安装目录进

    2024年02月16日
    浏览(56)
  • JS设置键盘快捷键、组合键

    键码了解下 [JavaScript键码表]身为一个键盘侠,不搞清楚键码映射表怎么可以???_码表映射脚本_你挚爱的强哥的博客-CSDN博客 keycode 0 =keycode 1 =keycode 2 =keycode 3 =keycode 4 =keycode 5 =keycode 6 =keycode 7 =keycode 8 = BackSpacekeycode 9 = Tabkeycode 10 =keycode 11 =keycode 12 = Clearkeycode 13 = Enterkeycode

    2024年02月03日
    浏览(51)
  • 罗技k380键盘的快捷键

    因为 他家店的客服好拉胯,不知道自家键盘的快捷键,我没使用过这一类的键盘不知道怎么办,只能网上搜罗资料进行记录和方便自己。 主要是 ipad 快捷键,键盘是罗技 k380,其他的系统纯属抄录其他渠道来的。 k380使用说明: 第一次连接时,打开键盘开关,打开 iPad 的 蓝牙

    2024年02月03日
    浏览(52)
  • Mac OS键盘常用快捷键

    图形 按键 ⌘ Command 键 ⌃ Control 键 ⌥ Option 键 ⇧ Shift 键 ⇪ Caps Lock fn 功能键 常用快捷键 剪切、拷贝和粘贴 您可以在大多数 app 中使用这些快捷键来剪切、拷贝或粘贴选中的项目。其中包括文本、图片、音乐等等。您甚至可以在 Finder 中拷贝和粘贴文件,来将文件拷贝到新位

    2024年02月14日
    浏览(59)
  • 自定义键盘快捷键调节电脑音量

    外接的键盘没有Fn。。。也没有调音量的键😅于是想自己弄一个 新建记事本文件打开,粘贴如下内容: 修改文件名后缀为.reg,双击运行会在注册表的HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlKeyboard Layout路径下新建Scancode Map,重启电脑后即可生效。 以上代码的内容为:F1静音或

    2024年02月09日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包