-
Доброго времени суток! Мне нужно работать с набором строк - записать в реестр и считать из реестра. В случае чтения - у меня получилось. Делаю я это с помощью процедуры ReadREG_MULTI_SZ . procedure ReadREG_MULTI_SZ(const CurrentKey: HKey; const Subkey, ValueName: string; Strings: TStrings); var valueType: DWORD; valueLen: DWORD; p, buffer: PChar; key: HKEY; begin // Clear TStrings // TStrings leeren Strings.Clear; // open the specified key // CurrentKey Schlussel offnen if RegOpenKeyEx(CurrentKey, PChar(Subkey), 0, KEY_READ, key) = ERROR_SUCCESS then begin // retrieve the type and data for a specified value name // Den Typ und Wert des Eintrags Ermitteln. SetLastError(RegQueryValueEx(key, PChar(ValueName), nil, @valueType, nil, @valueLen)); if GetLastError = ERROR_SUCCESS then if valueType = REG_MULTI_SZ then begin GetMem(buffer, valueLen); try // receive the value's data (in an array). // Ein Array von Null-terminierten Strings // wird zuruckgegeben RegQueryValueEx(key, PChar(ValueName), nil, nil, PBYTE(buffer), @valueLen); // Add values to stringlist // Werte in String Liste einfugen p := buffer; while p^ <> #0 do begin Strings.Add(p); Inc(p, lstrlen(p) + 1) end finally FreeMem(buffer) end end else raise ERegistryException.Create('Stringlist expected/ String Liste erwartet...') else raise ERegistryException.Create('Cannot Read MULTI_SZ Value/'+ 'Kann den MULTI_SZ Wert nicht lesen...'); end; end; А вот записать набор строк корректно у меня не получается. В результате преобразования преобразование строк в PCHAR: procedure ... var Buffer: Pointer; BufSize: DWORD; i, j, k: Integer; s: string; p: PChar; begin {подготовим буфер к записи} BufSize := 0; for i := 0 to list.Count - 1 do inc(BufSize, Length(list[i]) + 1); inc(BufSize); GetMem(Buffer, BufSize); k := 0; p := Buffer; for i := 0 to list.Count - 1 do begin s := list[i]; for j := 0 to Length(s) - 1 do begin p[k] := s[j + 1]; inc(k); end; p[k] := chr(0); inc(k); end; p[k] := chr(0); end; получаю не набор строк, а одну строку и дальше набор невразумительных символов. Поскажите как корректно записать Tstrings в параметр REG_MULTI_SZ регистра.
-
unit uRegistryEx;
interface
These classes are only usable in Windows!!!
uses
Winapi.Windows, System.SysUtils, System.Classes,
System.Win.Registry, System.RTLConsts;
Windows, SysUtils, Classes, Registry, RTLConsts;
const
HKEY_CURRENT_USER_LOCAL_SETTINGS = HKEY(Integer($80000007));
type
TRegistryEx = class(TRegistry)
public
procedure ReadMultiLine(const AName: string; Lines: TStrings);
procedure WriteMultiLine(const AName: string; Lines: TStrings);
end;
implementation
const
CRLF = #13#10;
ZeroChar = #0;
procedure TRegistryEx.ReadMultiLine(const AName: string; Lines: TStrings);
var
P: integer;
Str: string;
Size: integer;
begin
Size := GetDataSize(AName);
if (Size > 0) then
begin
SetLength(Str, Size div SizeOf(Char));
ReadBinaryData(AName, Str[1], Size);
repeat
P := Pos(ZeroChar, Str);
if P <> 0 then
begin
Delete(Str, P, 1);
Insert(CRLF, Str, P);
end;
until P = 0;
Lines.Text := Str;
end;
end;
procedure TRegistryEx.WriteMultiLine(const AName: string; Lines: TStrings);
var
i: integer;
Str: string;
begin
Str := '';
for I := 0 to Lines.Count - 1 do
if Length(Lines[I]) <> 0 then
Str := Str + Lines[I] + ZeroChar;
if not CheckResult(RegSetValueEx(CurrentKey, PChar(AName), 0, REG_MULTI_SZ, @Str[1],
Length(Str) * SizeOf(Char))) then
raise ERegistryException.CreateResFmt(@SRegSetDataFailed, [AName]);
end;
end.
-
> if P <> 0 then > begin > Delete(Str, P, 1); > Insert(CRLF, Str, P); > end;
сильно
-
> Игорь Шевченко © (30.11.12 22:52) [2]
> сильно
Да, коряво как то, я и сам знаю. Да StringReplace туда вставить и все. Или просто разбить по нулям и сразу добавить строки в список.
-
-
> Ваще имя (02.12.12 17:25) [4]
> while P^ <> #0 do > begin > S := P; > Strings.Add(S); > Inc(P, Length(S) + 1); > end;
ну я ж и говорю:
> Или просто разбить по нулям и сразу добавить строки в список.
Вообще, в виду того, что в реестре хранятся крайне малые строковые блоки, метод разбивки строки особой роли не играет, все равно будет быстро.
-
> Ваще имя (02.12.12 17:25) [4]
> P := MultiSz; > while P^ <> #0 do > begin > S := P; > Strings.Add(S); > Inc(P, Length(S) + 1); > end;
Вообще этот метод нельзя считать безопасным на 100%. Если в конце не окажется двух #0 то некоторых случаях уйдем в бесконечность, в лучшем случае получим мусор:
var
s: string;
sl: TStringList;
begin
sl := TStringList.Create;
try
s := '123456789';
_MultiSzToStrings(PChar(s), sl);
mmo1.Lines.Assign(sl);
finally
sl.Free;
end;
end;
-
> Вообще этот метод нельзя считать безопасным на 100%
Как и любую другую операцию со строками, завершенными \нулем, естественно.
Соответствует спецификации, а за корректность входных данных отвечает вызывающий.
-
> Ваще имя (02.12.12 18:50) [7]
> Как и любую другую операцию со строками, завершенными \нулем, > естественно.
в приведенный метод надо лишь добавить параметр Limit, значение которого мы можем получить из Tregistry.GetDataSize, и ограничить длину этим значением. Или использовать строки, как приведено выше, там Pos не выйдет за длину строки. Не вижу кстати ничего зазорного в однократном вызове StringReplace в данном случае, наезд на нее непонятен. Мы ж не гигабайты перемалывать будем.
-
> Ваще имя (02.12.12 18:50) [7]
> Соответствует спецификации, а за корректность входных данных > отвечает вызывающий.
Да, но в реестр без проблем можно записать строку не имеющую в конце двух #0. Большинство уязвимостей в ПО именно из таких ситуаций растут. Недаром MS понаделала функций для работы со строками, везде в которых добавлены лимиты.
-
> в приведенный метод надо лишь добавить параметр Limit
Можно и добавить, но что делать с последней строкой, если мы ждем четко определенного формата MULTI_SZ, а нам передали какую-то фигню?
> Мы ж не гигабайты перемалывать будем.
Вариант с Insert и Delete как раз перемалывает данные просто так.
> Большинство уязвимостей в ПО именно из таких ситуаций растут
Фактически, той уязвимости с перезаписью адреса возврата здесь нет. Возможны переполнение кучи и ошибка сегментации при чтении.
-
> Ваще имя (02.12.12 19:38) [10]
> Можно и добавить, но что делать с последней строкой, если > мы ждем четко определенного формата MULTI_SZ, а нам передали > какую-то фигню?
Что нам передали, то и считаем, дальше Limit не выйдем. Код конечно надо будет подправить чуток. Размер данных у нас же всегда есть и он корректный, т.к. определяется без оглядки на #0.
> Вариант с Insert и Delete как раз перемалывает данные просто > так.
согласен, не лучший вариант.
-
> Размер данных у нас же всегда есть
В том то и дело, что этот тип данных используется не только в реестре. И не всегда предоставляется длина блока. Таким образом, проверку надо делать вне процедуры, хотя, конечно L-версию написать не помешает.
|