OperationWithTimeout.pas 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. unit OperationWithTimeout;
  2. interface
  3. uses
  4. Winapi.Windows, Winapi.ShlObj, Winapi.ShellAPI, ActiveX;
  5. function ShellFolderGetAttributesOfWithTimeout(
  6. ShellFolder: IShellFolder; cidl: UINT; var apidl: PItemIDList; var rgfInOut: UINT; Timeout: Integer): HResult;
  7. function SHGetFileInfoWithTimeout(
  8. pszPath: LPCWSTR; dwFileAttributes: DWORD; var psfi: TSHFileInfoW;
  9. cbFileInfo, uFlags: UINT; Timeout: Integer): DWORD_PTR;
  10. function ShellFolderParseDisplayNameWithTimeout(
  11. ShellFolder: IShellFolder; hwndOwner: HWND; pbcReserved: Pointer; lpszDisplayName: POLESTR;
  12. out pchEaten: ULONG; out ppidl: PItemIDList; var dwAttributes: ULONG; Timeout: Integer): HResult;
  13. implementation
  14. uses
  15. System.Classes, System.Types, System.SysUtils, System.SyncObjs, System.Contnrs, CompThread;
  16. type
  17. TOperation = class;
  18. TOperationEvent = procedure(Operation: TOperation);
  19. TOperation = class(TObject)
  20. public
  21. OperationEvent: TOperationEvent;
  22. // ShellFolderGetAttributesOfWithTimeout
  23. ShellFolder: IShellFolder;
  24. cidl: UINT;
  25. apidl: PItemIDList;
  26. rgfInOut: UINT;
  27. Timeout: Integer;
  28. ResultHResult: HResult;
  29. // SHGetFileInfoWithTimeout
  30. PIDL: PItemIDList;
  31. Path: string;
  32. dwFileAttributes: DWORD;
  33. psfi: TSHFileInfoW;
  34. cbFileInfo, uFlags: UINT;
  35. ResultDWordPtr: DWORD_PTR;
  36. // ShellFolderParseDisplayNameWithTimeout
  37. // Uses ShellFolder and ResultHResult
  38. hwndOwner: HWND;
  39. pbcReserved: Pointer;
  40. DisplayName: string;
  41. pchEaten: ULONG;
  42. ppidl: PItemIDList;
  43. dwAttributes: ULONG;
  44. constructor Create(AOperationEvent: TOperationEvent);
  45. end;
  46. constructor TOperation.Create(AOperationEvent: TOperationEvent);
  47. begin
  48. OperationEvent := AOperationEvent;
  49. end;
  50. type
  51. TOperationWithTimeoutThread = class(TCompThread)
  52. public
  53. constructor Create;
  54. destructor Destroy; override;
  55. procedure Terminate; override;
  56. procedure Queue(Operation: TOperation);
  57. function WaitForOperation(Milliseconds: Cardinal): Boolean;
  58. procedure Remove(Operation: TOperation);
  59. protected
  60. procedure Execute; override;
  61. private
  62. FCriticalSection: TCriticalSection;
  63. FRequestEvent: THandle;
  64. FResultEvent: THandle;
  65. FQueue: TObjectList;
  66. FResults: TObjectList;
  67. end;
  68. constructor TOperationWithTimeoutThread.Create;
  69. begin
  70. inherited Create(True);
  71. FRequestEvent := CreateEvent(nil, False, False, nil);
  72. FResultEvent := CreateEvent(nil, False, False, nil);
  73. FCriticalSection := TCriticalSection.Create;
  74. FQueue := TObjectList.Create;
  75. FResults := TObjectList.Create;
  76. Resume;
  77. end;
  78. destructor TOperationWithTimeoutThread.Destroy;
  79. begin
  80. inherited;
  81. FQueue.Free;
  82. FResults.Free;
  83. FCriticalSection.Free;
  84. CloseHandle(FRequestEvent);
  85. CloseHandle(FResultEvent);
  86. end;
  87. procedure TOperationWithTimeoutThread.Terminate;
  88. begin
  89. inherited;
  90. SetEvent(FRequestEvent);
  91. end;
  92. procedure TOperationWithTimeoutThread.Execute;
  93. var
  94. Operation: TOperation;
  95. begin
  96. while WaitForSingleObject(FRequestEvent, INFINITE) = WAIT_OBJECT_0 do
  97. begin
  98. if Terminated then
  99. begin
  100. break;
  101. end
  102. else
  103. begin
  104. FCriticalSection.Enter;
  105. try
  106. Operation := TOperation(FQueue[0]);
  107. FQueue.Extract(Operation);
  108. finally
  109. FCriticalSection.Leave;
  110. end;
  111. Operation.OperationEvent(Operation);
  112. FResults.Add(Operation);
  113. SetEvent(FResultEvent);
  114. end;
  115. end;
  116. end;
  117. procedure TOperationWithTimeoutThread.Queue(Operation: TOperation);
  118. begin
  119. FCriticalSection.Enter;
  120. try
  121. FQueue.Add(Operation);
  122. finally
  123. FCriticalSection.Leave;
  124. end;
  125. SetEvent(FRequestEvent);
  126. end;
  127. function TOperationWithTimeoutThread.WaitForOperation(Milliseconds: Cardinal): Boolean;
  128. begin
  129. ResetEvent(FResultEvent);
  130. Result := (WaitForSingleObject(FResultEvent, Milliseconds) = WAIT_OBJECT_0);
  131. end;
  132. procedure TOperationWithTimeoutThread.Remove(Operation: TOperation);
  133. begin
  134. FCriticalSection.Enter;
  135. try
  136. FResults.Remove(Operation);
  137. finally
  138. FCriticalSection.Leave;
  139. end;
  140. end;
  141. var
  142. Thread: TOperationWithTimeoutThread = nil;
  143. procedure NeedThread;
  144. begin
  145. if not Assigned(Thread) then
  146. begin
  147. Thread := TOperationWithTimeoutThread.Create;
  148. end;
  149. end;
  150. function WaitForOperation(Milliseconds: Cardinal): Boolean;
  151. begin
  152. Result := Thread.WaitForOperation(Milliseconds);
  153. if not Result then
  154. begin
  155. // There's a chance for memory leak, if thread is terminated
  156. // between WaitFor() and this line
  157. Thread.FreeOnTerminate := True;
  158. Thread.Terminate;
  159. Thread := nil;
  160. end;
  161. end;
  162. procedure ShellFolderGetAttributesOfOperation(Operation: TOperation);
  163. var
  164. ErrorMode: Word;
  165. begin
  166. ErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
  167. try
  168. Operation.ResultHResult := Operation.ShellFolder.GetAttributesOf(Operation.cidl, Operation.apidl, Operation.rgfInOut);
  169. except
  170. Operation.ResultHResult := E_FAIL;
  171. end;
  172. SetErrorMode(ErrorMode);
  173. end;
  174. function ShellFolderGetAttributesOfWithTimeout(
  175. ShellFolder: IShellFolder; cidl: UINT; var apidl: PItemIDList; var rgfInOut: UINT; Timeout: Integer): HResult;
  176. var
  177. Operation: TOperation;
  178. begin
  179. NeedThread;
  180. Operation := TOperation.Create(ShellFolderGetAttributesOfOperation);
  181. Operation.ShellFolder := ShellFolder;
  182. Operation.cidl := cidl;
  183. Operation.apidl := apidl;
  184. Operation.rgfInOut := rgfInOut;
  185. Thread.Queue(Operation);
  186. if WaitForOperation(Timeout) then
  187. begin
  188. apidl := Operation.apidl;
  189. rgfInOut := Operation.rgfInOut;
  190. Result := Operation.ResultHResult;
  191. Thread.Remove(Operation);
  192. end
  193. else
  194. begin
  195. rgfInOut := 0;
  196. Result := E_FAIL;
  197. end;
  198. end;
  199. procedure SHGetFileInfoOperation(Operation: TOperation);
  200. var
  201. pszPath: LPCWSTR;
  202. begin
  203. if Operation.uFlags and SHGFI_PIDL <> 0 then
  204. begin
  205. pszPath := LPCWSTR(Operation.PIDL);
  206. end
  207. else
  208. begin
  209. pszPath := LPCWSTR(Operation.Path);
  210. end;
  211. Operation.ResultDWordPtr :=
  212. SHGetFileInfo(pszPath, Operation.dwFileAttributes, Operation.psfi, Operation.cbFileInfo, Operation.uFlags);
  213. end;
  214. function SHGetFileInfoWithTimeout(
  215. pszPath: LPCWSTR; dwFileAttributes: DWORD; var psfi: TSHFileInfoW;
  216. cbFileInfo, uFlags: UINT; Timeout: Integer): DWORD_PTR;
  217. var
  218. Operation: TOperation;
  219. begin
  220. NeedThread;
  221. Operation := TOperation.Create(SHGetFileInfoOperation);
  222. if uFlags and SHGFI_PIDL <> 0 then
  223. begin
  224. Operation.PIDL := PItemIDList(pszPath);
  225. end
  226. else
  227. begin
  228. Operation.Path := pszPath;
  229. end;
  230. Operation.dwFileAttributes := dwFileAttributes;
  231. Operation.psfi := psfi;
  232. Operation.cbFileInfo := cbFileInfo;
  233. Operation.uFlags := uFlags;
  234. Thread.Queue(Operation);
  235. if WaitForOperation(Timeout) then
  236. begin
  237. psfi := Operation.psfi;
  238. Result := Operation.ResultDWordPtr;
  239. Thread.Remove(Operation);
  240. end
  241. else
  242. begin
  243. FillChar(psfi, SizeOf(psfi), 0);
  244. Result := 0;
  245. end;
  246. end;
  247. procedure ShellFolderParseDisplayNameOperation(Operation: TOperation);
  248. begin
  249. Operation.ResultHResult :=
  250. Operation.ShellFolder.ParseDisplayName(
  251. Operation.hwndOwner, Operation.pbcReserved, PChar(Operation.DisplayName),
  252. Operation.pchEaten, Operation.ppidl, Operation.dwAttributes);
  253. end;
  254. function ShellFolderParseDisplayNameWithTimeout(
  255. ShellFolder: IShellFolder; hwndOwner: HWND; pbcReserved: Pointer; lpszDisplayName: POLESTR; out pchEaten: ULONG;
  256. out ppidl: PItemIDList; var dwAttributes: ULONG; Timeout: Integer): HResult;
  257. var
  258. Operation: TOperation;
  259. begin
  260. NeedThread;
  261. Operation := TOperation.Create(ShellFolderParseDisplayNameOperation);
  262. Operation.ShellFolder := ShellFolder;
  263. Operation.hwndOwner := hwndOwner;
  264. Operation.pbcReserved := pbcReserved;
  265. Operation.DisplayName := lpszDisplayName;
  266. Operation.pchEaten := 0;
  267. Operation.ppidl := nil;
  268. Operation.dwAttributes := 0;
  269. Thread.Queue(Operation);
  270. if WaitForOperation(Timeout) then
  271. begin
  272. ppidl := Operation.ppidl;
  273. dwAttributes := Operation.dwAttributes;
  274. Result := Operation.ResultHResult;
  275. Thread.Remove(Operation);
  276. end
  277. else
  278. begin
  279. ppidl := nil;
  280. dwAttributes := 0;
  281. Result := E_FAIL;
  282. end;
  283. end;
  284. initialization
  285. finalization
  286. if Assigned(Thread) then
  287. begin
  288. Thread.Free;
  289. end;
  290. end.