| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- 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 SHGetFileInfoWithTimeout(
- pszPath: LPCWSTR; 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;
- Path: string;
- 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
- // Needed for various API, particularly:
- // - SHGetFileInfo fails to return icon index on some systems;
- // - ICustomDestinationList.BeginList returns invalid "removed" array.
- CoInitialize(nil);
- 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 SHGetFileInfoOperation(Operation: TOperation);
- var
- pszPath: LPCWSTR;
- begin
- if Operation.uFlags and SHGFI_PIDL <> 0 then
- begin
- pszPath := LPCWSTR(Operation.PIDL);
- end
- else
- begin
- pszPath := LPCWSTR(Operation.Path);
- end;
- Operation.ResultDWordPtr :=
- SHGetFileInfo(pszPath, Operation.dwFileAttributes, Operation.psfi, Operation.cbFileInfo, Operation.uFlags);
- end;
- function SHGetFileInfoWithTimeout(
- pszPath: LPCWSTR; dwFileAttributes: DWORD; var psfi: TSHFileInfoW;
- cbFileInfo, uFlags: UINT; Timeout: Integer): DWORD_PTR;
- var
- Operation: TOperation;
- begin
- NeedThread;
- Operation := TOperation.Create(SHGetFileInfoOperation);
- if uFlags and SHGFI_PIDL <> 0 then
- begin
- Operation.PIDL := PItemIDList(pszPath);
- end
- else
- begin
- Operation.Path := pszPath;
- end;
- 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.
|