Набросаем такое тестовое приложение:
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(). //
Комментариев нет:
Отправить комментарий