c++ - Why does RegSetValueEx work even when I break the rule about accounting for NUL termination in the length? -
i've got simple program adds calc.exe startup:
#include <windows.h> #include <tchar.h> int main(){ _tprintf(text("adding calc.exe 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 world of c/c++/win32api still full of misteries... have few questions.
1. when define string automatically null terminated?
lpctstr lpprogram = text("c:\\windows\\system32\\calc.exe");
or should done:
lpctstr lpprogram = text("c:\\windows\\system32\\calc.exe\0");
2. in code final argument regsetvalueex set correct value?
from msdn - regsetvalueex function page:
cbdata [in] size of information pointed lpdata parameter, in bytes. if data of type reg_sz, reg_expand_sz, or reg_multi_sz, cbdata must include size of terminating null character or characters.
cchprogram set 28 characters without null termination. on system(because of unicode think?) cchprogram * sizeof(tchar) = 56.
shouldn't set 58 add null termination?
when run program, above, without modifications , i'll check calculator value in registry via modify binary date get:
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. i'am confuse:/
update
accounting null character adding 1 string length when calculating cbdata yields same result without adding it.
cchprogram * sizeof(tchar) produces same data entry (cchprogram + 1) * sizeof(tchar)
providing value smaller string length doesn't add null byte , copies given number of bytes.
27 * sizeof(tchar) cbdata produces:
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 on old xp, service pack god knows what, don't know how other version of windows handle it.
when define string automatically null terminated?
string literals null-terminated, yes. "hello"
{'h', 'e', 'l', 'l', 'o', '\0'}
.
in code final argument regsetvalueex set correct value?
you're right need null terminator. easier way sizeof(text("c:\\windows\\system32\\calc.exe"))
if string literal short, since sizeof("hello")
6; includes null-terminator, in cases, you'll need variable , have add 1 length string character-counting functions, since don't include null-terminator.
ben voigt made excellent point below const tchar[] program = text("text");
can used same way literal in call (sizeof(program)
), lot more maintainable when want change 1 less place in code, must actual project instead of small test, , can grow.
finally, there 2 things should out of head early:
hungarian notation: don't it. it's outdated , rather pointless.
tchar
: use wide strings windows api functions can.
what you're doing absolutely right checking function calls errors. wouldn't believe how many problems asked can solved checking failure , using getlasterror
when documentation says to.
since asked how you're supposed use c++ facilities, here's 1 way, couple changes make more sense using c++:
#include <windows.h> int main(){ //r means raw string literal. note 1 backslash std::cout << r"(adding calc.exe 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 continue..." std::cin.get(); }
a better way using c++ idioms @ least have regkey
raii class calls regclosekey
in destructor , saves work. @ least, used this:
regkey key(hkey_local_machine, keyname, key_set_value); regsetvalueexw(key, ...); //could have implicit or explicit conversion, fill in ... //regclosekey called when key goes out of scope
Comments
Post a Comment