Набросаем такое тестовое приложение:
program Project20;
{$APPTYPE CONSOLE}
usesWindows, SysUtils;
var
regValue, regData: WideString;
cbSize, LastError: DWORD;
begin
regValue := 'StringValue';
regData := 'ab';
cbSize := Length(regData) * SizeOf(WideChar);
Writeln(Format('RegSetValueExW: size = %d bytes', [cbSize]));
LastError := RegSetValueExW(HKEY_CURRENT_USER, PWideChar(regValue), 0, REG_SZ, PWideChar(regData), cbSize);
if (LastError = ERROR_SUCCESS) then
begin
cbSize := 0;
LastError := RegQueryValueExW(HKEY_CURRENT_USER, PWideChar(regValue), nil, nil, nil, @cbSize);
Writeln(Format('RegQueryValueExW: size = %d bytes', [cbSize]));
RegDeleteValueW(HKEY_CURRENT_USER, PWideChar(regValue));
end
end.
По правде говоря вышеприведенный пример показывает как не надо программировать. Но об этом чуть позже.
Запустив программу на исполнение, получим такой вывод:
C:\>Project20.exe RegSetValueExW: size = 4 bytes RegQueryValueExW: size = 6 bytes
Т.е. мы попросили записать 4 байта, а реально записано было 6! Мистика...
На самом мы и должны были передать 6 функции RegSetValueExW в качестве значения параметра cbData - так написано в документации (MSDN)
... cbData [in] The size of the information pointed to by the lpData parameter, in bytes. 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. ...
Но данный пример демонстрирует, что в некоторых случаях система может в некотором смысле "игнорировать" значение параметра cbData. В ходе небольшого "расследования" выяснилось, что это - дело рук RegSetValueExW. Ниже приведен ее псевдо-код (Delphi), которые поясняет суть происходящего (+ комментарии разработчика, найденные в старых исходниках Windows):
...
//
// Special hack to help out all the idiots who
// believe the length of a NULL terminated string is
// strlen(foo) instead of strlen(foo) + 1.
//
lpString := PWideChar(lpData);
StringLength := cbData div SizeOf(WideChar);
if (((dwType = REG_SZ) or (dwType = REG_EXPAND_SZ) or (dwType = REG_MULTI_SZ)) and
(StringLength > 0) and (lpString[pred(StringLength)] <> #0)) then
//
// Do this under an exception handler in case the last
// little bit crosses a page boundary.
//
try
if (lpString[StringLength] = #0) then
inc(cbData, SizeOf(WideChar)) // increase string length to account for NULL terminator
except
// guess they really really did not want a NULL terminator
end
...
А если вспомнить, что строковые типы в Delphi (String/WideString) для совместимости с API всегда завершаются нуль-терминатором, то все становится на свои места :) Условие (lpString[StringLength] = #0) выполняется и система "искусственно" увеличивает cbData.
В реализации RegQueryValueExW тоже есть "Special hack"...
// // Special hack to help out all the idiots who // believe the length of a NULL terminated string is // strlen(foo) instead of strlen(foo) + 1. // If the last character of the buffer is not a NULL // and there is enough space left in the caller's buffer, // slap a NULL in there to prevent him from going nuts // trying to do a strlen(). //

Комментариев нет:
Отправить комментарий