xmlreg.pp 16 KB

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