xmlreg.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  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. Case DataType of
  312. dtDWORD : DataNode.NodeValue:=IntToStr(PCardinal(@Data)^);
  313. dtString : begin
  314. SetLength(S,DataSize);
  315. If (DataSize>0) then
  316. Move(Data,S[1],DataSize);
  317. DataNode.NodeValue:=S;
  318. end;
  319. dtBinary : begin
  320. S:=BufToHex(Data,DataSize);
  321. DataNode.NodeValue:=S;
  322. end;
  323. end;
  324. end;
  325. If Result then
  326. begin
  327. FDirty:=True;
  328. MaybeFlush;
  329. end;
  330. end;
  331. Function TXmlRegistry.FindSubKey (S : String; N : TDomElement) : TDomElement;
  332. Var
  333. Node : TDOMNode;
  334. begin
  335. Result:=Nil;
  336. If N<>Nil then
  337. begin
  338. Node:=N.FirstChild;
  339. While (Result=Nil) and (Assigned(Node)) do
  340. begin
  341. If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SKey) then
  342. If CompareText(TDomElement(Node)[SName],S)=0 then
  343. Result:=TDomElement(Node);
  344. Node:=Node.NextSibling;
  345. end;
  346. end;
  347. end;
  348. Function TXmlRegistry.CreateSubKey (S : String; N : TDomElement) : TDomElement;
  349. begin
  350. Result:=FDocument.CreateElement(SKey);
  351. Result[SName]:=S;
  352. if N<>nil then
  353. N.AppendChild(Result);
  354. FDirty:=True;
  355. end;
  356. Function TXmlRegistry.FindValueKey (S : String) : TDomElement;
  357. Var
  358. Node : TDOMNode;
  359. begin
  360. If FCurrentElement<>Nil then
  361. begin
  362. Node:=FCurrentElement.FirstChild;
  363. Result:=Nil;
  364. While (Result=Nil) and (Assigned(Node)) do
  365. begin
  366. If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SValue) then
  367. If CompareText(TDomElement(Node)[SName],S)=0 then
  368. Result:=TDomElement(Node);
  369. Node:=Node.NextSibling;
  370. end;
  371. end;
  372. end;
  373. Function TXmlRegistry.CreateValueKey (S : String) : TDomElement;
  374. begin
  375. If Assigned(FCurrentElement) then
  376. begin
  377. Result:=FDocument.CreateElement(SValue);
  378. Result[SName]:=S;
  379. // textnode to hold the value;
  380. Result.AppendChild(FDocument.CreateTextNode(''));
  381. FCurrentElement.AppendChild(Result);
  382. FDirty:=True;
  383. end
  384. else
  385. Result:=Nil;
  386. end;
  387. Procedure TXMLregistry.MaybeFlush;
  388. begin
  389. If FAutoFlush then
  390. Flush;
  391. end;
  392. Procedure TXmlRegistry.Flush;
  393. Var
  394. S : TStream;
  395. begin
  396. If FDirty then
  397. begin
  398. S:=TFileStream.Create(FFileName,fmCreate);
  399. Try
  400. WriteXMLFile(FDocument,S);
  401. FDirty:=False;
  402. finally
  403. S.Free;
  404. end;
  405. end;
  406. end;
  407. Procedure TXmlRegistry.Load;
  408. Var
  409. S : TStream;
  410. begin
  411. If Not FileExists(FFileName) then
  412. CreateEmptyDoc
  413. else
  414. begin
  415. S:=TFileStream.Create(FFileName,fmOpenReadWrite);
  416. try
  417. LoadFromStream(S);
  418. finally
  419. S.Free;
  420. end;
  421. end;
  422. end;
  423. Procedure TXmlRegistry.LoadFromStream(S : TStream);
  424. begin
  425. If Assigned(FDocument) then
  426. begin
  427. FDocument.Free;
  428. FDocument:=Nil;
  429. end;
  430. ReadXMLFile(FDocument,S);
  431. if (FDocument=Nil) then
  432. CreateEmptyDoc;
  433. SetRootKey('HKEY_CURRENT_USER');
  434. FDirty:=False;
  435. end;
  436. Function TXmlRegistry.BufToHex(Const Buf; Len : Integer) : String;
  437. Var
  438. P : PByte;
  439. S : String;
  440. I : Integer;
  441. begin
  442. SetLength(Result,Len*2);
  443. P:=@Buf;
  444. For I:=0 to Len-1 do
  445. begin
  446. S:=HexStr(P[I],2);
  447. Move(S[1],Result[I*2+1],2);
  448. end;
  449. end;
  450. Function TXMLRegistry.hexToBuf(Const Str : String; Var Buf; Var Len : Integer ) : Integer;
  451. Var
  452. I : Integer;
  453. P : PByte;
  454. S : String;
  455. B : Byte;
  456. Code : Integer;
  457. begin
  458. P:=@Buf;
  459. Len:= Length(Str) div 2;
  460. For I:=0 to Len-1 do
  461. begin
  462. S:='$'+Copy(Str,(I*2)+1,2);
  463. Val(S,B,Code);
  464. If Code<>0 then
  465. begin
  466. Inc(Result);
  467. B:=0;
  468. end;
  469. P[I]:=B;
  470. end;
  471. end;
  472. Function TXMLRegistry.DeleteValue(S : String) : Boolean;
  473. Var
  474. N : TDomElement;
  475. begin
  476. N:=FindValueKey(S);
  477. Result:=(N<>Nil);
  478. If Result then
  479. begin
  480. FCurrentElement.RemoveChild(N);
  481. FDirty:=True;
  482. MaybeFlush;
  483. end;
  484. end;
  485. Function TXMLRegistry.GetValueSize(Name : String) : Integer;
  486. Var
  487. Info : TDataInfo;
  488. begin
  489. If GetValueInfo(Name,Info) then
  490. Result:=Info.DataSize
  491. else
  492. Result:=-1;
  493. end;
  494. Function TXMLRegistry.GetValueType(Name : String) : TDataType;
  495. Var
  496. Info : TDataInfo;
  497. begin
  498. If GetValueInfo(Name,Info) then
  499. Result:=Info.DataType
  500. else
  501. Result:=dtUnknown;
  502. end;
  503. Function TXMLRegistry.GetValueInfo(Name : String; Var Info : TDataInfo) : Boolean;
  504. Var
  505. N : TDomElement;
  506. DN : TDomNode;
  507. begin
  508. N:=FindValueKey(Name);
  509. Result:=(N<>Nil);
  510. If Result then
  511. begin
  512. DN:=N.FirstChild;
  513. Result:=DN<>Nil;
  514. If Result then
  515. With Info do
  516. begin
  517. DataType:=TDataType(StrToIntDef(N[SType],0));
  518. Case DataType of
  519. dtUnknown : DataSize:=0;
  520. dtDword : Datasize:=SizeOf(Cardinal);
  521. dtString : DataSize:=Length(DN.NodeValue);
  522. dtBinary : DataSize:=Length(DN.NodeValue) div 2;
  523. end;
  524. end;
  525. end;
  526. end;
  527. Function TXMLRegistry.GetKeyInfo(Var Info : TKeyInfo) : Boolean;
  528. Var
  529. Node,DataNode : TDOMNode;
  530. L : Integer;
  531. begin
  532. FillChar(Info,SizeOf(Info),0);
  533. Result:=FCurrentElement<>Nil;
  534. If Result then
  535. With Info do
  536. begin
  537. If (FFileName<>'') Then
  538. FTime:=FileAge(FFileName);
  539. Node:=FCurrentElement.FirstChild;
  540. While Assigned(Node) do
  541. begin
  542. If (Node.NodeType=ELEMENT_NODE) then
  543. If (Node.NodeName=SKey) then
  544. begin
  545. Inc(SubKeys);
  546. L:=Length(TDomElement(Node)[SName]);
  547. If (L>SubKeyLen) then
  548. SubKeyLen:=L;
  549. end
  550. else if (Node.NodeName=SValue) then
  551. begin
  552. Inc(Values);
  553. L:=Length(TDomElement(Node)[SName]);
  554. If (L>ValueLen) then
  555. ValueLen:=L;
  556. DataNode:=TDomElement(Node).FirstChild;
  557. If (DataNode<>Nil) and (DataNode is TDomText) then
  558. Case TDataType(StrToIntDef(TDomElement(Node)[SType],0)) of
  559. dtUnknown : L:=0;
  560. dtDWord : L:=4;
  561. DtString : L:=Length(DataNode.NodeValue);
  562. dtBinary : L:=Length(DataNode.NodeValue) div 2;
  563. end
  564. else
  565. L:=0;
  566. If (L>DataLen) Then
  567. DataLen:=L;
  568. end;
  569. Node:=Node.NextSibling;
  570. end;
  571. end;
  572. end;
  573. Function TXMLRegistry.EnumSubKeys(List : TStrings) : Integer;
  574. Var
  575. Node : TDOMNode;
  576. begin
  577. List.Clear;
  578. Result:=0;
  579. If FCurrentElement<>Nil then
  580. begin
  581. Node:=FCurrentElement.FirstChild;
  582. While Assigned(Node) do
  583. begin
  584. If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SKey) then
  585. List.Add(TDomElement(Node)[SName]);
  586. Node:=Node.NextSibling;
  587. end;
  588. Result:=List.Count;
  589. end;
  590. end;
  591. Function TXMLRegistry.EnumValues(List : TStrings) : Integer;
  592. Var
  593. Node : TDOMNode;
  594. begin
  595. List.Clear;
  596. Result:=0;
  597. If FCurrentElement<>Nil then
  598. begin
  599. Node:=FCurrentElement.FirstChild;
  600. While Assigned(Node) do
  601. begin
  602. If (Node.NodeType=ELEMENT_NODE) and (Node.NodeName=SValue) then
  603. List.Add(TDomElement(Node)[SName]);
  604. Node:=Node.NextSibling;
  605. end;
  606. Result:=List.Count;
  607. end;
  608. end;
  609. Function TXMLRegistry.KeyExists(KeyPath : String) : Boolean;
  610. begin
  611. Result:=FindKey(KeyPath)<>Nil;
  612. end;
  613. Function TXMLRegistry.RenameValue(Const OldName,NewName : String) : Boolean;
  614. Var
  615. N : TDomElement;
  616. begin
  617. N:=FindValueKey(OldName);
  618. If (N<>Nil) then
  619. begin
  620. N[SName]:=NewName;
  621. FDirty:=True;
  622. MaybeFlush;
  623. end;
  624. end;
  625. Function TXMLRegistry.FindKey (S : String) : TDomElement;
  626. Var
  627. SubKey : String;
  628. P : Integer;
  629. Node : TDomElement;
  630. begin
  631. Result:=Nil;
  632. If (Length(S)=0) then
  633. Exit;
  634. S:=NormalizeKey(S);
  635. If (FCurrentElement<>nil) then
  636. begin
  637. Delete(S,1,1);
  638. Node:=FCurrentElement;
  639. end
  640. else
  641. begin
  642. Delete(S,1,1);
  643. Node:=FDocument.DocumentElement;
  644. If (FRootKey<>'') then
  645. S:=FRootKey+S;
  646. end;
  647. repeat
  648. P:=Pos('/',S);
  649. If (P<>0) then
  650. begin
  651. SubKey:=Copy(S,1,P-1);
  652. Delete(S,1,P);
  653. Result:=FindSubKey(SubKey,Node);
  654. Node:=Result;
  655. end;
  656. Until (Result=Nil) or (Length(S)=0);
  657. end;
  658. Function TXmlRegistry.ValueExists(ValueName : String) : Boolean;
  659. begin
  660. Result:=FindValueKey(ValueName)<>Nil;
  661. end;
  662. end.