简体   繁体   English

为什么即使我打破了关于长度计入NUL终止的规定,RegSetValueEx仍能正常工作?

[英]Why does RegSetValueEx work even when I break the rule about accounting for NUL termination in the length?

I've got a simple program that adds calc.exe to startup: 我有一个简单的程序,可以将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);
}

For me the world of c/c++/WIN32API is still full of misteries... so I have few questions. 对我来说,c / c ++ / WIN32API的世界仍然充满着烦恼...所以我有几个问题。

1. When I define string is it automatically null terminated? 1.当我定义字符串时,它会自动以null结尾吗?

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

or should it be done: 还是应该这样做:

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

2. In my code is final argument to RegSetValueEx set to correct value? 2.在我的代码中,将RegSetValueEx的最终参数设置为正确的值吗?

From MSDN - RegSetValueEx function page: MSDN-RegSetValueEx函数页面:

cbData [in] The size of the information pointed to by the lpData parameter, in bytes. cbData [in] lpData参数指向的信息的大小,以字节为单位。 If the data is of type REG_SZ, REG_EXPAND_SZ, or REG_MULTI_SZ, cbData must include the size of the terminating null character or characters. 如果数据的类型为REG_SZ,REG_EXPAND_SZ或REG_MULTI_SZ,则cbData必须包含一个或多个终止空字符的大小。

cchProgram is set to 28 characters without null termination. cchProgram设置为28个字符, 没有空终止。 On my system(because of UNICODE I think?) cchProgram * sizeof(TCHAR) = 56. 在我的系统上(因为我认为是UNICODE?) cchProgram * sizeof(TCHAR) = 56。

Shouldn't I set it to 58 to add null termination? 我是否应该将其设置为58以添加空终止?


When I run this program, as it is above, without any modifications and I'll check Calculator value in registry via Modify binary date I get: 当我如上所述运行该程序时,未进行任何修改,我将通过Modify binary date在注册表中检查Calculator值,我得到:

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                   ..

Its 58 bytes including null termination. 它的58个字节包括空终止。 I'am confuse:/ 我很困惑:/

UPDATE 更新

Accounting for a NULL character by adding 1 to string length when calculating cbData yields exactly same result as without adding it. 在计算cbData时,通过在字符串长度上加1来解决NULL字符的问题,其结果与未加上字符的结果完全相同。

cchProgram * sizeof(TCHAR) produces same data entry as (cchProgram + 1) * sizeof(TCHAR) cchProgram * sizeof(TCHAR)产生与(cchProgram + 1)* sizeof(TCHAR)相同的数据条目

Providing value smaller then a string length doesn't add NULL byte and copies given number of bytes. 提供小于字符串长度的值不会添加NULL字节,而不会复制给定数量的字节。

27 * sizeof(TCHAR) as cbData produces: 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.

I am on some old XP, service pack god knows what, I don't know how other version of windows would handle it. 我使用的是旧版XP,服务包真不知道,我不知道其他版本的Windows将如何处理它。

1: Yes, it will be null terminated without the need for \\0 . 1:是的,它将以null终止,不需要\\0

