Recent

Author Topic: fpc 3.2.2 under Windows 10/11 64-Bit Pro: How to lock a Directory ?  (Read 738 times)

paule32

  • Hero Member
  • *****
  • Posts: 513
  • One in all. But, not all in one.
Hi,
how can I lock a Directory at Application start and Delete, and unlock the Directory when Applications ends ?
This would be for Windows 10/11 64-Bit Pro
MS-IIS - Internet Information Server, Apache, PHP/HTML/CSS, MinGW-32/64 MSys2 GNU C/C++ 13 (-stdc++20), FPC 3.2.2
A Friend in need, is a Friend indeed.

440bx

  • Hero Member
  • *****
  • Posts: 5441
how can I lock a Directory ...
Do you mean prevent any and all operations on the directory such as file creation, deletion, opening, reading, etc or do you mean something else ?

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 17103
  • Ceterum censeo Trump esse delendam
You need to use backup semantics that locks a directory for other users, who will get a sharing violation. Small example:
Code: Pascal  [Select][+][-]
  1. program LockDirectoryExample;
  2. {$mode objfpc}{$H+}
  3. uses
  4.   Windows, SysUtils;
  5.  
  6. var
  7.   hDir: THandle;
  8.   DirPath: string;
  9.  
  10. // Function to recursively delete a directory and its contents
  11. function DeleteDirectory(const Path: string): Boolean;
  12. var
  13.   SearchRec: TSearchRec;
  14. begin
  15.   Result := False;
  16.   if FindFirst(Path + '\*.*', faAnyFile, SearchRec) = 0 then
  17.   try
  18.     repeat
  19.       if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
  20.       begin
  21.         if (SearchRec.Attr and faDirectory) <> 0 then
  22.           DeleteDirectory(Path + '\' + SearchRec.Name)
  23.         else
  24.           DeleteFile(Path + '\' + SearchRec.Name);
  25.       end;
  26.     until FindNext(SearchRec) <> 0;
  27.   finally
  28.     FindClose(SearchRec);
  29.   end;
  30.   Result := RemoveDir(Path);
  31. end;
  32.  
  33. begin
  34.   //DirPath := 'C:\Your\Directory\Path'; // Replace with your directory path
  35.   DirPath := 'd:\testlock';
  36.   // Attempt to lock the directory
  37.   hDir := CreateFile(
  38.     PChar(DirPath),
  39.     0,                 // No access required, just lock
  40.     0,                 // Exclusive mode (no sharing)
  41.     nil,
  42.     OPEN_EXISTING,
  43.     FILE_FLAG_BACKUP_SEMANTICS,
  44.     0
  45.   );
  46.  
  47.   if hDir = INVALID_HANDLE_VALUE then
  48.   begin
  49.     Writeln('Error locking directory: ', SysErrorMessage(GetLastError));
  50.     Exit;
  51.   end;
  52.  
  53.   try
  54.     Writeln('Directory locked successfully. Running application...');
  55.     // Your application logic here
  56.     Writeln('can''t touch me');
  57.     Readln; // Pause for demonstration
  58.   finally
  59.     // Unlock the directory by closing the handle
  60.     CloseHandle(hDir);
  61.     Writeln('Directory unlocked.');
  62. {
  63.     // Delete the directory (optional)
  64.     if DeleteDirectory(DirPath) then
  65.       Writeln('Directory deleted successfully.')
  66.     else
  67.       Writeln('Failed to delete directory: ', SysErrorMessage(GetLastError));
  68. }
  69.   end;
  70. end.

This is simplified working code but note it is not robust enough as it is:
- Without further care, the lock stays if your application crashes
- You may fill in the correct security attributes for its use.
« Last Edit: May 04, 2025, 02:56:38 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

paule32

  • Hero Member
  • *****
  • Posts: 513
  • One in all. But, not all in one.
I mean locking from the Application.
At start till end.

Because my new Project use temporary Files at current time.
That no other User as the Application can access the directory.

Read and Write should be able by the Application.
Deny for all other Processes.
MS-IIS - Internet Information Server, Apache, PHP/HTML/CSS, MinGW-32/64 MSys2 GNU C/C++ 13 (-stdc++20), FPC 3.2.2
A Friend in need, is a Friend indeed.

paule32

  • Hero Member
  • *****
  • Posts: 513
  • One in all. But, not all in one.
@Thaddy

Thank you for the fast reply.
I will let your Code a change, and catch them for my Project.
MS-IIS - Internet Information Server, Apache, PHP/HTML/CSS, MinGW-32/64 MSys2 GNU C/C++ 13 (-stdc++20), FPC 3.2.2
A Friend in need, is a Friend indeed.

Thaddy

  • Hero Member
  • *****
  • Posts: 17103
  • Ceterum censeo Trump esse delendam
@Thaddy

Thank you for the fast reply.
I will let your Code a change, and catch them for my Project.
It also has - commented out - code to delete the directory. Also works but I did not know your requirements.
Note my warnings.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Thaddy

  • Hero Member
  • *****
  • Posts: 17103
  • Ceterum censeo Trump esse delendam
Wait! it needs improvement: we need the security attributes after all, I though I could get away with it. Give me a couple of minutes.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

paule32

  • Hero Member
  • *****
  • Posts: 513
  • One in all. But, not all in one.
yes. deletion of the directory works fine.
MS-IIS - Internet Information Server, Apache, PHP/HTML/CSS, MinGW-32/64 MSys2 GNU C/C++ 13 (-stdc++20), FPC 3.2.2
A Friend in need, is a Friend indeed.

Thaddy

  • Hero Member
  • *****
  • Posts: 17103
  • Ceterum censeo Trump esse delendam
Yes but the locking is not fool proof. I am busy with ACL locking. Problem is that I discovered some wrong decalrations in JwaWindows/jwaAcctrl so I need to fix that first. Can take some time: The declare pointerto pointer to structure where the accesscontrol structure. Better solve that with a var.
« Last Edit: May 04, 2025, 04:53:59 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

paule32

  • Hero Member
  • *****
  • Posts: 513
  • One in all. But, not all in one.
I have some Code. But it hangs the Application:

Code: Pascal  [Select][+][-]
  1. unit misc;
  2.  
  3. {$mode Delphi}
  4.  
  5. interface
  6.  
  7. uses
  8.   Windows, Classes, SysUtils, Dialogs;
  9.  
  10. type
  11.   TLockedFile = record
  12.     Handle: THandle;
  13.     FilePath: String;
  14.   end;
  15.  
  16. type
  17.   PFILE_NOTIFY_INFORMATION = ^FILE_NOTIFY_INFORMATION;
  18.   FILE_NOTIFY_INFORMATION = record
  19.     NextEntryOffset: DWORD;
  20.     Action: DWORD;
  21.     FileNameLength: DWORD;
  22.     FileName: array[0..0] of PChar;
  23.   end;
  24.  
  25.   TFileLockerThread = class(TThread)
  26.   private
  27.     FDirPath: string;
  28.     FDirHandle: THandle;
  29.     FFiles: array of TLockedFile;
  30.     FLock: TRTLCriticalSection;
  31.   protected
  32.     procedure Execute; override;
  33.     procedure LockFile(const FileName: string);
  34.   public
  35.     constructor Create(const DirPath: string);
  36.     destructor Destroy; override;
  37.   end;
  38. implementation
  39.  
  40. procedure TFileLockerThread.LockFile(const FileName: string);
  41. var
  42.   FullPath: String;
  43.   hFile: THandle;
  44.   Entry: TLockedFile;
  45.   Count: Integer;
  46. begin
  47.   FullPath := FDirPath + FileName;
  48.  
  49.   hFile := CreateFileW(
  50.     PWideChar(FullPath),
  51.     GENERIC_READ,
  52.     0,
  53.     nil,
  54.     OPEN_EXISTING,
  55.     FILE_ATTRIBUTE_NORMAL,
  56.     0
  57.   );
  58.  
  59.   if hFile <> INVALID_HANDLE_VALUE then
  60.   begin
  61.     //Writeln('Neue Datei gesperrt: ', FullPath);
  62.     ShowMessage('lock new file: ' + FullPath);
  63.     Entry.Handle := hFile;
  64.     Entry.FilePath := FullPath;
  65.  
  66.     EnterCriticalSection(FLock);
  67.     Count := Length(FFiles);
  68.     SetLength(FFiles, Count + 1);
  69.     FFiles[Count] := Entry;
  70.     LeaveCriticalSection(FLock);
  71.   end;
  72. end;
  73.  
  74. destructor TFileLockerThread.Destroy;
  75. var
  76.   i: Integer;
  77. begin
  78.   if FDirHandle <> INVALID_HANDLE_VALUE then
  79.     CloseHandle(FDirHandle);
  80.  
  81.   EnterCriticalSection(FLock);
  82.   try
  83.     for i := 0 to High(FFiles) do
  84.     begin
  85.       CloseHandle(FFiles[i].Handle);
  86.       if not DeleteFileW(PWideChar(FFiles[i].FilePath)) then
  87.       ShowMessage('Error: could not delete: ' + FFiles[i].FilePath);
  88.     end;
  89.   finally
  90.     LeaveCriticalSection(FLock);
  91.     DoneCriticalSection(FLock);
  92.   end;
  93.  
  94.   inherited Destroy;
  95. end;
  96.  
  97. constructor TFileLockerThread.Create(const DirPath: string);
  98. begin
  99.   inherited Create(False);
  100.   FreeOnTerminate := False;
  101.  
  102.   FDirPath := IncludeTrailingPathDelimiter(DirPath);
  103.   InitializeCriticalSection(FLock);
  104.  
  105.   FDirHandle := CreateFileW(
  106.     PWideChar(FDirPath),
  107.     FILE_LIST_DIRECTORY,
  108.     FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
  109.     nil,
  110.     OPEN_EXISTING,
  111.     FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED,
  112.     0
  113.   );
  114.  
  115.   if FDirHandle = INVALID_HANDLE_VALUE then
  116.   raise Exception.CreateFmt('Error: could not open directory.' + #10 +
  117.   'Error Code: ' + '%d', [GetLastError]);
  118. end;
  119.  
  120. procedure TFileLockerThread.Execute;
  121. const
  122.   BUF_SIZE = 2048;
  123. var
  124.   Buffer: array[0..BUF_SIZE - 1] of Byte;
  125.   BytesReturned: DWORD;
  126.   NotifyInfo: PFILE_NOTIFY_INFORMATION;
  127.   FileName: WideString;
  128. begin
  129.   while not Terminated do
  130.   begin
  131.     if ReadDirectoryChangesW(
  132.       FDirHandle,
  133.       @Buffer,
  134.       SizeOf(Buffer),
  135.       False,
  136.       FILE_NOTIFY_CHANGE_FILE_NAME,
  137.       @BytesReturned,
  138.       nil,
  139.       nil
  140.     ) then
  141.     begin
  142.       NotifyInfo := @Buffer;
  143.       repeat
  144.         SetString(FileName, PChar(NotifyInfo^.FileName), NotifyInfo^.FileNameLength div SizeOf(WideChar));
  145.  
  146.         if NotifyInfo^.Action = FILE_ACTION_ADDED then
  147.           LockFile(PChar(FileName));
  148.  
  149.         if NotifyInfo^.NextEntryOffset = 0 then
  150.           Break;
  151.  
  152.         NotifyInfo := PFILE_NOTIFY_INFORMATION(PByte(NotifyInfo) + NotifyInfo^.NextEntryOffset);
  153.       until False;
  154.     end
  155.     else
  156.     begin
  157.       ShowMessage('Error: ReadDirectoryChangesW: ' + IntToStr(GetLastError));
  158.     end;
  159.   end;
  160. end;
  161.  
  162. procedure StartLock;
  163. var
  164.   LockerThread: TFileLockerThread;
  165. begin
  166.   try
  167.     LockerThread := TFileLockerThread.Create('C:\Test');
  168.     //Writeln('Überwachung läuft. ENTER zum Beenden...');
  169.     //Readln;
  170.     LockerThread.Terminate;
  171.     LockerThread.WaitFor;
  172.     LockerThread.Free;
  173.   except
  174.     on E: Exception do
  175.     ShowMessage('Error: ' + E.Message);
  176.   end;
  177. end;
  178.  
  179. end.

and the Application start Code:

Code: Pascal  [Select][+][-]
  1. program chmViewer;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}
  7.   cthreads,
  8.   {$ENDIF}
  9.   {$IFDEF HASAMIGA}
  10.   athreads,
  11.   {$ENDIF}
  12.   Interfaces, // this includes the LCL widgetset
  13.   Windows, Dialogs, SysUtils, StrUtils,
  14.   uCEFApplication,  // WICHTIG: Initialisiert CEF
  15.   Forms, main, misc
  16.   { you can add units after this };
  17.  
  18. {$R *.res}
  19.  
  20. var
  21.   dirpath: String;
  22.   hdir: THandle;
  23.  
  24. // Function to recursively delete a directory and its contents
  25. function DeleteDirectory(const Path: string): Boolean;
  26. var
  27.   SearchRec: TSearchRec;
  28. begin
  29.   Result := False;
  30.   if FindFirst(Path + '\*.*', faAnyFile, SearchRec) = 0 then
  31.   try
  32.     repeat
  33.       if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
  34.       begin
  35.         if (SearchRec.Attr and faDirectory) <> 0 then
  36.           DeleteDirectory(Path + '\' + SearchRec.Name)
  37.         else
  38.           DeleteFile(Path + '\' + SearchRec.Name);
  39.       end;
  40.     until FindNext(SearchRec) <> 0;
  41.   finally
  42.     FindClose(SearchRec);
  43.   end;
  44.   Result := RemoveDir(Path);
  45. end;
  46.  
  47. var
  48.   LockerThread: TFileLockerThread;
  49.  
  50. begin
  51.   GlobalCEFApp := TCefApplication.Create;
  52.  
  53.   if not GlobalCEFApp.StartMainProcess then
  54.   begin
  55.     GlobalCEFApp.Free;
  56.     Halt(0);
  57.   end;
  58.  
  59.   DirPath := ExtractFilePath(ParamStr(0)) + '\lib';
  60.   ShowMessage(DirPath);
  61.  
  62.   if DirectoryExists(DirPath) then
  63.   RemoveDir(DirPath);
  64.   CreateDir(DirPath);
  65.  
  66.   try
  67.     try
  68.       //LockerThread := TFileLockerThread.Create(DirPath);
  69.  
  70.       RequireDerivedFormResource := True;
  71.       Application.Scaled:=True;
  72.       Application.Initialize;
  73.       Application.CreateForm(TForm1, Form1);
  74.       Application.Run;
  75.  
  76.       //LockerThread.Terminate;
  77.       //LockerThread.WaitFor;
  78.       //LockerThread.Free;
  79.  
  80.     except
  81.       on E: Exception do
  82.       begin
  83.         ShowMessage('Error: ' + E.Message + #10 +
  84.         SysErrorMessage(GetLastError));
  85.         Halt(3);
  86.       end;
  87.     end;
  88.   finally
  89.     DeleteDirectory(DirPath);
  90.   end;
  91. end.

I comment the LockerThread that should be observe the Access.
When not comment, the Application appear in the Task-Manager, but brings no Application Window
MS-IIS - Internet Information Server, Apache, PHP/HTML/CSS, MinGW-32/64 MSys2 GNU C/C++ 13 (-stdc++20), FPC 3.2.2
A Friend in need, is a Friend indeed.

paule32

  • Hero Member
  • *****
  • Posts: 513
  • One in all. But, not all in one.
Re: fpc 3.2.2 under Windows 10/11 64-Bit Pro: How to lock a Directory ?
« Reply #10 on: May 06, 2025, 12:16:22 pm »
could someone review the Code ?
Thank you
MS-IIS - Internet Information Server, Apache, PHP/HTML/CSS, MinGW-32/64 MSys2 GNU C/C++ 13 (-stdc++20), FPC 3.2.2
A Friend in need, is a Friend indeed.

Fibonacci

  • Hero Member
  • *****
  • Posts: 753
  • Internal Error Hunter
Re: fpc 3.2.2 under Windows 10/11 64-Bit Pro: How to lock a Directory ?
« Reply #11 on: May 06, 2025, 04:25:17 pm »
This raises an exception:

Code: Pascal  [Select][+][-]
  1. LockerThread := TFileLockerThread.Create(DirPath);

So the rest of the code (Application.CreateForm & Run) isnt executed.

Fix for this: in TFileLockerThread.Create cast the path to WideString first:

Code: Pascal  [Select][+][-]
  1.   FDirHandle := CreateFileW(
  2.     PWideChar(WideString(FDirPath)),
  3.     0,

Suggestion: place TFileLockerThread.Create in a separate try-except block.

If you use W versions of the WinAPI functions, you should actually use WIDE strings, not ANSI (in FPC use UnicodeString for WinAPI's wide), but you have used a regular (Ansi)String in the contructor, and to store the path (FDirPath, DirPath). You should use UnicodeString everywhere.

And also:

Code: Pascal  [Select][+][-]
  1. DirPath := ExtractFilePath(ParamStr(0)) + '\lib';
->
Code: Pascal  [Select][+][-]
  1. DirPath := ExtractFilePath(ParamStr(0)) + 'lib';

 

TinyPortal © 2005-2018