|
@@ -0,0 +1,311 @@
|
|
|
+unit OperationWithTimeout;
|
|
|
+
|
|
|
+interface
|
|
|
+
|
|
|
+uses
|
|
|
+ Winapi.Windows, Winapi.ShlObj, Winapi.ShellAPI, ActiveX;
|
|
|
+
|
|
|
+function ShellFolderGetAttributesOfWithTimeout(
|
|
|
+ ShellFolder: IShellFolder; cidl: UINT; var apidl: PItemIDList; var rgfInOut: UINT; Timeout: Integer): HResult;
|
|
|
+
|
|
|
+function SHGetFileInfoForPidlWithTimeout(
|
|
|
+ PIDL: PItemIDList; dwFileAttributes: DWORD; var psfi: TSHFileInfoW;
|
|
|
+ cbFileInfo, uFlags: UINT; Timeout: Integer): DWORD_PTR;
|
|
|
+
|
|
|
+function ShellFolderParseDisplayNameWithTimeout(
|
|
|
+ ShellFolder: IShellFolder; hwndOwner: HWND; pbcReserved: Pointer; lpszDisplayName: POLESTR;
|
|
|
+ out pchEaten: ULONG; out ppidl: PItemIDList; var dwAttributes: ULONG; Timeout: Integer): HResult;
|
|
|
+
|
|
|
+implementation
|
|
|
+
|
|
|
+uses
|
|
|
+ System.Classes, System.Types, System.SysUtils, System.SyncObjs, System.Contnrs, CompThread;
|
|
|
+
|
|
|
+type
|
|
|
+ TOperation = class;
|
|
|
+
|
|
|
+ TOperationEvent = procedure(Operation: TOperation);
|
|
|
+
|
|
|
+ TOperation = class(TObject)
|
|
|
+ public
|
|
|
+ OperationEvent: TOperationEvent;
|
|
|
+
|
|
|
+ // ShellFolderGetAttributesOfWithTimeout
|
|
|
+ ShellFolder: IShellFolder;
|
|
|
+ cidl: UINT;
|
|
|
+ apidl: PItemIDList;
|
|
|
+ rgfInOut: UINT;
|
|
|
+ Timeout: Integer;
|
|
|
+ ResultHResult: HResult;
|
|
|
+
|
|
|
+ // SHGetFileInfoWithTimeout
|
|
|
+ PIDL: PItemIDList;
|
|
|
+ dwFileAttributes: DWORD;
|
|
|
+ psfi: TSHFileInfoW;
|
|
|
+ cbFileInfo, uFlags: UINT;
|
|
|
+ ResultDWordPtr: DWORD_PTR;
|
|
|
+
|
|
|
+ // ShellFolderParseDisplayNameWithTimeout
|
|
|
+ // Uses ShellFolder and ResultHResult
|
|
|
+ hwndOwner: HWND;
|
|
|
+ pbcReserved: Pointer;
|
|
|
+ DisplayName: string;
|
|
|
+ pchEaten: ULONG;
|
|
|
+ ppidl: PItemIDList;
|
|
|
+ dwAttributes: ULONG;
|
|
|
+
|
|
|
+ constructor Create(AOperationEvent: TOperationEvent);
|
|
|
+ end;
|
|
|
+
|
|
|
+constructor TOperation.Create(AOperationEvent: TOperationEvent);
|
|
|
+begin
|
|
|
+ OperationEvent := AOperationEvent;
|
|
|
+end;
|
|
|
+
|
|
|
+type
|
|
|
+ TOperationWithTimeoutThread = class(TCompThread)
|
|
|
+ public
|
|
|
+ constructor Create;
|
|
|
+ destructor Destroy; override;
|
|
|
+ procedure Terminate; override;
|
|
|
+
|
|
|
+ procedure Queue(Operation: TOperation);
|
|
|
+ function WaitForOperation(Milliseconds: Cardinal): Boolean;
|
|
|
+ procedure Remove(Operation: TOperation);
|
|
|
+
|
|
|
+ protected
|
|
|
+ procedure Execute; override;
|
|
|
+
|
|
|
+ private
|
|
|
+ FCriticalSection: TCriticalSection;
|
|
|
+ FRequestEvent: THandle;
|
|
|
+ FResultEvent: THandle;
|
|
|
+ FQueue: TObjectList;
|
|
|
+ FResults: TObjectList;
|
|
|
+ end;
|
|
|
+
|
|
|
+constructor TOperationWithTimeoutThread.Create;
|
|
|
+begin
|
|
|
+ inherited Create(True);
|
|
|
+ FRequestEvent := CreateEvent(nil, False, False, nil);
|
|
|
+ FResultEvent := CreateEvent(nil, False, False, nil);
|
|
|
+ FCriticalSection := TCriticalSection.Create;
|
|
|
+ FQueue := TObjectList.Create;
|
|
|
+ FResults := TObjectList.Create;
|
|
|
+ Resume;
|
|
|
+end;
|
|
|
+
|
|
|
+destructor TOperationWithTimeoutThread.Destroy;
|
|
|
+begin
|
|
|
+ inherited;
|
|
|
+ FQueue.Free;
|
|
|
+ FResults.Free;
|
|
|
+ FCriticalSection.Free;
|
|
|
+ CloseHandle(FRequestEvent);
|
|
|
+ CloseHandle(FResultEvent);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TOperationWithTimeoutThread.Terminate;
|
|
|
+begin
|
|
|
+ inherited;
|
|
|
+ SetEvent(FRequestEvent);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TOperationWithTimeoutThread.Execute;
|
|
|
+var
|
|
|
+ Operation: TOperation;
|
|
|
+begin
|
|
|
+ while WaitForSingleObject(FRequestEvent, INFINITE) = WAIT_OBJECT_0 do
|
|
|
+ begin
|
|
|
+ if Terminated then
|
|
|
+ begin
|
|
|
+ break;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ FCriticalSection.Enter;
|
|
|
+ try
|
|
|
+ Operation := TOperation(FQueue[0]);
|
|
|
+ FQueue.Extract(Operation);
|
|
|
+ finally
|
|
|
+ FCriticalSection.Leave;
|
|
|
+ end;
|
|
|
+
|
|
|
+ Operation.OperationEvent(Operation);
|
|
|
+ FResults.Add(Operation);
|
|
|
+ SetEvent(FResultEvent);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TOperationWithTimeoutThread.Queue(Operation: TOperation);
|
|
|
+begin
|
|
|
+ FCriticalSection.Enter;
|
|
|
+ try
|
|
|
+ FQueue.Add(Operation);
|
|
|
+ finally
|
|
|
+ FCriticalSection.Leave;
|
|
|
+ end;
|
|
|
+
|
|
|
+ SetEvent(FRequestEvent);
|
|
|
+end;
|
|
|
+
|
|
|
+function TOperationWithTimeoutThread.WaitForOperation(Milliseconds: Cardinal): Boolean;
|
|
|
+begin
|
|
|
+ ResetEvent(FResultEvent);
|
|
|
+ Result := (WaitForSingleObject(FResultEvent, Milliseconds) = WAIT_OBJECT_0);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TOperationWithTimeoutThread.Remove(Operation: TOperation);
|
|
|
+begin
|
|
|
+ FCriticalSection.Enter;
|
|
|
+ try
|
|
|
+ FResults.Remove(Operation);
|
|
|
+ finally
|
|
|
+ FCriticalSection.Leave;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+var
|
|
|
+ Thread: TOperationWithTimeoutThread = nil;
|
|
|
+
|
|
|
+procedure NeedThread;
|
|
|
+begin
|
|
|
+ if not Assigned(Thread) then
|
|
|
+ begin
|
|
|
+ Thread := TOperationWithTimeoutThread.Create;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+function WaitForOperation(Milliseconds: Cardinal): Boolean;
|
|
|
+begin
|
|
|
+ Result := Thread.WaitForOperation(Milliseconds);
|
|
|
+ if not Result then
|
|
|
+ begin
|
|
|
+ // There's a chance for memory leak, if thread is terminated
|
|
|
+ // between WaitFor() and this line
|
|
|
+ Thread.FreeOnTerminate := True;
|
|
|
+ Thread.Terminate;
|
|
|
+ Thread := nil;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure ShellFolderGetAttributesOfOperation(Operation: TOperation);
|
|
|
+var
|
|
|
+ ErrorMode: Word;
|
|
|
+begin
|
|
|
+ ErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
|
|
|
+ try
|
|
|
+ Operation.ResultHResult := Operation.ShellFolder.GetAttributesOf(Operation.cidl, Operation.apidl, Operation.rgfInOut);
|
|
|
+ except
|
|
|
+ Operation.ResultHResult := E_FAIL;
|
|
|
+ end;
|
|
|
+ SetErrorMode(ErrorMode);
|
|
|
+end;
|
|
|
+
|
|
|
+function ShellFolderGetAttributesOfWithTimeout(
|
|
|
+ ShellFolder: IShellFolder; cidl: UINT; var apidl: PItemIDList; var rgfInOut: UINT; Timeout: Integer): HResult;
|
|
|
+var
|
|
|
+ Operation: TOperation;
|
|
|
+begin
|
|
|
+ NeedThread;
|
|
|
+ Operation := TOperation.Create(ShellFolderGetAttributesOfOperation);
|
|
|
+ Operation.ShellFolder := ShellFolder;
|
|
|
+ Operation.cidl := cidl;
|
|
|
+ Operation.apidl := apidl;
|
|
|
+ Operation.rgfInOut := rgfInOut;
|
|
|
+ Thread.Queue(Operation);
|
|
|
+ if WaitForOperation(Timeout) then
|
|
|
+ begin
|
|
|
+ apidl := Operation.apidl;
|
|
|
+ rgfInOut := Operation.rgfInOut;
|
|
|
+ Result := Operation.ResultHResult;
|
|
|
+ Thread.Remove(Operation);
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ rgfInOut := 0;
|
|
|
+ Result := E_FAIL;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure SHGetFileInfoForPidlOperation(Operation: TOperation);
|
|
|
+begin
|
|
|
+ Operation.ResultDWordPtr :=
|
|
|
+ SHGetFileInfo(PChar(Operation.PIDL), Operation.dwFileAttributes, Operation.psfi, Operation.cbFileInfo, Operation.uFlags);
|
|
|
+end;
|
|
|
+
|
|
|
+function SHGetFileInfoForPidlWithTimeout(
|
|
|
+ PIDL: PItemIDList; dwFileAttributes: DWORD; var psfi: TSHFileInfoW;
|
|
|
+ cbFileInfo, uFlags: UINT; Timeout: Integer): DWORD_PTR;
|
|
|
+var
|
|
|
+ Operation: TOperation;
|
|
|
+begin
|
|
|
+ NeedThread;
|
|
|
+ Operation := TOperation.Create(SHGetFileInfoForPidlOperation);
|
|
|
+ Operation.PIDL := PIDL;
|
|
|
+ Operation.dwFileAttributes := dwFileAttributes;
|
|
|
+ Operation.psfi := psfi;
|
|
|
+ Operation.cbFileInfo := cbFileInfo;
|
|
|
+ Operation.uFlags := uFlags;
|
|
|
+ Thread.Queue(Operation);
|
|
|
+ if WaitForOperation(Timeout) then
|
|
|
+ begin
|
|
|
+ psfi := Operation.psfi;
|
|
|
+ Result := Operation.ResultDWordPtr;
|
|
|
+ Thread.Remove(Operation);
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ FillChar(psfi, SizeOf(psfi), 0);
|
|
|
+ Result := 0;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure ShellFolderParseDisplayNameOperation(Operation: TOperation);
|
|
|
+begin
|
|
|
+ Operation.ResultHResult :=
|
|
|
+ Operation.ShellFolder.ParseDisplayName(
|
|
|
+ Operation.hwndOwner, Operation.pbcReserved, PChar(Operation.DisplayName),
|
|
|
+ Operation.pchEaten, Operation.ppidl, Operation.dwAttributes);
|
|
|
+end;
|
|
|
+
|
|
|
+function ShellFolderParseDisplayNameWithTimeout(
|
|
|
+ ShellFolder: IShellFolder; hwndOwner: HWND; pbcReserved: Pointer; lpszDisplayName: POLESTR; out pchEaten: ULONG;
|
|
|
+ out ppidl: PItemIDList; var dwAttributes: ULONG; Timeout: Integer): HResult;
|
|
|
+var
|
|
|
+ Operation: TOperation;
|
|
|
+begin
|
|
|
+ NeedThread;
|
|
|
+ Operation := TOperation.Create(ShellFolderParseDisplayNameOperation);
|
|
|
+ Operation.ShellFolder := ShellFolder;
|
|
|
+ Operation.hwndOwner := hwndOwner;
|
|
|
+ Operation.pbcReserved := pbcReserved;
|
|
|
+ Operation.DisplayName := lpszDisplayName;
|
|
|
+ Operation.pchEaten := 0;
|
|
|
+ Operation.ppidl := nil;
|
|
|
+ Operation.dwAttributes := 0;
|
|
|
+ Thread.Queue(Operation);
|
|
|
+ if WaitForOperation(Timeout) then
|
|
|
+ begin
|
|
|
+ ppidl := Operation.ppidl;
|
|
|
+ dwAttributes := Operation.dwAttributes;
|
|
|
+ Result := Operation.ResultHResult;
|
|
|
+ Thread.Remove(Operation);
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ ppidl := nil;
|
|
|
+ dwAttributes := 0;
|
|
|
+ Result := E_FAIL;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+initialization
|
|
|
+
|
|
|
+finalization
|
|
|
+ if Assigned(Thread) then
|
|
|
+ begin
|
|
|
+ Thread.Free;
|
|
|
+ end;
|
|
|
+end.
|