xmlreg.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  1. {$mode objfpc}
  2. {$h+}
  3. unit xmlreg;
  4. Interface
  5. uses
  6. sysutils,classes,dom,xmlread,xmlwrite;
  7. Type
  8. TDataType = (dtUnknown,dtDWORD,dtString,dtBinary);
  9. TDataInfo = record
  10. DataType : TDataType;
  11. DataSize : Integer;
  12. end;
  13. TKeyInfo = record
  14. SubKeys,
  15. SubKeyLen,
  16. Values,
  17. ValueLen,
  18. DataLen : Integer;
  19. FTime : TDateTime;
  20. end;
  21. { TXmlRegistry }
  22. TXmlRegistry = Class(TObject)
  23. Private
  24. FAutoFlush,
  25. FDirty : Boolean;
  26. FFileName : String;
  27. FRootKey : String;
  28. FDocument : TXMLDocument;
  29. FCurrentElement : TDomElement;
  30. FCurrentKey : String;
  31. Procedure SetFileName(Value : String);
  32. Protected
  33. Procedure LoadFromStream(S : TStream);
  34. Function NormalizeKey(KeyPath : String) : String;
  35. Procedure CreateEmptyDoc;
  36. Function FindKey (S : String) : TDomElement;
  37. Function FindSubKey (S : String; N : TDomElement) : TDomElement;
  38. Function CreateSubKey (S : String; N : TDomElement) : TDomElement;
  39. Function FindValueKey (S : String) : TDomElement;
  40. Function CreateValueKey (S : String) : TDomElement;
  41. Function BufToHex(Const Buf; Len : Integer) : String;
  42. Function hexToBuf(Const Str : String; Var Buf; Var Len : Integer ) : Integer;
  43. Procedure MaybeFlush;
  44. Property Document : TXMLDocument Read FDocument;
  45. Property Dirty : Boolean Read FDirty write FDirty;
  46. Public
  47. Constructor Create(AFileName : String);
  48. Destructor Destroy;override;
  49. Function SetKey(KeyPath : String; AllowCreate : Boolean) : Boolean ;
  50. Procedure SetRootKey(Value : String);
  51. Function DeleteKey(KeyPath : String) : Boolean;
  52. Function CreateKey(KeyPath : String) : Boolean;
  53. Function GetValueSize(Name : String) : Integer;
  54. Function GetValueType(Name : String) : TDataType;
  55. Function GetValueInfo(Name : String; Var Info : TDataInfo) : Boolean;
  56. Function GetKeyInfo(Var Info : TKeyInfo) : Boolean;
  57. Function EnumSubKeys(List : TStrings) : Integer;
  58. Function EnumValues(List : TStrings) : Integer;
  59. Function KeyExists(KeyPath : String) : Boolean;
  60. Function ValueExists(ValueName : String) : Boolean;
  61. Function RenameValue(Const OldName,NewName : String) : Boolean;
  62. Function DeleteValue(S : String) : Boolean;
  63. Procedure Flush;
  64. Procedure Load;
  65. Function GetValueData(Name : String; Var DataType : TDataType; Var Data; Var DataSize : Integer) : Boolean;
  66. Function SetValueData(Name : String; DataType : TDataType; Const Data; DataSize : Integer) : Boolean;
  67. Property FileName : String Read FFileName Write SetFileName;
  68. Property RootKey : String Read FRootKey Write SetRootkey;
  69. Property AutoFlush : Boolean Read FAutoFlush Write FAutoFlush;
  70. end;
  71. // used Key types
  72. Const
  73. SXmlReg = 'XMLReg';
  74. SKey = 'Key';
  75. SValue = 'Value';
  76. SName = 'Name';
  77. SType = 'Type';
  78. SData = 'Data';
  79. Implementation
  80. Constructor TXmlRegistry.Create(AFileName : String);
  81. begin
  82. FFileName:=AFileName;
  83. FautoFlush:=True;
  84. If (AFileName<>'') then
  85. Load
  86. else
  87. CreateEmptyDoc;
  88. end;
  89. destructor TXmlRegistry.Destroy;
  90. begin
  91. if Assigned(FDocument) then FDocument.Free;
  92. inherited Destroy;
  93. end;
  94. Procedure TXmlRegistry.SetFileName(Value : String);
  95. begin
  96. If Value<>FFileName then
  97. begin
  98. FFilename:=Value;
  99. Flush;
  100. end;
  101. end;
  102. Procedure TXmlRegistry.CreateEmptyDoc;
  103. Const
  104. template = '<?xml version="1.0" encoding="ISO8859-1"?>'+LineEnding+
  105. '<'+SXMLReg+'>'+LineEnding+
  106. '</'+SXMLReg+'>'+LineEnding;
  107. Var
  108. S : TStream;
  109. begin
  110. S:=TStringStream.Create(Template);
  111. S.Seek(0,soFromBeginning);
  112. Try
  113. LoadFromStream(S);
  114. Finally
  115. S.Free;
  116. end;
  117. end;
  118. Function TXmlRegistry.NormalizeKey(KeyPath : String) : String;
  119. Var
  120. L : Integer;
  121. begin
  122. Result:=StringReplace(KeyPath,'\','/',[rfReplaceAll]);
  123. L:=Length(Result);
  124. If (L>0) and (Result[L]<>'/') then
  125. Result:=Result+'/';
  126. If (L>0) and (Result[1]<>'/') then
  127. Result:='/' + Result;
  128. end;
  129. Function TXmlRegistry.SetKey(KeyPath : String; AllowCreate : Boolean) : boolean;
  130. Var
  131. SubKey,ResultKey : String;
  132. P : Integer;
  133. Node,Node2 : TDomElement;
  134. begin
  135. Result:=(Length(KeyPath)>0);
  136. If Not Result then
  137. Exit;
  138. KeyPath:=NormalizeKey(KeyPath);
  139. If (FCurrentElement<>nil) then
  140. begin
  141. Delete(Keypath,1,1);
  142. Node:=FCurrentElement;
  143. Resultkey:=FCurrentKey;
  144. end
  145. else
  146. begin
  147. Delete(Keypath,1,1);
  148. Node:=FDocument.DocumentElement;
  149. If (FRootKey<>'') then
  150. KeyPath:=FRootKey+KeyPath;
  151. ResultKey:='';
  152. end;
  153. Result:=True;
  154. repeat
  155. P:=Pos('/',KeyPath);
  156. If (P<>0) then
  157. begin
  158. SubKey:=Copy(KeyPath,1,P-1);
  159. Delete(KeyPath,1,P);
  160. Node2:=FindSubKey(SubKey,Node);
  161. Result:=(Node2<>Nil);
  162. If Result then
  163. Node:=Node2
  164. else
  165. begin
  166. If AllowCreate then
  167. Begin
  168. Node2:=CreateSubKey(SubKey,Node);
  169. Result:=Node2<>Nil;
  170. If Result Then
  171. Node:=Node2;
  172. end;
  173. end;
  174. If Result then
  175. ResultKey:=ResultKey+SubKey+'/';
  176. end;
  177. Until (Not Result) or (Length(KeyPath)=0);
  178. If Result then
  179. begin
  180. FCurrentkey:=ResultKey;
  181. FCurrentElement:=Node;
  182. end;
  183. MaybeFlush;
  184. end;
  185. Procedure TXmlRegistry.SetRootKey(Value : String);
  186. begin
  187. FRootKey:=NormalizeKey(Value);
  188. If (Length(FRootKey)>1) and (FRootKey[1]='/') then
  189. Delete(FRootKey,1,1);
  190. FCurrentKey:='';
  191. FCurrentElement:=Nil;
  192. end;
  193. Function TXmlRegistry.DeleteKey(KeyPath : String) : Boolean;
  194. Var
  195. N : TDomElement;
  196. begin
  197. N:=FindKey(KeyPath);
  198. Result:=(N<>Nil);
  199. If Result then
  200. begin
  201. (N.ParentNode as TDomElement).RemoveChild(N);
  202. FDirty:=True;
  203. MaybeFlush;
  204. end;
  205. end;
  206. Function TXmlRegistry.CreateKey(KeyPath : String) : Boolean;
  207. Var
  208. SubKey : String;
  209. P : Integer;
  210. Node,Node2 : TDomElement;
  211. begin
  212. Result:=(Length(KeyPath)>0);
  213. If Not Result then
  214. Exit;
  215. KeyPath:=NormalizeKey(KeyPath);
  216. If (FCurrentElement<>nil) then
  217. begin
  218. Delete(Keypath,1,1);
  219. Node:=FCurrentElement;
  220. end
  221. else
  222. begin
  223. Delete(Keypath,1,1);
  224. Node:=FDocument.DocumentElement;
  225. If (FRootKey<>'') then
  226. KeyPath:=FRootKey+KeyPath;
  227. end;
  228. Result:=True;
  229. repeat
  230. P:=Pos('/',KeyPath);
  231. If (P<>0) then
  232. begin
  233. SubKey:=Copy(KeyPath,1,P-1);
  234. Delete(KeyPath,1,P);
  235. Node2:=FindSubKey(SubKey,Node);
  236. Result:=(Node2<>Nil);
  237. If Result then
  238. Node:=Node2
  239. else
  240. begin
  241. Node2:=CreateSubKey(SubKey,Node);
  242. Result:=Node2<>Nil;
  243. Node:=Node2
  244. end;
  245. end;
  246. Until (Not Result) or (Length(KeyPath)=0);
  247. MaybeFlush;
  248. end;
  249. Function TXmlRegistry.GetValueData(Name : String; Var DataType : TDataType; Var Data; Var DataSize : Integer) : Boolean;
  250. Type
  251. PCardinal = ^Cardinal;
  252. Var
  253. Node : TDomElement;
  254. DataNode : TDomNode;
  255. ND : Integer;
  256. Dt : TDataType;
  257. S : AnsiString;
  258. begin
  259. Node:=FindValueKey(Name);
  260. Result:=Node<>Nil;
  261. If Result then
  262. begin
  263. DataNode:=Node.FirstChild;
  264. Result:=(DataNode<>Nil) and (DataNode is TDomText);
  265. If Result then
  266. begin
  267. ND:=StrToIntDef(Node[Stype],0);
  268. Result:=ND<=Ord(High(TDataType));
  269. If Result then
  270. begin
  271. DataType:=TDataType(StrToIntDef(Node[Stype],0));
  272. Case DataType of
  273. dtDWORD : begin
  274. PCardinal(@Data)^:=StrToIntDef(DataNode.NodeValue,0);
  275. DataSize:=SizeOf(Cardinal);
  276. end;
  277. dtString : begin
  278. S:=DataNode.NodeValue; // Convert to ansistring
  279. DataSize:=Length(S);
  280. If (DataSize>0) then
  281. Move(S[1],Data,DataSize);
  282. end;
  283. dtBinary : begin
  284. DataSize:=Length(DataNode.NodeValue);
  285. If (DataSize>0) then
  286. HexToBuf(DataNode.NodeValue,Data,DataSize);
  287. end;
  288. end;
  289. end;
  290. end;
  291. end;
  292. end;
  293. Function TXmlRegistry.SetValueData(Name : String; DataType : TDataType; Const Data; DataSize : Integer) : Boolean;
  294. Type
  295. PCardinal = ^Cardinal;
  296. Var
  297. Node : TDomElement;
  298. DataNode : TDomNode;
  299. ND : Integer;
  300. Dt : TDataType;
  301. S : String;
  302. begin
  303. Node:=FindValueKey(Name);
  304. If Node=Nil then
  305. Node:=CreateValueKey(Name);
  306. Result:=(Node<>Nil);
  307. If Result then
  308. begin
  309. Node[SType]:=IntToStr(Ord(DataType));
  310. DataNode:=Node.FirstChild;
  311. Result:=DataNode<>Nil; // Bug 9879. Create child here?
  312. If Result Then
  313. begin
  314. Case DataType of
  315. dtDWORD : DataNode.NodeValue:=IntToStr(PCardinal(@Data)^);
  316. dtString : begin
  317. SetLength(S,DataSize);
  318. If (DataSize>0) then
  319. Move(Data,S[1],DataSize);
  320. DataNode.NodeValue:=S;
  321. end;
  322. dtBinary : begin
  323. S:=BufToHex(Data,DataSize);
  324. DataNode.NodeValue:=S;
  325. end;
  326. end;
  327. end;
  328. end;
  329. If Result then
  330. begin
  331. FDirty:=True;
  332. MaybeFlush;
  333. end;
  334. end;
  335. Function TXmlRegistry.FindSubKey (S : String; N : TDomElement) : TDomElement;
  336. Var
  337. Node : TDOMNode;
  338. begin
  339. Result:=Nil;
  340. If N<>Nil then
  341. begin
  342. Node:=N.FirstChild;
  343. While (Result=Nil) and (Assigned(Node)) do
  344. begin
  345. If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SKey) then
  346. If CompareText(TDomElement(Node)[SName],S)=0 then
  347. Result:=TDomElement(Node);
  348. Node:=Node.NextSibling;
  349. end;
  350. end;
  351. end;
  352. Function TXmlRegistry.CreateSubKey (S : String; N : TDomElement) : TDomElement;
  353. begin
  354. Result:=FDocument.CreateElement(SKey);
  355. Result[SName]:=S;
  356. if N<>nil then
  357. N.AppendChild(Result);
  358. FDirty:=True;
  359. end;
  360. Function TXmlRegistry.FindValueKey (S : String) : TDomElement;
  361. Var
  362. Node : TDOMNode;
  363. begin
  364. If FCurrentElement<>Nil then
  365. begin
  366. Node:=FCurrentElement.FirstChild;
  367. Result:=Nil;
  368. While (Result=Nil) and (Assigned(Node)) do
  369. begin
  370. If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SValue) then
  371. If CompareText(TDomElement(Node)[SName],S)=0 then
  372. Result:=TDomElement(Node);
  373. Node:=Node.NextSibling;
  374. end;
  375. end;
  376. end;
  377. Function TXmlRegistry.CreateValueKey (S : String) : TDomElement;
  378. begin
  379. If Assigned(FCurrentElement) then
  380. begin
  381. Result:=FDocument.CreateElement(SValue);
  382. Result[SName]:=S;
  383. // textnode to hold the value;
  384. Result.AppendChild(FDocument.CreateTextNode(''));
  385. FCurrentElement.AppendChild(Result);
  386. FDirty:=True;
  387. end
  388. else
  389. Result:=Nil;
  390. end;
  391. Procedure TXMLregistry.MaybeFlush;
  392. begin
  393. If FAutoFlush then
  394. Flush;
  395. end;
  396. Procedure TXmlRegistry.Flush;
  397. Var
  398. S : TStream;
  399. begin
  400. If FDirty then
  401. begin
  402. S:=TFileStream.Create(FFileName,fmCreate);
  403. Try
  404. WriteXMLFile(FDocument,S);
  405. FDirty:=False;
  406. finally
  407. S.Free;
  408. end;
  409. end;
  410. end;
  411. Procedure TXmlRegistry.Load;
  412. Var
  413. S : TStream;
  414. begin
  415. If Not FileExists(FFileName) then
  416. CreateEmptyDoc
  417. else
  418. begin
  419. S:=TFileStream.Create(FFileName,fmOpenReadWrite);
  420. try
  421. LoadFromStream(S);
  422. finally
  423. S.Free;
  424. end;
  425. end;
  426. end;
  427. Procedure TXmlRegistry.LoadFromStream(S : TStream);
  428. begin
  429. If Assigned(FDocument) then
  430. begin
  431. FDocument.Free;
  432. FDocument:=Nil;
  433. end;
  434. ReadXMLFile(FDocument,S);
  435. if (FDocument=Nil) then
  436. CreateEmptyDoc;
  437. SetRootKey('HKEY_CURRENT_USER');
  438. FDirty:=False;
  439. end;
  440. Function TXmlRegistry.BufToHex(Const Buf; Len : Integer) : String;
  441. Var
  442. P : PByte;
  443. S : String;
  444. I : Integer;
  445. begin
  446. SetLength(Result,Len*2);
  447. P:=@Buf;
  448. For I:=0 to Len-1 do
  449. begin
  450. S:=HexStr(P[I],2);
  451. Move(S[1],Result[I*2+1],2);
  452. end;
  453. end;
  454. Function TXMLRegistry.hexToBuf(Const Str : String; Var Buf; Var Len : Integer ) : Integer;
  455. Var
  456. I : Integer;
  457. P : PByte;
  458. S : String;
  459. B : Byte;
  460. Code : Integer;
  461. begin
  462. P:=@Buf;
  463. Len:= Length(Str) div 2;
  464. For I:=0 to Len-1 do
  465. begin
  466. S:='$'+Copy(Str,(I*2)+1,2);
  467. Val(S,B,Code);
  468. If Code<>0 then
  469. begin
  470. Inc(Result);
  471. B:=0;
  472. end;
  473. P[I]:=B;
  474. end;
  475. end;
  476. Function TXMLRegistry.DeleteValue(S : String) : Boolean;
  477. Var
  478. N : TDomElement;
  479. begin
  480. N:=FindValueKey(S);
  481. Result:=(N<>Nil);
  482. If Result then
  483. begin
  484. FCurrentElement.RemoveChild(N);
  485. FDirty:=True;
  486. MaybeFlush;
  487. end;
  488. end;
  489. Function TXMLRegistry.GetValueSize(Name : String) : Integer;
  490. Var
  491. Info : TDataInfo;
  492. begin
  493. If GetValueInfo(Name,Info) then
  494. Result:=Info.DataSize
  495. else
  496. Result:=-1;
  497. end;
  498. Function TXMLRegistry.GetValueType(Name : String) : TDataType;
  499. Var
  500. Info : TDataInfo;
  501. begin
  502. If GetValueInfo(Name,Info) then
  503. Result:=Info.DataType
  504. else
  505. Result:=dtUnknown;
  506. end;
  507. Function TXMLRegistry.GetValueInfo(Name : String; Var Info : TDataInfo) : Boolean;
  508. Var
  509. N : TDomElement;
  510. DN : TDomNode;
  511. begin
  512. N:=FindValueKey(Name);
  513. Result:=(N<>Nil);
  514. If Result then
  515. begin
  516. DN:=N.FirstChild;
  517. Result:=DN<>Nil;
  518. If Result then
  519. With Info do
  520. begin
  521. DataType:=TDataType(StrToIntDef(N[SType],0));
  522. Case DataType of
  523. dtUnknown : DataSize:=0;
  524. dtDword : Datasize:=SizeOf(Cardinal);
  525. dtString : DataSize:=Length(DN.NodeValue);
  526. dtBinary : DataSize:=Length(DN.NodeValue) div 2;
  527. end;
  528. end;
  529. end;
  530. end;
  531. Function TXMLRegistry.GetKeyInfo(Var Info : TKeyInfo) : Boolean;
  532. Var
  533. Node,DataNode : TDOMNode;
  534. L : Integer;
  535. begin
  536. FillChar(Info,SizeOf(Info),0);
  537. Result:=FCurrentElement<>Nil;
  538. If Result then
  539. With Info do
  540. begin
  541. If (FFileName<>'') Then
  542. FTime:=FileAge(FFileName);
  543. Node:=FCurrentElement.FirstChild;
  544. While Assigned(Node) do
  545. begin
  546. If (Node.NodeType=ELEMENT_NODE) then
  547. If (Node.NodeName=SKey) then
  548. begin
  549. Inc(SubKeys);
  550. L:=Length(TDomElement(Node)[SName]);
  551. If (L>SubKeyLen) then
  552. SubKeyLen:=L;
  553. end
  554. else if (Node.NodeName=SValue) then
  555. begin
  556. Inc(Values);
  557. L:=Length(TDomElement(Node)[SName]);
  558. If (L>ValueLen) then
  559. ValueLen:=L;
  560. DataNode:=TDomElement(Node).FirstChild;
  561. If (DataNode<>Nil) and (DataNode is TDomText) then
  562. Case TDataType(StrToIntDef(TDomElement(Node)[SType],0)) of
  563. dtUnknown : L:=0;
  564. dtDWord : L:=4;
  565. DtString : L:=Length(DataNode.NodeValue);
  566. dtBinary : L:=Length(DataNode.NodeValue) div 2;
  567. end
  568. else
  569. L:=0;
  570. If (L>DataLen) Then
  571. DataLen:=L;
  572. end;
  573. Node:=Node.NextSibling;
  574. end;
  575. end;
  576. end;
  577. Function TXMLRegistry.EnumSubKeys(List : TStrings) : Integer;
  578. Var
  579. Node : TDOMNode;
  580. begin
  581. List.Clear;
  582. Result:=0;
  583. If FCurrentElement<>Nil then
  584. begin
  585. Node:=FCurrentElement.FirstChild;
  586. While Assigned(Node) do
  587. begin
  588. If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SKey) then
  589. List.Add(TDomElement(Node)[SName]);
  590. Node:=Node.NextSibling;
  591. end;
  592. Result:=List.Count;
  593. end;
  594. end;
  595. Function TXMLRegistry.EnumValues(List : TStrings) : Integer;
  596. Var
  597. Node : TDOMNode;
  598. begin
  599. List.Clear;
  600. Result:=0;
  601. If FCurrentElement<>Nil then
  602. begin
  603. Node:=FCurrentElement.FirstChild;
  604. While Assigned(Node) do
  605. begin
  606. If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SValue) then
  607. List.Add(TDomElement(Node)[SName]);
  608. Node:=Node.NextSibling;
  609. end;
  610. Result:=List.Count;
  611. end;
  612. end;
  613. Function TXMLRegistry.KeyExists(KeyPath : String) : Boolean;
  614. begin
  615. Result:=FindKey(KeyPath)<>Nil;
  616. end;
  617. Function TXMLRegistry.RenameValue(Const OldName,NewName : String) : Boolean;
  618. Var
  619. N : TDomElement;
  620. begin
  621. N:=FindValueKey(OldName);
  622. result:=n<>nil;
  623. If (Result) then
  624. begin
  625. N[SName]:=NewName;
  626. FDirty:=True;
  627. MaybeFlush;
  628. end;
  629. end;
  630. Function TXMLRegistry.FindKey (S : String) : TDomElement;
  631. Var
  632. SubKey : String;
  633. P : Integer;
  634. Node : TDomElement;
  635. begin
  636. Result:=Nil;
  637. If (Length(S)=0) then
  638. Exit;
  639. S:=NormalizeKey(S);
  640. If (FCurrentElement<>nil) then
  641. begin
  642. Delete(S,1,1);
  643. Node:=FCurrentElement;
  644. end
  645. else
  646. begin
  647. Delete(S,1,1);
  648. Node:=FDocument.DocumentElement;
  649. If (FRootKey<>'') then
  650. S:=FRootKey+S;
  651. end;
  652. repeat
  653. P:=Pos('/',S);
  654. If (P<>0) then
  655. begin
  656. SubKey:=Copy(S,1,P-1);
  657. Delete(S,1,P);
  658. Result:=FindSubKey(SubKey,Node);
  659. Node:=Result;
  660. end;
  661. Until (Result=Nil) or (Length(S)=0);
  662. end;
  663. Function TXmlRegistry.ValueExists(ValueName : String) : Boolean;
  664. begin
  665. Result:=FindValueKey(ValueName)<>Nil;
  666. end;
  667. end.