dcntfslinks.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. {
  2. Double Commander
  3. -------------------------------------------------------------------------
  4. This unit contains functions to work with hard and symbolic links
  5. on the NTFS file system.
  6. Copyright (C) 2012-2025 Alexander Koblov ([email protected])
  7. This library is free software; you can redistribute it and/or
  8. modify it under the terms of the GNU Lesser General Public
  9. License as published by the Free Software Foundation; either
  10. version 2.1 of the License, or (at your option) any later version.
  11. This library is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. Lesser General Public License for more details.
  15. You should have received a copy of the GNU Lesser General Public
  16. License along with this library. If not, see <https://www.gnu.org/licenses/>.
  17. }
  18. unit DCNtfsLinks;
  19. {$mode delphi}
  20. interface
  21. uses
  22. Windows, SysUtils;
  23. const
  24. // CreateSymbolicLink flags
  25. SYMBOLIC_LINK_FLAG_FILE = 0;
  26. SYMBOLIC_LINK_FLAG_DIRECTORY = 1;
  27. // CreateFile flags
  28. FILE_FLAG_OPEN_REPARSE_POINT = $00200000;
  29. // DeviceIoControl control codes
  30. FSCTL_SET_REPARSE_POINT = $000900A4;
  31. FSCTL_GET_REPARSE_POINT = $000900A8;
  32. FSCTL_DELETE_REPARSE_POINT = $000900AC;
  33. // WSL and Cygwin symbolic link
  34. IO_REPARSE_TAG_LX_SYMLINK = $A000001D;
  35. const
  36. LX_SYMLINK_HEADER_SIZE = 4;
  37. REPARSE_DATA_HEADER_SIZE = 8;
  38. MOUNT_POINT_HEADER_SIZE = 8;
  39. FILE_DOES_NOT_EXIST = DWORD(-1);
  40. wsLongFileNamePrefix = UnicodeString('\\?\');
  41. wsNativeFileNamePrefix = UnicodeString('\??\');
  42. wsNetworkFileNamePrefix = UnicodeString('\??\UNC\');
  43. type
  44. {$packrecords c}
  45. TSymbolicLinkReparseBuffer = record
  46. SubstituteNameOffset: USHORT;
  47. SubstituteNameLength: USHORT;
  48. PrintNameOffset: USHORT;
  49. PrintNameLength: USHORT;
  50. Flags: ULONG;
  51. PathBuffer: array[0..0] of WCHAR;
  52. end;
  53. TMountPointReparseBuffer = record
  54. SubstituteNameOffset: USHORT;
  55. SubstituteNameLength: USHORT;
  56. PrintNameOffset: USHORT;
  57. PrintNameLength: USHORT;
  58. PathBuffer: array[0..0] of WCHAR;
  59. end;
  60. TLxSymlinkReparseBuffer = record
  61. FileType: DWORD;
  62. PathBuffer: array[0..0] of AnsiChar;
  63. end;
  64. TGenericReparseBuffer = record
  65. DataBuffer: array[0..0] of UCHAR;
  66. end;
  67. REPARSE_DATA_BUFFER = record
  68. ReparseTag: ULONG;
  69. ReparseDataLength: USHORT;
  70. Reserved: USHORT;
  71. case Integer of
  72. 0: (SymbolicLinkReparseBuffer: TSymbolicLinkReparseBuffer);
  73. 1: (MountPointReparseBuffer: TMountPointReparseBuffer);
  74. 2: (LxSymlinkReparseBuffer: TLxSymlinkReparseBuffer);
  75. 3: (GenericReparseBuffer: TGenericReparseBuffer);
  76. end;
  77. TReparseDataBuffer = REPARSE_DATA_BUFFER;
  78. PReparseDataBuffer = ^REPARSE_DATA_BUFFER;
  79. {$packrecords default}
  80. {en
  81. Creates a symbolic link.
  82. This function is only supported on the NTFS file system.
  83. On Windows 2000/XP it works for directories only
  84. On Windows Vista/Seven it works for directories and files
  85. (for files it works only with Administrator rights)
  86. @param(AFileName The name of the existing file)
  87. @param(ALinkName The name of the symbolic link)
  88. @returns(The function returns @true if successful, @false otherwise)
  89. }
  90. function CreateSymLink(const ATargetName, ALinkName: UnicodeString; Attr: UInt32): Boolean;
  91. {en
  92. Established a hard link beetwen an existing file and new file. This function
  93. is only supported on the NTFS file system, and only for files, not directories.
  94. @param(AFileName The name of the existing file)
  95. @param(ALinkName The name of the new hard link)
  96. @returns(The function returns @true if successful, @false otherwise)
  97. }
  98. function CreateHardLink(const AFileName, ALinkName: UnicodeString): Boolean;
  99. {en
  100. Reads a symbolic link target.
  101. This function is only supported on the NTFS file system.
  102. @param(aSymlinkFileName The name of the symbolic link)
  103. @param(aTargetFileName The name of the target file/directory)
  104. @returns(The function returns @true if successful, @false otherwise)
  105. }
  106. function ReadSymLink(const aSymlinkFileName: UnicodeString; out aTargetFileName: UnicodeString): Boolean;
  107. {en
  108. Creates a WSL/Cygwin symbolic link.
  109. @param(aTargetFileName The name of the existing file)
  110. @param(aSymlinkFileName The name of the symbolic link)
  111. @returns(The function returns @true if successful, @false otherwise)
  112. }
  113. function CreateSymLinkUnix(const aTargetFileName: String; const aSymlinkFileName: UnicodeString): Boolean;
  114. implementation
  115. const
  116. ERROR_DIRECTORY_NOT_SUPPORTED = 336;
  117. type
  118. TCreateSymbolicLinkW = function(
  119. pwcSymlinkFileName,
  120. pwcTargetFileName: PWideChar;
  121. dwFlags: DWORD): BOOL; stdcall;
  122. TCreateHardLinkW = function (
  123. lpFileName,
  124. lpExistingFileName: LPCWSTR;
  125. lpSecurityAttributes: LPSECURITY_ATTRIBUTES): BOOL; stdcall;
  126. var
  127. HasNewApi: Boolean = False;
  128. MayCreateSymLink: Boolean = False;
  129. CreateHardLinkW: TCreateHardLinkW = nil;
  130. CreateSymbolicLinkW: TCreateSymbolicLinkW = nil;
  131. function _CreateHardLink_New(AFileName : UnicodeString; ALinkName: UnicodeString): Boolean;
  132. begin
  133. if Assigned(CreateHardLinkW) then
  134. Result:= CreateHardLinkW(PWideChar(ALinkName), PWideChar(AFileName), nil)
  135. else begin
  136. Result:= False;
  137. SetLastError(ERROR_NOT_SUPPORTED);
  138. end;
  139. end;
  140. function _CreateHardLink_Old(aExistingFileName, aFileName: UnicodeString): Boolean;
  141. var
  142. hFile: THandle;
  143. lpBuffer: TWin32StreamId;
  144. wcFileName: array[0..MAX_PATH] of WideChar;
  145. dwNumberOfBytesWritten: DWORD = 0;
  146. lpContext: LPVOID = nil;
  147. lpFilePart: LPWSTR = nil;
  148. begin
  149. Result:= GetFullPathNameW(PWideChar(aFileName), MAX_PATH, wcFileName, lpFilePart) > 0;
  150. if Result then
  151. begin
  152. hFile:= CreateFileW(PWideChar(aExistingFileName),
  153. GENERIC_READ or GENERIC_WRITE,
  154. FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
  155. nil, OPEN_EXISTING, 0, 0);
  156. Result:= (hFile <> INVALID_HANDLE_VALUE);
  157. end;
  158. if Result then
  159. try
  160. ZeroMemory(@lpBuffer, SizeOf(TWin32StreamId));
  161. with lpBuffer do
  162. begin
  163. dwStreamId:= BACKUP_LINK;
  164. Size.LowPart:= (Length(aFileName) + 1) * SizeOf(WideChar);
  165. end;
  166. // Write stream header
  167. Result:= BackupWrite(hFile,
  168. @lpBuffer,
  169. SizeOf(TWin32StreamId) - SizeOf(PWideChar),
  170. dwNumberOfBytesWritten,
  171. False,
  172. False,
  173. lpContext);
  174. if not Result then Exit;
  175. // Write file name buffer
  176. Result:= BackupWrite(hFile,
  177. @wcFileName,
  178. lpBuffer.Size.LowPart,
  179. dwNumberOfBytesWritten,
  180. False,
  181. False,
  182. lpContext);
  183. if not Result then Exit;
  184. // Finish write operation
  185. Result:= BackupWrite(hFile,
  186. nil,
  187. 0,
  188. dwNumberOfBytesWritten,
  189. True,
  190. False,
  191. lpContext);
  192. finally
  193. CloseHandle(hFile);
  194. end;
  195. end;
  196. function CreateHardLink(const AFileName, ALinkName: UnicodeString): Boolean;
  197. var
  198. dwAttributes: DWORD;
  199. begin
  200. dwAttributes := Windows.GetFileAttributesW(PWideChar(AFileName));
  201. if dwAttributes = FILE_DOES_NOT_EXIST then Exit(False);
  202. if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
  203. begin
  204. SetLastError(ERROR_DIRECTORY_NOT_SUPPORTED);
  205. Exit(False);
  206. end;
  207. dwAttributes := Windows.GetFileAttributesW(PWideChar(ALinkName));
  208. if dwAttributes <> FILE_DOES_NOT_EXIST then
  209. begin
  210. SetLastError(ERROR_FILE_EXISTS);
  211. Exit(False);
  212. end;
  213. if HasNewApi then
  214. Result:= _CreateHardLink_New(AFileName, ALinkName)
  215. else
  216. Result:= _CreateHardLink_Old(AFileName, ALinkName)
  217. end;
  218. function _CreateSymLink_New(const ATargetFileName, ASymlinkFileName: UnicodeString; dwFlags: DWORD): Boolean;
  219. begin
  220. if not Assigned(CreateSymbolicLinkW) then
  221. begin
  222. Result:= False;
  223. SetLastError(ERROR_NOT_SUPPORTED);
  224. end
  225. // CreateSymbolicLinkW under Windows 10 1903 does not return error if user doesn't have
  226. // SeCreateSymbolicLinkPrivilege, so we make manual check and return error in this case
  227. else begin
  228. if MayCreateSymLink then
  229. Result:= CreateSymbolicLinkW(PWideChar(ASymlinkFileName), PWideChar(ATargetFileName), dwFlags)
  230. else begin
  231. Result:= False;
  232. SetLastError(ERROR_PRIVILEGE_NOT_HELD);
  233. end
  234. end;
  235. end;
  236. function _CreateSymLink_Old(aTargetFileName, aSymlinkFileName: UnicodeString): Boolean;
  237. var
  238. hDevice: THandle;
  239. lpInBuffer: PReparseDataBuffer;
  240. dwLastError,
  241. nInBufferSize,
  242. dwPathBufferSize: DWORD;
  243. wsNativeFileName: UnicodeString;
  244. lpBytesReturned: DWORD = 0;
  245. begin
  246. Result:= CreateDirectoryW(PWideChar(aSymlinkFileName), nil);
  247. if Result then
  248. try
  249. hDevice:= CreateFileW(PWideChar(aSymlinkFileName),
  250. GENERIC_WRITE, 0, nil, OPEN_EXISTING,
  251. FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OPEN_REPARSE_POINT, 0);
  252. if hDevice = INVALID_HANDLE_VALUE then
  253. begin
  254. dwLastError:= GetLastError;
  255. Exit(False);
  256. end;
  257. if Pos(wsLongFileNamePrefix, aTargetFileName) <> 1 then
  258. wsNativeFileName:= wsNativeFileNamePrefix + aTargetFileName
  259. else begin
  260. wsNativeFileName:= wsNativeFileNamePrefix + Copy(aTargetFileName, 5, MaxInt);
  261. end;
  262. // File name length with trailing zero and zero for empty PrintName
  263. dwPathBufferSize:= Length(wsNativeFileName) * SizeOf(WideChar) + 4;
  264. nInBufferSize:= REPARSE_DATA_HEADER_SIZE + MOUNT_POINT_HEADER_SIZE + dwPathBufferSize;
  265. lpInBuffer:= GetMem(nInBufferSize);
  266. ZeroMemory(lpInBuffer, nInBufferSize);
  267. with lpInBuffer^, lpInBuffer^.MountPointReparseBuffer do
  268. begin
  269. ReparseTag:= IO_REPARSE_TAG_MOUNT_POINT;
  270. ReparseDataLength:= MOUNT_POINT_HEADER_SIZE + dwPathBufferSize;
  271. SubstituteNameLength:= Length(wsNativeFileName) * SizeOf(WideChar);
  272. PrintNameOffset:= SubstituteNameOffset + SubstituteNameLength + SizeOf(WideChar);
  273. CopyMemory(@PathBuffer[0], @wsNativeFileName[1], SubstituteNameLength);
  274. end;
  275. Result:= DeviceIoControl(hDevice, // handle to file or directory
  276. FSCTL_SET_REPARSE_POINT, // dwIoControlCode
  277. lpInBuffer, // input buffer
  278. nInBufferSize, // size of input buffer
  279. nil, // lpOutBuffer
  280. 0, // nOutBufferSize
  281. lpBytesReturned, // lpBytesReturned
  282. nil); // OVERLAPPED structure
  283. if not Result then dwLastError:= GetLastError;
  284. FreeMem(lpInBuffer);
  285. CloseHandle(hDevice);
  286. finally
  287. if not Result then
  288. begin
  289. RemoveDirectoryW(PWideChar(aSymlinkFileName));
  290. SetLastError(dwLastError);
  291. end;
  292. end;
  293. end;
  294. function CreateSymLink(const ATargetName, ALinkName: UnicodeString; Attr: UInt32): Boolean;
  295. var
  296. dwAttributes: DWORD;
  297. lpFilePart: LPWSTR = nil;
  298. AFileName, AFullPathName: UnicodeString;
  299. begin
  300. Result:= False;
  301. if (Length(ATargetName) > 1) and CharInSet(ATargetName[2], [':', '\']) then
  302. AFullPathName:= ATargetName
  303. else begin
  304. SetLength(AFullPathName, MaxSmallint);
  305. AFileName:= ExtractFilePath(ALinkName) + ATargetName;
  306. dwAttributes:= GetFullPathNameW(PWideChar(AFileName), MaxSmallint, PWideChar(AFullPathName), lpFilePart);
  307. if dwAttributes > 0 then
  308. SetLength(AFullPathName, dwAttributes)
  309. else begin
  310. AFullPathName:= ATargetName;
  311. end;
  312. end;
  313. if (Attr <> FILE_DOES_NOT_EXIST) then
  314. dwAttributes:= Attr
  315. else begin
  316. dwAttributes:= Windows.GetFileAttributesW(PWideChar(AFullPathName));
  317. end;
  318. if dwAttributes = FILE_DOES_NOT_EXIST then Exit;
  319. if HasNewApi = False then
  320. begin
  321. if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
  322. Result:= _CreateSymLink_Old(AFullPathName, ALinkName)
  323. else
  324. SetLastError(ERROR_NOT_SUPPORTED);
  325. end
  326. else begin
  327. if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  328. Result:= _CreateSymLink_New(ATargetName, ALinkName, SYMBOLIC_LINK_FLAG_FILE)
  329. else begin
  330. if (not MayCreateSymLink) and (Pos('\\', AFullPathName) = 0) then
  331. Result:= _CreateSymLink_Old(AFullPathName, ALinkName)
  332. else begin
  333. Result:= _CreateSymLink_New(ATargetName, ALinkName, SYMBOLIC_LINK_FLAG_DIRECTORY);
  334. end;
  335. end;
  336. end;
  337. end;
  338. function CreateSymLinkUnix(const aTargetFileName: String; const aSymlinkFileName: UnicodeString): Boolean;
  339. var
  340. hDevice: THandle;
  341. dwLastError: DWORD;
  342. nInBufferSize: DWORD;
  343. dwPathBufferSize: DWORD;
  344. lpBytesReturned: DWORD = 0;
  345. lpInBuffer: PReparseDataBuffer;
  346. begin
  347. hDevice:= CreateFileW(PWideChar(aSymlinkFileName),
  348. GENERIC_WRITE, 0, nil, CREATE_NEW,
  349. FILE_FLAG_OPEN_REPARSE_POINT, 0);
  350. if hDevice = INVALID_HANDLE_VALUE then Exit(False);
  351. dwPathBufferSize:= Length(aTargetFileName);
  352. nInBufferSize:= REPARSE_DATA_HEADER_SIZE + LX_SYMLINK_HEADER_SIZE + dwPathBufferSize;
  353. lpInBuffer:= GetMem(nInBufferSize);
  354. ZeroMemory(lpInBuffer, nInBufferSize);
  355. with lpInBuffer^, lpInBuffer^.LxSymlinkReparseBuffer do
  356. begin
  357. FileType:= 2; // symbolic link
  358. ReparseTag:= IO_REPARSE_TAG_LX_SYMLINK;
  359. ReparseDataLength:= LX_SYMLINK_HEADER_SIZE + dwPathBufferSize;
  360. CopyMemory(@PathBuffer[0], @aTargetFileName[1], Length(aTargetFileName));
  361. end;
  362. Result:= DeviceIoControl(hDevice, // handle to file or directory
  363. FSCTL_SET_REPARSE_POINT, // dwIoControlCode
  364. lpInBuffer, // input buffer
  365. nInBufferSize, // size of input buffer
  366. nil, // lpOutBuffer
  367. 0, // nOutBufferSize
  368. lpBytesReturned, // lpBytesReturned
  369. nil); // OVERLAPPED structure
  370. // File system does not support reparse points
  371. // Create a normal file with the link target inside
  372. if (not Result) and (GetLastError = ERROR_INVALID_FUNCTION) then
  373. begin
  374. Result:= (FileWrite(hDevice, aTargetFileName[1], dwPathBufferSize) = dwPathBufferSize);
  375. if Result then SetFileAttributesW(PWideChar(aSymlinkFileName), FILE_ATTRIBUTE_SYSTEM);
  376. end;
  377. if not Result then dwLastError:= GetLastError;
  378. FreeMem(lpInBuffer);
  379. CloseHandle(hDevice);
  380. if not Result then
  381. begin
  382. DeleteFileW(PWideChar(aSymlinkFileName));
  383. SetLastError(dwLastError);
  384. end;
  385. end;
  386. function ReadSymLink(const aSymlinkFileName: UnicodeString; out aTargetFileName: UnicodeString): Boolean;
  387. var
  388. L: Integer;
  389. hDevice: THandle;
  390. dwFileAttributes: DWORD;
  391. caOutBuffer: array[0..MaxSmallint] of Byte;
  392. lpOutBuffer: TReparseDataBuffer absolute caOutBuffer;
  393. pwcTargetFileName: PWideChar;
  394. lpBytesReturned: DWORD = 0;
  395. dwFlagsAndAttributes: DWORD;
  396. begin
  397. dwFileAttributes:= GetFileAttributesW(PWideChar(aSymlinkFileName));
  398. Result:= dwFileAttributes <> FILE_DOES_NOT_EXIST;
  399. if Result then
  400. begin
  401. if (dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  402. dwFlagsAndAttributes:= FILE_FLAG_OPEN_REPARSE_POINT
  403. else
  404. dwFlagsAndAttributes:= FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OPEN_REPARSE_POINT;
  405. // Open reparse point
  406. hDevice:= CreateFileW(PWideChar(aSymlinkFileName),
  407. 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
  408. nil, OPEN_EXISTING, dwFlagsAndAttributes, 0);
  409. Result:= hDevice <> INVALID_HANDLE_VALUE;
  410. if not Result then Exit;
  411. Result:= DeviceIoControl(hDevice, // handle to file or directory
  412. FSCTL_GET_REPARSE_POINT, // dwIoControlCode
  413. nil, // input buffer
  414. 0, // size of input buffer
  415. @caOutBuffer, // lpOutBuffer
  416. SizeOf(caOutBuffer), // nOutBufferSize
  417. lpBytesReturned, // lpBytesReturned
  418. nil); // OVERLAPPED structure
  419. CloseHandle(hDevice);
  420. if Result then
  421. begin
  422. case lpOutBuffer.ReparseTag of
  423. IO_REPARSE_TAG_SYMLINK:
  424. with lpOutBuffer.SymbolicLinkReparseBuffer do
  425. begin
  426. pwcTargetFileName:= @PathBuffer[0];
  427. pwcTargetFileName:= pwcTargetFileName + SubstituteNameOffset div SizeOf(WideChar);
  428. SetLength(aTargetFileName, SubstituteNameLength div SizeOf(WideChar));
  429. CopyMemory(PWideChar(aTargetFileName), pwcTargetFileName, SubstituteNameLength);
  430. end;
  431. IO_REPARSE_TAG_MOUNT_POINT:
  432. with lpOutBuffer.MountPointReparseBuffer do
  433. begin
  434. pwcTargetFileName:= @PathBuffer[0];
  435. pwcTargetFileName:= pwcTargetFileName + SubstituteNameOffset div SizeOf(WideChar);
  436. SetLength(aTargetFileName, SubstituteNameLength div SizeOf(WideChar));
  437. CopyMemory(PWideChar(aTargetFileName), pwcTargetFileName, SubstituteNameLength);
  438. end;
  439. IO_REPARSE_TAG_LX_SYMLINK:
  440. with lpOutBuffer.LxSymlinkReparseBuffer do
  441. begin
  442. L:= lpOutBuffer.ReparseDataLength - SizeOf(FileType);
  443. SetLength(aTargetFileName, L + 1);
  444. SetLength(aTargetFileName, MultiByteToWideChar(CP_UTF8, 0, @PathBuffer[0], L, PWideChar(aTargetFileName), L + 1));
  445. end;
  446. end;
  447. if Pos(wsNetworkFileNamePrefix, aTargetFileName) = 1 then
  448. Delete(aTargetFileName, 2, Length(wsNetworkFileNamePrefix) - 2)
  449. else if Pos(wsNativeFileNamePrefix, aTargetFileName) = 1 then
  450. Delete(aTargetFileName, 1, Length(wsNativeFileNamePrefix));
  451. end;
  452. end;
  453. end;
  454. function MayCreateSymbolicLink: Boolean;
  455. const
  456. SE_CREATE_SYMBOLIC_LINK_NAME = 'SeCreateSymbolicLinkPrivilege';
  457. var
  458. I: Integer;
  459. hProcess: HANDLE;
  460. dwLength: DWORD = 0;
  461. seCreateSymbolicLink: LUID = 0;
  462. TokenInformation: array [0..1023] of Byte;
  463. Privileges: TTokenPrivileges absolute TokenInformation;
  464. begin
  465. hProcess:= GetCurrentProcess();
  466. if (OpenProcessToken(hProcess, TOKEN_READ, hProcess)) then
  467. try
  468. if (LookupPrivilegeValueW(nil, SE_CREATE_SYMBOLIC_LINK_NAME, seCreateSymbolicLink)) then
  469. begin
  470. if (GetTokenInformation(hProcess, TokenPrivileges, @Privileges, SizeOf(TokenInformation), dwLength)) then
  471. begin
  472. {$PUSH}{$R-}
  473. for I:= 0 to Int32(Privileges.PrivilegeCount) - 1 do
  474. begin
  475. if Privileges.Privileges[I].Luid = seCreateSymbolicLink then
  476. Exit(True);
  477. end;
  478. {$POP}
  479. end;
  480. end;
  481. finally
  482. CloseHandle(hProcess);
  483. end;
  484. Result:= False;
  485. end;
  486. procedure Initialize;
  487. var
  488. AHandle: HMODULE;
  489. begin
  490. MayCreateSymLink:= MayCreateSymbolicLink;
  491. HasNewApi:= (Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion >= 6);
  492. if HasNewApi then begin
  493. AHandle:= GetModuleHandle('kernel32.dll');
  494. CreateHardLinkW:= TCreateHardLinkW(GetProcAddress(AHandle, 'CreateHardLinkW'));
  495. CreateSymbolicLinkW:= TCreateSymbolicLinkW(GetProcAddress(AHandle, 'CreateSymbolicLinkW'));
  496. end;
  497. end;
  498. initialization
  499. Initialize;
  500. end.