sysutils.pp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2010 by Sven Barth
  4. member of the Free Pascal development team
  5. Sysutils unit for NativeNT
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit sysutils;
  13. interface
  14. {$MODE objfpc}
  15. {$MODESWITCH OUT}
  16. { force ansistrings }
  17. {$H+}
  18. uses
  19. ndk;
  20. {$DEFINE HAS_SLEEP}
  21. {$DEFINE HAS_CREATEGUID}
  22. type
  23. TNativeNTFindData = record
  24. SearchSpec: UnicodeString;
  25. NamePos: LongInt;
  26. Handle: THandle;
  27. IsDirObj: Boolean;
  28. SearchAttr: LongInt;
  29. Context: ULONG;
  30. LastRes: NTSTATUS;
  31. end;
  32. { used OS file system APIs use ansistring }
  33. {$define SYSUTILS_HAS_UNICODESTR_FILEUTIL_IMPL}
  34. { OS has an ansistring/single byte environment variable API (actually it's
  35. unicodestring, but that's not yet implemented) }
  36. {$define SYSUTILS_HAS_ANSISTR_ENVVAR_IMPL}
  37. { Include platform independent interface part }
  38. {$i sysutilh.inc}
  39. implementation
  40. uses
  41. sysconst, ndkutils;
  42. {$DEFINE FPC_NOGENERICANSIROUTINES}
  43. { Include platform independent implementation part }
  44. {$i sysutils.inc}
  45. {****************************************************************************
  46. File Functions
  47. ****************************************************************************}
  48. function FileOpen(const FileName : UnicodeString; Mode : Integer) : THandle;
  49. const
  50. AccessMode: array[0..2] of ACCESS_MASK = (
  51. GENERIC_READ,
  52. GENERIC_WRITE,
  53. GENERIC_READ or GENERIC_WRITE);
  54. ShareMode: array[0..4] of ULONG = (
  55. 0,
  56. 0,
  57. FILE_SHARE_READ,
  58. FILE_SHARE_WRITE,
  59. FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE);
  60. var
  61. ntstr: UNICODE_STRING;
  62. objattr: OBJECT_ATTRIBUTES;
  63. iostatus: IO_STATUS_BLOCK;
  64. begin
  65. UnicodeStrToNtStr(FileName, ntstr);
  66. InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
  67. NtCreateFile(@Result, AccessMode[Mode and 3] or NT_SYNCHRONIZE, @objattr,
  68. @iostatus, Nil, FILE_ATTRIBUTE_NORMAL, ShareMode[(Mode and $F0) shr 4],
  69. FILE_OPEN, FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil, 0);
  70. FreeNtStr(ntstr);
  71. end;
  72. function FileCreate(const FileName : UnicodeString) : THandle;
  73. begin
  74. FileCreate := FileCreate(FileName, fmShareDenyNone, 0);
  75. end;
  76. function FileCreate(const FileName : UnicodeString; Rights: longint) : THandle;
  77. begin
  78. FileCreate := FileCreate(FileName, fmShareDenyNone, Rights);
  79. end;
  80. function FileCreate(const FileName : UnicodeString; ShareMode : longint; Rights: longint) : THandle;
  81. const
  82. ShareModeFlags: array[0..4] of ULONG = (
  83. 0,
  84. 0,
  85. FILE_SHARE_READ,
  86. FILE_SHARE_WRITE,
  87. FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE);
  88. var
  89. ntstr: UNICODE_STRING;
  90. objattr: OBJECT_ATTRIBUTES;
  91. iostatus: IO_STATUS_BLOCK;
  92. res: NTSTATUS;
  93. begin
  94. UnicodeStrToNtStr(FileName, ntstr);
  95. InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
  96. NtCreateFile(@Result, GENERIC_READ or GENERIC_WRITE or NT_SYNCHRONIZE,
  97. @objattr, @iostatus, Nil, FILE_ATTRIBUTE_NORMAL,
  98. ShareModeFlags[(ShareMode and $F0) shr 4], FILE_OVERWRITE_IF,
  99. FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil, 0);
  100. FreeNtStr(ntstr);
  101. end;
  102. function FileRead(Handle : THandle; out Buffer; Count : longint) : Longint;
  103. var
  104. iostatus: IO_STATUS_BLOCK;
  105. res: NTSTATUS;
  106. begin
  107. res := NtReadFile(Handle, 0, Nil, Nil, @iostatus, @Buffer, Count, Nil, Nil);
  108. if res = STATUS_PENDING then begin
  109. res := NtWaitForSingleObject(Handle, False, Nil);
  110. if NT_SUCCESS(res) then
  111. res := iostatus.union1.Status;
  112. end;
  113. if NT_SUCCESS(res) then
  114. Result := LongInt(iostatus.Information)
  115. else
  116. Result := -1;
  117. end;
  118. function FileWrite(Handle : THandle; const Buffer; Count : Longint) : Longint;
  119. var
  120. iostatus: IO_STATUS_BLOCK;
  121. res: NTSTATUS;
  122. begin
  123. res := NtWriteFile(Handle, 0, Nil, Nil, @iostatus, @Buffer, Count, Nil,
  124. Nil);
  125. if res = STATUS_PENDING then begin
  126. res := NtWaitForSingleObject(Handle, False, Nil);
  127. if NT_SUCCESS(res) then
  128. res := iostatus.union1.Status;
  129. end;
  130. if NT_SUCCESS(res) then
  131. Result := LongInt(iostatus.Information)
  132. else
  133. Result := -1;
  134. end;
  135. function FileSeek(Handle : THandle;FOffset,Origin : Longint) : Longint;
  136. begin
  137. Result := longint(FileSeek(Handle, Int64(FOffset), Origin));
  138. end;
  139. function FileSeek(Handle : THandle; FOffset: Int64; Origin: Longint) : Int64;
  140. const
  141. ErrorCode = $FFFFFFFFFFFFFFFF;
  142. var
  143. position: FILE_POSITION_INFORMATION;
  144. standard: FILE_STANDARD_INFORMATION;
  145. iostatus: IO_STATUS_BLOCK;
  146. res: NTSTATUS;
  147. begin
  148. { determine the new position }
  149. case Origin of
  150. fsFromBeginning:
  151. position.CurrentByteOffset.QuadPart := FOffset;
  152. fsFromCurrent: begin
  153. res := NtQueryInformationFile(Handle, @iostatus, @position,
  154. SizeOf(FILE_POSITION_INFORMATION), FilePositionInformation);
  155. if res < 0 then begin
  156. Result := ErrorCode;
  157. Exit;
  158. end;
  159. position.CurrentByteOffset.QuadPart :=
  160. position.CurrentByteOffset.QuadPart + FOffset;
  161. end;
  162. fsFromEnd: begin
  163. res := NtQueryInformationFile(Handle, @iostatus, @standard,
  164. SizeOf(FILE_STANDARD_INFORMATION), FileStandardInformation);
  165. if res < 0 then begin
  166. Result := ErrorCode;
  167. Exit;
  168. end;
  169. position.CurrentByteOffset.QuadPart := standard.EndOfFile.QuadPart +
  170. FOffset;
  171. end;
  172. else begin
  173. Result := ErrorCode;
  174. Exit;
  175. end;
  176. end;
  177. { set the new position }
  178. res := NtSetInformationFile(Handle, @iostatus, @position,
  179. SizeOf(FILE_POSITION_INFORMATION), FilePositionInformation);
  180. if res < 0 then
  181. Result := ErrorCode
  182. else
  183. Result := position.CurrentByteOffset.QuadPart;
  184. end;
  185. procedure FileClose(Handle : THandle);
  186. begin
  187. NtClose(Handle);
  188. end;
  189. function FileTruncate(Handle : THandle;Size: Int64) : boolean;
  190. var
  191. endoffileinfo: FILE_END_OF_FILE_INFORMATION;
  192. allocinfo: FILE_ALLOCATION_INFORMATION;
  193. iostatus: IO_STATUS_BLOCK;
  194. res: NTSTATUS;
  195. begin
  196. // based on ReactOS' SetEndOfFile
  197. endoffileinfo.EndOfFile.QuadPart := Size;
  198. res := NtSetInformationFile(Handle, @iostatus, @endoffileinfo,
  199. SizeOf(FILE_END_OF_FILE_INFORMATION), FileEndOfFileInformation);
  200. if NT_SUCCESS(res) then begin
  201. allocinfo.AllocationSize.QuadPart := Size;
  202. res := NtSetInformationFile(handle, @iostatus, @allocinfo,
  203. SizeOf(FILE_ALLOCATION_INFORMATION), FileAllocationInformation);
  204. Result := NT_SUCCESS(res);
  205. end else
  206. Result := False;
  207. end;
  208. function NTToDosTime(const NtTime: LARGE_INTEGER): LongInt;
  209. var
  210. userdata: PKUSER_SHARED_DATA;
  211. local, bias: LARGE_INTEGER;
  212. fields: TIME_FIELDS;
  213. zs: LongInt;
  214. begin
  215. userdata := SharedUserData;
  216. repeat
  217. bias.u.HighPart := userdata^.TimeZoneBias.High1Time;
  218. bias.u.LowPart := userdata^.TimeZoneBias.LowPart;
  219. until bias.u.HighPart = userdata^.TimeZoneBias.High2Time;
  220. local.QuadPart := NtTime.QuadPart - bias.QuadPart;
  221. RtlTimeToTimeFields(@local, @fields);
  222. { from objpas\datutil.inc\DateTimeToDosDateTime }
  223. Result := - 1980;
  224. Result := Result + fields.Year and 127;
  225. Result := Result shl 4;
  226. Result := Result + fields.Month;
  227. Result := Result shl 5;
  228. Result := Result + fields.Day;
  229. Result := Result shl 16;
  230. zs := fields.Hour;
  231. zs := zs shl 6;
  232. zs := zs + fields.Minute;
  233. zs := zs shl 5;
  234. zs := zs + fields.Second div 2;
  235. Result := Result + (zs and $ffff);
  236. end;
  237. function DosToNtTime(aDTime: LongInt; var aNtTime: LARGE_INTEGER): Boolean;
  238. var
  239. fields: TIME_FIELDS;
  240. local, bias: LARGE_INTEGER;
  241. userdata: PKUSER_SHARED_DATA;
  242. begin
  243. { from objpas\datutil.inc\DosDateTimeToDateTime }
  244. fields.Second := (aDTime and 31) * 2;
  245. aDTime := aDTime shr 5;
  246. fields.Minute := aDTime and 63;
  247. aDTime := aDTime shr 6;
  248. fields.Hour := aDTime and 31;
  249. aDTime := aDTime shr 5;
  250. fields.Day := aDTime and 31;
  251. aDTime := aDTime shr 5;
  252. fields.Month := aDTime and 15;
  253. aDTime := aDTime shr 4;
  254. fields.Year := aDTime + 1980;
  255. Result := RtlTimeFieldsToTime(@fields, @local);
  256. if not Result then
  257. Exit;
  258. userdata := SharedUserData;
  259. repeat
  260. bias.u.HighPart := userdata^.TimeZoneBias.High1Time;
  261. bias.u.LowPart := userdata^.TimeZoneBias.LowPart;
  262. until bias.u.HighPart = userdata^.TimeZoneBias.High2Time;
  263. aNtTime.QuadPart := local.QuadPart + bias.QuadPart;
  264. end;
  265. function FileAge(const FileName: UnicodeString): Longint;
  266. begin
  267. { TODO }
  268. Result := -1;
  269. end;
  270. function FileExists(const FileName: UnicodeString): Boolean;
  271. var
  272. ntstr: UNICODE_STRING;
  273. objattr: OBJECT_ATTRIBUTES;
  274. res: NTSTATUS;
  275. iostatus: IO_STATUS_BLOCK;
  276. h: THandle;
  277. begin
  278. UnicodeStrToNtStr(FileName, ntstr);
  279. InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
  280. res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
  281. @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
  282. FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
  283. Result := NT_SUCCESS(res);
  284. if Result then
  285. NtClose(h);
  286. FreeNtStr(ntstr);
  287. end;
  288. function DirectoryExists(const Directory : UnicodeString) : Boolean;
  289. var
  290. ntstr: UNICODE_STRING;
  291. objattr: OBJECT_ATTRIBUTES;
  292. res: NTSTATUS;
  293. iostatus: IO_STATUS_BLOCK;
  294. h: THandle;
  295. begin
  296. UnicodeStrToNtStr(Directory, ntstr);
  297. InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
  298. { first test wether this is a object directory }
  299. res := NtOpenDirectoryObject(@h, DIRECTORY_QUERY, @objattr);
  300. if NT_SUCCESS(res) then
  301. Result := True
  302. else begin
  303. if res = STATUS_OBJECT_TYPE_MISMATCH then begin
  304. { this is a file object! }
  305. res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
  306. @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
  307. FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
  308. Result := NT_SUCCESS(res);
  309. end else
  310. Result := False;
  311. end;
  312. if Result then
  313. NtClose(h);
  314. FreeNtStr(ntstr);
  315. end;
  316. { copied from rtl/unix/sysutils.pp and adapted to UTF-16 }
  317. Function FNMatch(const Pattern,Name:UnicodeString):Boolean;
  318. Var
  319. LenPat,LenName : longint;
  320. function NameUtf16CodePointLen(index: longint): longint;
  321. begin
  322. { see https://en.wikipedia.org/wiki/UTF-16#Description for details }
  323. Result:=1;
  324. { valid surrogate pair? }
  325. if (Name[index]>=#$D800) and
  326. (Name[index]<=#$DBFF) then
  327. begin
  328. if (index+1<=LenName) and
  329. (Name[index+1]>=#$DC00) and
  330. (Name[index+1]<=#$DFFF) then
  331. inc(Result)
  332. else
  333. exit;
  334. end;
  335. { combining diacritics?
  336. 1) U+0300 - U+036F
  337. 2) U+1DC0 - U+1DFF
  338. 3) U+20D0 - U+20FF
  339. 4) U+FE20 - U+FE2F
  340. }
  341. while (index+Result+1<=LenName) and
  342. ((word(ord(Name[index+Result+1])-$0300) <= word($036F-$0300)) or
  343. (word(ord(Name[index+Result+1])-$1DC0) <= word($1DFF-$1DC0)) or
  344. (word(ord(Name[index+Result+1])-$20D0) <= word($20FF-$20D0)) or
  345. (word(ord(Name[index+Result+1])-$FE20) <= word($FE2F-$FE20))) do
  346. begin
  347. inc(Result)
  348. end;
  349. end;
  350. procedure GoToLastByteOfUtf16CodePoint(var j: longint);
  351. begin
  352. { Take one less, because we have to stop at the last word of the sequence.
  353. }
  354. inc(j,NameUtf16CodePointLen(j)-1);
  355. end;
  356. { input:
  357. i: current position in pattern (start of utf-16 code point)
  358. j: current position in name (start of utf-16 code point)
  359. update_i_j: should i and j be changed by the routine or not
  360. output:
  361. i: if update_i_j, then position of last matching part of code point in
  362. pattern, or first non-matching code point in pattern. Otherwise the
  363. same value as on input.
  364. j: if update_i_j, then position of last matching part of code point in
  365. name, or first non-matching code point in name. Otherwise the
  366. same value as on input.
  367. result: true if match, false if no match
  368. }
  369. function CompareUtf16CodePoint(var i,j: longint; update_i_j: boolean): Boolean;
  370. var
  371. words,
  372. new_i,
  373. new_j: longint;
  374. begin
  375. words:=NameUtf16CodePointLen(j);
  376. new_i:=i;
  377. new_j:=j;
  378. { ensure that a part of an UTF-8 codepoint isn't interpreted
  379. as '*' or '?' }
  380. repeat
  381. dec(words);
  382. Result:=
  383. (new_j<=LenName) and
  384. (new_i<=LenPat) and
  385. (Pattern[new_i]=Name[new_j]);
  386. inc(new_i);
  387. inc(new_j);
  388. until not(Result) or
  389. (words=0);
  390. if update_i_j then
  391. begin
  392. i:=new_i;
  393. j:=new_j;
  394. end;
  395. end;
  396. Function DoFNMatch(i,j:longint):Boolean;
  397. Var
  398. Found : boolean;
  399. Begin
  400. Found:=true;
  401. While Found and (i<=LenPat) Do
  402. Begin
  403. Case Pattern[i] of
  404. '?' :
  405. begin
  406. Found:=(j<=LenName);
  407. GoToLastByteOfUtf16CodePoint(j);
  408. end;
  409. '*' : Begin
  410. {find the next character in pattern, different of ? and *}
  411. while Found do
  412. begin
  413. inc(i);
  414. if i>LenPat then
  415. Break;
  416. case Pattern[i] of
  417. '*' : ;
  418. '?' : begin
  419. if j>LenName then
  420. begin
  421. DoFNMatch:=false;
  422. Exit;
  423. end;
  424. GoToLastByteOfUtf16CodePoint(j);
  425. inc(j);
  426. end;
  427. else
  428. Found:=false;
  429. end;
  430. end;
  431. Assert((i>LenPat) or ( (Pattern[i]<>'*') and (Pattern[i]<>'?') ));
  432. { Now, find in name the character which i points to, if the * or
  433. ? wasn't the last character in the pattern, else, use up all
  434. the chars in name }
  435. Found:=false;
  436. if (i<=LenPat) then
  437. begin
  438. repeat
  439. {find a letter (not only first !) which maches pattern[i]}
  440. while (j<=LenName) and
  441. ((name[j]<>pattern[i]) or
  442. not CompareUtf16CodePoint(i,j,false)) do
  443. begin
  444. GoToLastByteOfUtf16CodePoint(j);
  445. inc(j);
  446. end;
  447. if (j<LenName) then
  448. begin
  449. { while positions i/j have already been checked, we have to
  450. ensure that we don't split a code point }
  451. if DoFnMatch(i,j) then
  452. begin
  453. i:=LenPat;
  454. j:=LenName;{we can stop}
  455. Found:=true;
  456. Break;
  457. end
  458. { We didn't find one, need to look further }
  459. else
  460. begin
  461. GoToLastByteOfUtf16CodePoint(j);
  462. inc(j);
  463. end;
  464. end
  465. else if j=LenName then
  466. begin
  467. Found:=true;
  468. Break;
  469. end;
  470. { This 'until' condition must be j>LenName, not j>=LenName.
  471. That's because when we 'need to look further' and
  472. j = LenName then loop must not terminate. }
  473. until (j>LenName);
  474. end
  475. else
  476. begin
  477. j:=LenName;{we can stop}
  478. Found:=true;
  479. end;
  480. end;
  481. #$D800..#$DBFF:
  482. begin
  483. { ensure that a part of an UTF-16 codepoint isn't matched with
  484. '*' or '?' }
  485. Found:=CompareUtf16CodePoint(i,j,true);
  486. { at this point, either Found is false (and we'll stop), or
  487. both pattern[i] and name[j] are the end of the current code
  488. point and equal }
  489. end
  490. else {not a wildcard character in pattern}
  491. Found:=(j<=LenName) and (pattern[i]=name[j]);
  492. end;
  493. inc(i);
  494. inc(j);
  495. end;
  496. DoFnMatch:=Found and (j>LenName);
  497. end;
  498. Begin {start FNMatch}
  499. LenPat:=Length(Pattern);
  500. LenName:=Length(Name);
  501. FNMatch:=DoFNMatch(1,1);
  502. End;
  503. function FindGetFileInfo(const s: UnicodeString; var f: TAbstractSearchRec; var Name: UnicodeString): Boolean;
  504. var
  505. ntstr: UNICODE_STRING;
  506. objattr: OBJECT_ATTRIBUTES;
  507. res: NTSTATUS;
  508. h: THandle;
  509. iostatus: IO_STATUS_BLOCK;
  510. attr: LongInt;
  511. filename: UnicodeString;
  512. isfileobj: Boolean;
  513. objinfo: OBJECT_BASIC_INFORMATION;
  514. fileinfo: FILE_BASIC_INFORMATION;
  515. time: LongInt;
  516. begin
  517. UnicodeStrToNtStr(s, ntstr);
  518. InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
  519. filename := ExtractFileName(s);
  520. { TODO : handle symlinks }
  521. { If Assigned(F.FindHandle) and ((((PUnixFindData(f.FindHandle)^.searchattr)) and faSymlink) > 0) then
  522. FindGetFileInfo:=(fplstat(pointer(s),st)=0)
  523. else
  524. FindGetFileInfo:=(fpstat(pointer(s),st)=0);}
  525. attr := 0;
  526. Result := False;
  527. if (faDirectory and f.FindData.SearchAttr <> 0) and
  528. ((filename = '.') or (filename = '..')) then begin
  529. attr := faDirectory;
  530. res := STATUS_SUCCESS;
  531. end else
  532. res := STATUS_INVALID_PARAMETER;
  533. isfileobj := False;
  534. if not NT_SUCCESS(res) then begin
  535. { first check whether it's a directory }
  536. res := NtOpenDirectoryObject(@h, DIRECTORY_QUERY, @objattr);
  537. if not NT_SUCCESS(res) then
  538. if res = STATUS_OBJECT_TYPE_MISMATCH then begin
  539. res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
  540. @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
  541. FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
  542. isfileobj := NT_SUCCESS(res);
  543. end;
  544. if NT_SUCCESS(res) then
  545. attr := faDirectory;
  546. end;
  547. if not NT_SUCCESS(res) then begin
  548. { first try whether we have a file object }
  549. res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
  550. @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
  551. FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
  552. isfileobj := NT_SUCCESS(res);
  553. if res = STATUS_OBJECT_TYPE_MISMATCH then begin
  554. { is this an object? }
  555. res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
  556. @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
  557. FILE_SYNCHRONOUS_IO_NONALERT);
  558. if (res = STATUS_OBJECT_TYPE_MISMATCH)
  559. and (f.FindData.SearchAttr and faSysFile <> 0) then begin
  560. { this is some other system file like an event or port, so we can only
  561. provide it's name }
  562. res := STATUS_SUCCESS;
  563. attr := faSysFile;
  564. end;
  565. end;
  566. end;
  567. FreeNtStr(ntstr);
  568. if not NT_SUCCESS(res) then
  569. Exit;
  570. time := 0;
  571. if isfileobj then begin
  572. res := NtQueryInformationFile(h, @iostatus, @fileinfo, SizeOf(fileinfo),
  573. FileBasicInformation);
  574. if NT_SUCCESS(res) then begin
  575. time := NtToDosTime(fileinfo.LastWriteTime);
  576. { copy file attributes? }
  577. end;
  578. end else begin
  579. res := NtQueryObject(h, ObjectBasicInformation, @objinfo, SizeOf(objinfo),
  580. Nil);
  581. if NT_SUCCESS(res) then begin
  582. time := NtToDosTime(objinfo.CreateTime);
  583. { what about attributes? }
  584. end;
  585. end;
  586. if (attr and not f.FindData.SearchAttr) = 0 then begin
  587. Name := filename;
  588. f.Attr := attr;
  589. f.Size := 0;
  590. {$ifndef FPUNONE}
  591. if time = 0 then
  592. { for now we use "Now" as a fall back; ideally this should be the system
  593. start time }
  594. f.Time := DateTimeToFileDate(Now)
  595. else
  596. f.Time := time;
  597. {$endif}
  598. Result := True;
  599. end else
  600. Result := False;
  601. NtClose(h);
  602. end;
  603. Procedure InternalFindClose (var Handle: THandle; var FindData: TFindData);
  604. begin
  605. if FindData.Handle <> 0 then
  606. begin
  607. NtClose(FindData.Handle);
  608. FindData.Handle:=0;
  609. end;
  610. end;
  611. Function InternalFindNext (Var Rslt : TAbstractSearchRec; var Name: UnicodeString) : Longint;
  612. {
  613. re-opens dir if not already in array and calls FindGetFileInfo
  614. }
  615. Var
  616. DirName : UnicodeString;
  617. FName,
  618. SName : UnicodeString;
  619. Found,
  620. Finished : boolean;
  621. ntstr: UNICODE_STRING;
  622. objattr: OBJECT_ATTRIBUTES;
  623. buf: array of WideChar;
  624. len: LongWord;
  625. res: NTSTATUS;
  626. i: LongInt;
  627. dirinfo: POBJECT_DIRECTORY_INFORMATION;
  628. filedirinfo: PFILE_DIRECTORY_INFORMATION;
  629. pc: PChar;
  630. filename: UnicodeString;
  631. iostatus: IO_STATUS_BLOCK;
  632. begin
  633. { TODO : relative directories }
  634. Result := -1;
  635. { SearchSpec='' means that there were no wild cards, so only one file to
  636. find.
  637. }
  638. if Rslt.FindData.SearchSpec = '' then
  639. Exit;
  640. { relative directories not supported for now }
  641. if Rslt.FindData.NamePos = 0 then
  642. Exit;
  643. if Rslt.FindData.Handle = 0 then begin
  644. if Rslt.FindData.NamePos > 1 then
  645. filename := Copy(Rslt.FindData.SearchSpec, 1, Rslt.FindData.NamePos - 1)
  646. else
  647. if Rslt.FindData.NamePos = 1 then
  648. filename := Copy(Rslt.FindData.SearchSpec, 1, 1)
  649. else
  650. filename := Rslt.FindData.SearchSpec;
  651. UnicodeStrToNtStr(filename, ntstr);
  652. InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
  653. res := NtOpenDirectoryObject(@Rslt.FindData.Handle,
  654. DIRECTORY_QUERY or DIRECTORY_TRAVERSE, @objattr);
  655. if not NT_SUCCESS(res) then begin
  656. if res = STATUS_OBJECT_TYPE_MISMATCH then
  657. res := NtOpenFile(@Rslt.FindData.Handle,
  658. FILE_LIST_DIRECTORY or NT_SYNCHRONIZE, @objattr,
  659. @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
  660. FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
  661. end else
  662. Rslt.FindData.IsDirObj := True;
  663. FreeNTStr(ntstr);
  664. if not NT_SUCCESS(res) then
  665. Exit;
  666. end;
  667. { if (NTFindData^.SearchType = 0) and
  668. (NTFindData^.Dirptr = Nil) then
  669. begin
  670. If NTFindData^.NamePos = 0 Then
  671. DirName:='./'
  672. Else
  673. DirName:=Copy(NTFindData^.SearchSpec,1,NTFindData^.NamePos);
  674. NTFindData^.DirPtr := fpopendir(Pchar(pointer(DirName)));
  675. end;}
  676. SName := Copy(Rslt.FindData.SearchSpec, Rslt.FindData.NamePos + 1,
  677. Length(Rslt.FindData.SearchSpec));
  678. Found := False;
  679. Finished := not NT_SUCCESS(Rslt.FindData.LastRes)
  680. or (Rslt.FindData.LastRes = STATUS_NO_MORE_ENTRIES);
  681. SetLength(buf, 200);
  682. dirinfo := @buf[0];
  683. filedirinfo := @buf[0];
  684. while not Finished do begin
  685. if Rslt.FindData.IsDirObj then
  686. res := NtQueryDirectoryObject(Rslt.FindData.Handle, @buf[0],
  687. Length(buf) * SizeOf(buf[0]), True, False,
  688. @Rslt.FindData.Context, @len)
  689. else
  690. res := NtQueryDirectoryFile(Rslt.FindData.Handle, 0, Nil, Nil, @iostatus,
  691. @buf[0], Length(buf) * SizeOf(buf[0]), FileDirectoryInformation,
  692. True, Nil, False);
  693. if Rslt.FindData.IsDirObj then begin
  694. Finished := (res = STATUS_NO_MORE_ENTRIES)
  695. or (res = STATUS_NO_MORE_FILES)
  696. or not NT_SUCCESS(res);
  697. Rslt.FindData.LastRes := res;
  698. if dirinfo^.Name.Length > 0 then begin
  699. SetLength(FName, dirinfo^.Name.Length div 2);
  700. move(dirinfo^.Name.Buffer[0],FName[1],dirinfo^.Name.Length);
  701. {$ifdef debug_findnext}
  702. Write(FName, ' (');
  703. for i := 0 to dirinfo^.TypeName.Length div 2 - 1 do
  704. if dirinfo^.TypeName.Buffer[i] < #256 then
  705. Write(AnsiChar(Byte(dirinfo^.TypeName.Buffer[i])))
  706. else
  707. Write('?');
  708. Writeln(')');
  709. {$endif debug_findnext}
  710. end else
  711. FName := '';
  712. end else begin
  713. SetLength(FName, filedirinfo^.FileNameLength div 2);
  714. move(filedirinfo^.FileName[0],FName[1],filedirinfo^.FileNameLength);
  715. end;
  716. if FName = '' then
  717. Finished := True
  718. else begin
  719. if FNMatch(SName, FName) then begin
  720. Found := FindGetFileInfo(Copy(Rslt.FindData.SearchSpec, 1,
  721. Rslt.FindData.NamePos) + FName, Rslt, Name);
  722. if Found then begin
  723. Result := 0;
  724. Exit;
  725. end;
  726. end;
  727. end;
  728. end;
  729. end;
  730. Function InternalFindFirst (Const Path : UnicodeString; Attr : Longint; out Rslt : TAbstractSearchRec; var Name : UnicodeString) : Longint;
  731. {
  732. opens dir and calls FindNext if needed.
  733. }
  734. Begin
  735. Result := -1;
  736. if Path = '' then
  737. Exit;
  738. Rslt.FindData.SearchAttr := Attr;
  739. {Wildcards?}
  740. if (Pos('?', Path) = 0) and (Pos('*', Path) = 0) then begin
  741. if FindGetFileInfo(Path, Rslt, Name) then
  742. Result := 0;
  743. end else begin
  744. {Create Info}
  745. Rslt.FindData.SearchSpec := Path;
  746. Rslt.FindData.NamePos := Length(Rslt.FindData.SearchSpec);
  747. while (Rslt.FindData.NamePos > 0)
  748. and (Rslt.FindData.SearchSpec[Rslt.FindData.NamePos] <> DirectorySeparator)
  749. do
  750. Dec(Rslt.FindData.NamePos);
  751. Result := InternalFindNext(Rslt,Name);
  752. end;
  753. if Result <> 0 then
  754. InternalFindClose(Rslt.FindHandle,Rslt.FindData);
  755. end;
  756. function FileGetDate(Handle: THandle): Longint;
  757. var
  758. res: NTSTATUS;
  759. basic: FILE_BASIC_INFORMATION;
  760. iostatus: IO_STATUS_BLOCK;
  761. begin
  762. res := NtQueryInformationFile(Handle, @iostatus, @basic,
  763. SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation);
  764. if NT_SUCCESS(res) then
  765. Result := NtToDosTime(basic.LastWriteTime)
  766. else
  767. Result := -1;
  768. end;
  769. function FileSetDate(Handle: THandle;Age: Longint): Longint;
  770. var
  771. res: NTSTATUS;
  772. basic: FILE_BASIC_INFORMATION;
  773. iostatus: IO_STATUS_BLOCK;
  774. begin
  775. res := NtQueryInformationFile(Handle, @iostatus, @basic,
  776. SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation);
  777. if NT_SUCCESS(res) then begin
  778. if not DosToNtTime(Age, basic.LastWriteTime) then begin
  779. Result := -1;
  780. Exit;
  781. end;
  782. res := NtSetInformationFile(Handle, @iostatus, @basic,
  783. SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation);
  784. if NT_SUCCESS(res) then
  785. Result := 0
  786. else
  787. Result := res;
  788. end else
  789. Result := res;
  790. end;
  791. function FileGetAttr(const FileName: UnicodeString): Longint;
  792. var
  793. objattr: OBJECT_ATTRIBUTES;
  794. info: FILE_NETWORK_OPEN_INFORMATION;
  795. res: NTSTATUS;
  796. ntstr: UNICODE_STRING;
  797. begin
  798. UnicodeStrToNtStr(FileName, ntstr);
  799. InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
  800. res := NtQueryFullAttributesFile(@objattr, @info);
  801. if NT_SUCCESS(res) then
  802. Result := info.FileAttributes
  803. else
  804. Result := 0;
  805. FreeNtStr(ntstr);
  806. end;
  807. function FileSetAttr(const Filename: UnicodeString; Attr: LongInt): Longint;
  808. var
  809. h: THandle;
  810. objattr: OBJECT_ATTRIBUTES;
  811. ntstr: UNICODE_STRING;
  812. basic: FILE_BASIC_INFORMATION;
  813. res: NTSTATUS;
  814. iostatus: IO_STATUS_BLOCK;
  815. begin
  816. UnicodeStrToNtStr(Filename, ntstr);
  817. InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
  818. res := NtOpenFile(@h,
  819. NT_SYNCHRONIZE or FILE_READ_ATTRIBUTES or FILE_WRITE_ATTRIBUTES,
  820. @objattr, @iostatus,
  821. FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
  822. FILE_SYNCHRONOUS_IO_NONALERT);
  823. FreeNtStr(ntstr);
  824. if NT_SUCCESS(res) then begin
  825. res := NtQueryInformationFile(h, @iostatus, @basic,
  826. SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation);
  827. if NT_SUCCESS(res) then begin
  828. basic.FileAttributes := Attr;
  829. Result := NtSetInformationFile(h, @iostatus, @basic,
  830. SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation);
  831. end;
  832. NtClose(h);
  833. end else
  834. Result := res;
  835. end;
  836. function DeleteFile(const FileName: UnicodeString): Boolean;
  837. var
  838. h: THandle;
  839. objattr: OBJECT_ATTRIBUTES;
  840. ntstr: UNICODE_STRING;
  841. dispinfo: FILE_DISPOSITION_INFORMATION;
  842. res: NTSTATUS;
  843. iostatus: IO_STATUS_BLOCK;
  844. begin
  845. UnicodeStrToNtStr(Filename, ntstr);
  846. InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
  847. res := NtOpenFile(@h, NT_DELETE, @objattr, @iostatus,
  848. FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
  849. FILE_NON_DIRECTORY_FILE);
  850. FreeNtStr(ntstr);
  851. if NT_SUCCESS(res) then begin
  852. dispinfo.DeleteFile := True;
  853. res := NtSetInformationFile(h, @iostatus, @dispinfo,
  854. SizeOf(FILE_DISPOSITION_INFORMATION), FileDispositionInformation);
  855. Result := NT_SUCCESS(res);
  856. NtClose(h);
  857. end else
  858. Result := False;
  859. end;
  860. function RenameFile(const OldName, NewName: UnicodeString): Boolean;
  861. var
  862. h: THandle;
  863. objattr: OBJECT_ATTRIBUTES;
  864. iostatus: IO_STATUS_BLOCK;
  865. dest, src: UNICODE_STRING;
  866. renameinfo: PFILE_RENAME_INFORMATION;
  867. res: LongInt;
  868. begin
  869. { check whether the destination exists first }
  870. UnicodeStrToNtStr(NewName, dest);
  871. InitializeObjectAttributes(objattr, @dest, 0, 0, Nil);
  872. res := NtCreateFile(@h, 0, @objattr, @iostatus, Nil, 0,
  873. FILE_SHARE_READ or FILE_SHARE_WRITE, FILE_OPEN,
  874. FILE_NON_DIRECTORY_FILE, Nil, 0);
  875. if NT_SUCCESS(res) then begin
  876. { destination already exists => error }
  877. NtClose(h);
  878. Result := False;
  879. end else begin
  880. UnicodeStrToNtStr(OldName, src);
  881. InitializeObjectAttributes(objattr, @src, 0, 0, Nil);
  882. res := NtCreateFile(@h,
  883. GENERIC_ALL or NT_SYNCHRONIZE or FILE_READ_ATTRIBUTES,
  884. @objattr, @iostatus, Nil, 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
  885. FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT or FILE_OPEN_REMOTE_INSTANCE
  886. or FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil,
  887. 0);
  888. if NT_SUCCESS(res) then begin
  889. renameinfo := GetMem(SizeOf(FILE_RENAME_INFORMATION) + dest.Length);
  890. with renameinfo^ do begin
  891. ReplaceIfExists := False;
  892. RootDirectory := 0;
  893. FileNameLength := dest.Length;
  894. Move(dest.Buffer^, renameinfo^.FileName, dest.Length);
  895. end;
  896. res := NtSetInformationFile(h, @iostatus, renameinfo,
  897. SizeOf(FILE_RENAME_INFORMATION) + dest.Length,
  898. FileRenameInformation);
  899. if not NT_SUCCESS(res) then begin
  900. { this could happen if src and destination reside on different drives,
  901. so we need to copy the file manually }
  902. {$message warning 'RenameFile: Implement file copy!'}
  903. Result := False;
  904. end else
  905. Result := True;
  906. NtClose(h);
  907. end else
  908. Result := False;
  909. FreeNtStr(src);
  910. end;
  911. FreeNtStr(dest);
  912. end;
  913. {****************************************************************************
  914. Disk Functions
  915. ****************************************************************************}
  916. function diskfree(drive: byte): int64;
  917. begin
  918. { here the mount manager needs to be queried }
  919. Result := -1;
  920. end;
  921. function disksize(drive: byte): int64;
  922. begin
  923. { here the mount manager needs to be queried }
  924. Result := -1;
  925. end;
  926. {****************************************************************************
  927. Time Functions
  928. ****************************************************************************}
  929. procedure GetLocalTime(var SystemTime: TSystemTime);
  930. var
  931. bias, syst: LARGE_INTEGER;
  932. fields: TIME_FIELDS;
  933. userdata: PKUSER_SHARED_DATA;
  934. begin
  935. // get UTC time
  936. userdata := SharedUserData;
  937. repeat
  938. syst.u.HighPart := userdata^.SystemTime.High1Time;
  939. syst.u.LowPart := userdata^.SystemTime.LowPart;
  940. until syst.u.HighPart = userdata^.SystemTime.High2Time;
  941. // adjust to local time
  942. repeat
  943. bias.u.HighPart := userdata^.TimeZoneBias.High1Time;
  944. bias.u.LowPart := userdata^.TimeZoneBias.LowPart;
  945. until bias.u.HighPart = userdata^.TimeZoneBias.High2Time;
  946. syst.QuadPart := syst.QuadPart - bias.QuadPart;
  947. RtlTimeToTimeFields(@syst, @fields);
  948. SystemTime.Year := fields.Year;
  949. SystemTime.Month := fields.Month;
  950. SystemTime.Day := fields.Day;
  951. SystemTime.Hour := fields.Hour;
  952. SystemTime.Minute := fields.Minute;
  953. SystemTime.Second := fields.Second;
  954. SystemTime.Millisecond := fields.MilliSeconds;
  955. end;
  956. {****************************************************************************
  957. Misc Functions
  958. ****************************************************************************}
  959. procedure sysbeep;
  960. begin
  961. { empty }
  962. end;
  963. procedure InitInternational;
  964. begin
  965. InitInternationalGeneric;
  966. end;
  967. {****************************************************************************
  968. Target Dependent
  969. ****************************************************************************}
  970. function SysErrorMessage(ErrorCode: Integer): String;
  971. begin
  972. Result := 'NT error code: 0x' + IntToHex(ErrorCode, 8);
  973. end;
  974. {****************************************************************************
  975. Initialization code
  976. ****************************************************************************}
  977. function wstrlen(p: PWideChar): SizeInt; external name 'FPC_PWIDECHAR_LENGTH';
  978. function GetEnvironmentVariable(const EnvVar: String): String;
  979. var
  980. s, upperenvvar : UTF8String;
  981. i : longint;
  982. hp: pwidechar;
  983. len: sizeint;
  984. begin
  985. { TODO : test once I know how to execute processes }
  986. Result:='';
  987. hp:=PPEB(CurrentPEB)^.ProcessParameters^.Environment;
  988. { first convert to UTF-8, then uppercase in order to avoid potential data
  989. loss }
  990. upperenvvar:=EnvVar;
  991. upperenvvar:=UpperCase(upperenvvar);
  992. while hp^<>#0 do
  993. begin
  994. len:=UnicodeToUTF8(Nil, hp, 0);
  995. SetLength(s,len);
  996. UnicodeToUTF8(PChar(s), hp, len);
  997. i:=pos('=',s);
  998. if uppercase(copy(s,1,i-1))=upperenvvar then
  999. begin
  1000. { copy() returns a rawbytestring -> will keep UTF-8 encoding }
  1001. Result:=copy(s,i+1,length(s)-i);
  1002. break;
  1003. end;
  1004. { next string entry}
  1005. hp:=hp+wstrlen(hp)+1;
  1006. end;
  1007. end;
  1008. function GetEnvironmentVariableCount: Integer;
  1009. var
  1010. hp : pwidechar;
  1011. begin
  1012. Result:=0;
  1013. hp:=PPEB(CurrentPEB)^.ProcessParameters^.Environment;
  1014. If (Hp<>Nil) then
  1015. while hp^<>#0 do
  1016. begin
  1017. Inc(Result);
  1018. hp:=hp+wstrlen(hp)+1;
  1019. end;
  1020. end;
  1021. function GetEnvironmentString(Index: Integer): {$ifdef FPC_RTL_UNICODE}UnicodeString{$else}AnsiString{$endif};
  1022. var
  1023. hp : pwidechar;
  1024. len: sizeint;
  1025. begin
  1026. Result:='';
  1027. hp:=PPEB(CurrentPEB)^.ProcessParameters^.Environment;
  1028. If (Hp<>Nil) then
  1029. begin
  1030. while (hp^<>#0) and (Index>1) do
  1031. begin
  1032. Dec(Index);
  1033. hp:=hp+wstrlen(hp)+1;
  1034. end;
  1035. If (hp^<>#0) then
  1036. begin
  1037. {$ifdef FPC_RTL_UNICODE}
  1038. Result:=hp;
  1039. {$else}
  1040. len:=UnicodeToUTF8(Nil, hp, 0);
  1041. SetLength(Result, len);
  1042. UnicodeToUTF8(PChar(Result), hp, len);
  1043. SetCodePage(RawByteString(Result),CP_UTF8,false);
  1044. {$endif}
  1045. end;
  1046. end;
  1047. end;
  1048. function ExecuteProcess(const Path: AnsiString; const ComLine: AnsiString;
  1049. Flags: TExecuteFlags = []): Integer;
  1050. begin
  1051. { TODO : implement }
  1052. Result := 0;
  1053. end;
  1054. function ExecuteProcess(const Path: AnsiString;
  1055. const ComLine: Array of AnsiString; Flags:TExecuteFlags = []): Integer;
  1056. var
  1057. CommandLine: AnsiString;
  1058. I: integer;
  1059. begin
  1060. Commandline := '';
  1061. for I := 0 to High (ComLine) do
  1062. if Pos (' ', ComLine [I]) <> 0 then
  1063. CommandLine := CommandLine + ' ' + '"' + ComLine [I] + '"'
  1064. else
  1065. CommandLine := CommandLine + ' ' + Comline [I];
  1066. ExecuteProcess := ExecuteProcess (Path, CommandLine,Flags);
  1067. end;
  1068. procedure Sleep(Milliseconds: Cardinal);
  1069. const
  1070. DelayFactor = 10000;
  1071. var
  1072. interval: LARGE_INTEGER;
  1073. begin
  1074. interval.QuadPart := - Milliseconds * DelayFactor;
  1075. NtDelayExecution(False, @interval);
  1076. end;
  1077. {****************************************************************************
  1078. Initialization code
  1079. ****************************************************************************}
  1080. initialization
  1081. InitExceptions; { Initialize exceptions. OS independent }
  1082. InitInternational; { Initialize internationalization settings }
  1083. OnBeep := @SysBeep;
  1084. finalization
  1085. DoneExceptions;
  1086. end.