xmlreg.pp 16 KB

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