1

我有一个简单的程序,将 calc.exe 添加到启动:

#include <windows.h>
#include <tchar.h>

int main(){
    _tprintf(TEXT("Adding calc.exe to SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run...\n"));

    HKEY hRegRunKey;
    LPCTSTR lpKeyName = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run");
    LPCTSTR lpKeyValue = TEXT("Calculator");

    LPCTSTR lpProgram = TEXT("C:\\WINDOWS\\system32\\calc.exe");
    DWORD cchProgram = _tcslen(lpProgram);

    _tprintf(TEXT("Path: %s. \n"), lpProgram);
    _tprintf(TEXT("Length: %d. \n"), cchProgram);

    if(RegOpenKeyEx( HKEY_LOCAL_MACHINE, lpKeyName, 0, KEY_SET_VALUE, &hRegRunKey) == ERROR_SUCCESS){
        if(RegSetValueEx(hRegRunKey, lpKeyValue, 0, REG_SZ, (const BYTE *)lpProgram, cchProgram * sizeof(TCHAR)) != ERROR_SUCCESS){
            _tprintf(TEXT("ERROR: Can't set key value.\n"));
            exit(1);
        }else{
            _tprintf(TEXT("Key has been added sucessfully.\n"));
        }
    }

    Sleep(5000);
    RegCloseKey(hRegRunKey);
}

对我来说,c/c++/WIN32API 的世界仍然充满了谜团……所以我没有几个问题。

1. 当我定义字符串时,它会自动以空值终止吗?

LPCTSTR lpProgram = TEXT("C:\\WINDOWS\\system32\\calc.exe");

或者应该这样做:

LPCTSTR lpProgram = TEXT("C:\\WINDOWS\\system32\\calc.exe\0");

2. 在我的代码中,RegSetValueEx 的最后一个参数是否设置为正确的值?

来自MSDN - RegSetValueEx 功能页面:

cbData [in] lpData 参数指向的信息的大小,以字节为单位。如果数据的类型为 REG_SZ、REG_EXPAND_SZ 或 REG_MULTI_SZ,则 cbData 必须包括终止空字符的大小。

cchProgram设置为 28 个字符,无空终止。在我的系统上(因为我认为是 UNICODE?)cchProgram * sizeof(TCHAR) = 56。

我不应该将其设置为 58 以添加空终止吗?


当我运行这个程序时,如上所述,没有任何修改,我将通过修改二进制日期检查注册表中的计算器值,我得到:

43 00 3A 00 5C 00 57 00 C.:.\.W.
49 00 4E 00 44 00 4F 00 I.N.D.O.
57 00 53 00 5C 00 73 00 W.S.\.s.
79 00 73 00 74 00 65 00 y.s.t.e.
6D 00 33 00 32 00 5C 00 m.3.2.\.
63 00 61 00 6C 00 63 00 c.a.l.c.
2E 00 65 00 78 00 65 00 ..e.x.e.
00 00                   ..

它的 58 个字节包括空终止。我很困惑:/

更新

在计算cbData时,通过在字符串长度上加 1 来计算 NULL 字符会产生与不加它完全相同的结果。

cchProgram * sizeof(TCHAR)产生与(cchProgram + 1) * sizeof(TCHAR)相同的数据条目

提供小于字符串长度的值不会添加 NULL 字节并复制给定的字节数。

27 * sizeof(TCHAR)作为 cbData 产生:

43 00 3A 00 5C 00 57 00 C.:.\.W.
49 00 4E 00 44 00 4F 00 I.N.D.O.
57 00 53 00 5C 00 73 00 W.S.\.s.
79 00 73 00 74 00 65 00 y.s.t.e.
6D 00 33 00 32 00 5C 00 m.3.2.\.
63 00 61 00 6C 00 63 00 c.a.l.c.
2E 00 65 00 78 00       ..e.x.

我在一些旧的 XP 上,服务包天知道是什么,我不知道其他版本的 windows 会如何处理它。

4

2 回答 2

3

1:是的,它将被空终止而无需\0.

双引号字符串 (") 是文字常量,其类型实际上是以空字符结尾的字符数组。因此,括在双引号之间的字符串文字总是在末尾自动附加空字符 ('\0')。

2:_tcslen()不包括空终止符。您可以添加sizeof(TCHAR)以添加它。

该值仍然有效的原因可能是因为即使输入不正确,Windows 也会尝试保持稳健。它可能会自动为您附加空终止符。但是,因为文档说您必须包含空终止符,所以它可能并不总是附加它。

于 2013-05-13T22:09:53.307 回答
2

当我定义字符串时,它会自动以空值终止吗?

字符串文字是空终止的,是的。"Hello"实际上是{'H', 'e', 'l', 'l', 'o', '\0'}

在我的代码中,RegSetValueEx 的最后一个参数设置为正确的值吗?

你是对的,你需要空终止符。更简单的方法是sizeof(TEXT("C:\\WINDOWS\\system32\\calc.exe"))如果字符串文字很短,因为sizeof("Hello")是 6;它包括空终止符,但在大多数情况下,您将需要您的变量,并且必须将您从字符串字符计数函数获得的长度加一,因为它们不包括空终止符。

Ben Voigt 在下面提出了一个很好的观点,即 aconst TCHAR[] program = TEXT("text");可以与调用 ( sizeof(program)) 中的文字相同的方式使用,但是当您想要更改代码中的少一个地方时,它更易于维护,这对于任何实际项目都是必须的而不是一个非常小的测试,甚至可以增长。

最后,您应该尽早摆脱两件事:

  1. 匈牙利符号:不要这样做。它已经过时而且毫无意义。

  2. TCHAR: 只需将宽字符串与任何 Windows API 函数一起使用即可。

你做的绝对正确的是检查函数调用是否有错误。您不会相信有多少问题可以通过检查故障并GetLastError在文档说明时使用来解决。


既然你问过你应该如何使用 C++ 工具,这里有一种方法,有一些更改对使用 C++ 更有意义:

#include <windows.h>

int main(){
    //R means raw string literal. Note one backslash
    std::cout << R"(Adding calc.exe to SOFTWARE\Microsoft\Windows\CurrentVersion\Run...)" << '\n';

    const WCHAR[] keyName = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Run)");

    std::cout << "Enter program name: ";
    std::wstring keyValue;        
    if (!std::getline(std::wcin, keyValue)) {/*error*/}

    std::cout << "Enter full program path: ";
    std::wstring program;
    if (!std::getline(std::wcin, program)) {/*error*/}

    std::wcout << "Path: " << program << ".\n";
    std::cout << "Length: " << program.size() << ".\n";

    HKEY runKey;
    if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName, 0, KEY_SET_VALUE, &runKey)) {/*error*/}

    if(RegSetValueExW(runKey, keyValue.c_str(), 0, REG_SZ, reinterpret_cast<const BYTE *>(program.c_str()), (program.size() + 1) * 2)) {
        std::cout << "ERROR: Can't set key value.\n";
        return 1;
    }

    if (RegCloseKey(runKey)) {/*error*/}

    std::cout << "Key has been added successfully.\n";

    std::cout << "Press enter to continue..."    
    std::cin.get();
}

使用 C++ 习惯用法的更好方法是至少有一个RegKeyRAII 类,该类调用RegCloseKey其析构函数并为您节省工作。至少,它可以像这样使用:

RegKey key(HKEY_LOCAL_MACHINE, keyName, KEY_SET_VALUE);
RegSetValueExW(key, ...); //could have implicit or explicit conversion, fill in the ...
//RegCloseKey called when key goes out of scope
于 2013-05-13T22:05:02.040 回答