Double quoted strings (") are literal constants whose type is in fact a null-terminated array of characters. So string literals enclosed between double quotes always have a null character ('\\0') automatically appended at the end. 双引号字符串(“)是文字常量,其类型实际上是一个以空值终止的字符数组。因此,双引号之间包含的字符串文字总是在结尾处自动附加一个空字符('\\ 0')。

2: _tcslen() doesn't include the null terminator. 2: _tcslen()不包含空终止符。 You can add sizeof(TCHAR) to add it. 您可以添加sizeof(TCHAR)来添加它。

The reason the value still works is probably because Windows tries to be robust even when given incorrect input. 该值仍然有效的原因可能是因为即使输入不正确,Windows也会尝试保持鲁棒性。 It is probably automatically appending the null terminator for you. 它可能会自动为您附加空终止符。 However, because the documentation says you must include the null terminator it may not always append it. 但是,由于文档说您必须包括null终止符,因此它不一定总是附加它。

When I define string is it automatically null terminated? 当我定义字符串时,它会自动以null结尾吗?

String literals are null-terminated, yes. 字符串文字以空值结尾,是的。 "Hello" is actually {'H', 'e', 'l', 'l', 'o', '\\0'} . "Hello"实际上是{'H', 'e', 'l', 'l', 'o', '\\0'}

In my code is final argument to RegSetValueEx set to correct value? 在我的代码中,将RegSetValueEx的最终参数设置为正确的值吗?

You're right that you need the null terminator. 没错,您需要空终止符。 An easier way would be sizeof(TEXT("C:\\\\WINDOWS\\\\system32\\\\calc.exe")) if the string literal is short, since sizeof("Hello") is 6; 如果字符串文字很短, sizeof(TEXT("C:\\\\WINDOWS\\\\system32\\\\calc.exe"))简单的方法是sizeof(TEXT("C:\\\\WINDOWS\\\\system32\\\\calc.exe")) ,因为sizeof("Hello")为6; it includes the null-terminator, but in most cases, you'll need your variable and will have to add one to the length you get from string character-counting functions, since they don't include the null-terminator. 它包含空终止符,但是在大多数情况下,您将需要变量,并且必须在从字符串字符计数函数获得的长度中增加一个,因为它们不包含空终止符。

Ben Voigt made an excellent point below that a const TCHAR[] program = TEXT("text"); Ben Voigt在下面指出了一个很好的观点: const TCHAR[] program = TEXT("text"); can be used the same way as a literal in the call ( sizeof(program) ), but it a lot more maintainable when you want to change one less place in the code, which is a must for any actual project instead of a really small test, and even that can grow. 可以像调用中的文字一样使用它( sizeof(program) ),但是当您想在代码中减少更少的位置时,它的可维护性要高得多,这对于任何实际项目都是必须的,而不是很小的项目测试,甚至可以增长。

Finally, there are two things you should get out of your head early: 最后,您应该提早做好两件事:

  1. Hungarian notation: Don't do it. 匈牙利语表示法:请勿这样做。 It's outdated and rather pointless. 它已经过时,而且毫无意义。

  2. TCHAR : Just use wide strings with any Windows API functions you can. TCHAR :只需将宽字符串与任何Windows API函数一起使用即可。

What you're doing absolutely right is checking function calls for errors. 您绝对正确的做法是检查函数调用是否存在错误。 You wouldn't believe how many problems asked about can be solved by checking for failure and using GetLastError when the documentation says to. 您不会相信通过文档中的检查失败和使用GetLastError可以解决多少问题。


Since you asked how you're supposed to use C++ facilities, here's one way, with a couple changes that make more sense for using C++: 既然您问过应该如何使用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();
}

A better way to do this using C++ idioms would be to at least have a RegKey RAII class that calls RegCloseKey in its destructor and saves you the work. 使用C ++习惯用法进行此操作的更好方法是至少具有一个RegKey RAII类,该类在其析构函数中调用RegCloseKey并为您节省工作。 At the very least, it could be used like this: 至少,它可以这样使用:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么即使我没有为它分配内存也能工作? - Why does gets work even when I have not allocated memory to it? RegOpenKeyEx和RegSetValueEx失败,但是我不知道为什么 - RegOpenKeyEx and RegSetValueEx fail, but I dont know why 为什么它在违反初始化列表的规则时会起作用 - Why does it work when it breaks the rule of order of initialization list 即使我没有提到数组的大小,为什么在编译和运行代码时仍能正常工作? - Why does the code work fine when I compile and run it even though I have not mentioned size of array? 为什么 return 0 或 break 不能与逗号运算符一起使用? - Why does return 0 or break not work with the comma operator? 为什么这个 boost::spirit::qi 规则不起作用? - why does this boost::spirit::qi rule not work? 为什么即使我放了一个 break 语句,我的程序也会一直循环 - Why my program keeps looping even when I put a break statement 为什么这个函数对参数不起作用 - Why this function does not work about parameters 为什么即使我使用[[fallthrough]],GCC也会警告我一个漏洞? - Why is GCC warning me about a fallthrough even when I use [[fallthrough]]? RegSetValueEx未在Unicode项目构建中设置 - RegSetValueEx does not set in unicode project build
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM