JclHookExcept.pas 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. {**************************************************************************************************}
  2. { }
  3. { Project JEDI Code Library (JCL) }
  4. { }
  5. { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
  6. { you may not use this file except in compliance with the License. You may obtain a copy of the }
  7. { License at http://www.mozilla.org/MPL/ }
  8. { }
  9. { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
  10. { ANY KIND, either express or implied. See the License for the specific language governing rights }
  11. { and limitations under the License. }
  12. { }
  13. { The Original Code is JclHookExcept.pas. }
  14. { }
  15. { The Initial Developer of the Original Code is Petr Vones. Portions created by Petr Vones are }
  16. { Copyright (C) Petr Vones. All Rights Reserved. }
  17. { }
  18. { Contributor(s): }
  19. { Petr Vones (pvones) }
  20. { Robert Marquardt (marquardt) }
  21. { Andreas Hausladen (ahuser) }
  22. { }
  23. {**************************************************************************************************}
  24. { }
  25. { Exception hooking routines }
  26. { }
  27. {**************************************************************************************************}
  28. { }
  29. { Last modified: $Date:: $ }
  30. { Revision: $Rev:: $ }
  31. { Author: $Author:: $ }
  32. { }
  33. {**************************************************************************************************}
  34. unit JclHookExcept;
  35. interface
  36. {$I jcl.inc}
  37. {$I windowsonly.inc}
  38. uses
  39. {$IFDEF UNITVERSIONING}
  40. JclUnitVersioning,
  41. {$ENDIF UNITVERSIONING}
  42. {$IFDEF HAS_UNITSCOPE}
  43. Winapi.Windows, System.SysUtils, System.Classes;
  44. {$ELSE ~HAS_UNITSCOPE}
  45. Windows, SysUtils, Classes;
  46. {$ENDIF ~HAS_UNITSCOPE}
  47. type
  48. // Exception hooking notifiers routines
  49. {$IFDEF BORLAND}
  50. TJclExceptFilterProc = function(ExceptRecord: PExceptionRecord): Exception;
  51. {$ENDIF BORLAND}
  52. TJclExceptNotifyProc = procedure(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean);
  53. TJclExceptNotifyProcEx = procedure(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean; StackPointer: Pointer);
  54. TJclExceptNotifyMethod = procedure(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean) of object;
  55. TJclExceptNotifyPriority = (npNormal, npFirstChain);
  56. {$IFDEF BORLAND}
  57. function JclAddExceptFilter(const FilterProc: TJclExceptFilterProc; Priority: TJclExceptNotifyPriority = npNormal): Boolean;
  58. {$ENDIF BORLAND}
  59. function JclAddExceptNotifier(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority = npNormal): Boolean; overload;
  60. function JclAddExceptNotifier(const NotifyProc: TJclExceptNotifyProcEx; Priority: TJclExceptNotifyPriority = npNormal): Boolean; overload;
  61. function JclAddExceptNotifier(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority = npNormal): Boolean; overload;
  62. {$IFDEF BORLAND}
  63. function JclRemoveExceptFilter(const FilterProc: TJclExceptFilterProc): Boolean;
  64. {$ENDIF BORLAND}
  65. function JclRemoveExceptNotifier(const NotifyProc: TJclExceptNotifyProc): Boolean; overload;
  66. function JclRemoveExceptNotifier(const NotifyProc: TJclExceptNotifyProcEx): Boolean; overload;
  67. function JclRemoveExceptNotifier(const NotifyMethod: TJclExceptNotifyMethod): Boolean; overload;
  68. procedure JclReplaceExceptObj(NewExceptObj: Exception);
  69. // Exception hooking routines
  70. function JclHookExceptions: Boolean;
  71. function JclUnhookExceptions: Boolean;
  72. function JclExceptionsHooked: Boolean;
  73. function JclHookExceptionsInModule(Module: HMODULE): Boolean;
  74. function JclUnhookExceptionsInModule(Module: HMODULE): Boolean;
  75. // Exceptions hooking in libraries
  76. type
  77. TJclModuleArray = array of HMODULE;
  78. function JclInitializeLibrariesHookExcept: Boolean;
  79. function JclHookedExceptModulesList(out ModulesList: TJclModuleArray): Boolean;
  80. // Hooking routines location info helper
  81. function JclBelongsHookedCode(Address: Pointer): Boolean;
  82. {$IFDEF UNITVERSIONING}
  83. const
  84. UnitVersioning: TUnitVersionInfo = (
  85. RCSfile: '$URL$';
  86. Revision: '$Revision$';
  87. Date: '$Date$';
  88. LogPath: 'JCL\source\windows';
  89. Extra: '';
  90. Data: nil
  91. );
  92. {$ENDIF UNITVERSIONING}
  93. implementation
  94. uses
  95. JclBase,
  96. JclPeImage,
  97. JclSysInfo, JclSysUtils;
  98. type
  99. PExceptionArguments = ^TExceptionArguments;
  100. TExceptionArguments = record
  101. ExceptAddr: Pointer;
  102. ExceptObj: Exception;
  103. end;
  104. {$IFDEF BORLAND}
  105. TFilterItem = class(TObject)
  106. private
  107. FExceptFilterProc: TJclExceptFilterProc;
  108. FPriority: TJclExceptNotifyPriority;
  109. public
  110. constructor Create(const ExceptFilterProc: TJclExceptFilterProc; APriority: TJclExceptNotifyPriority);
  111. function DoFilterException(ExceptRecord: PExceptionRecord; out ExceptObj: Exception): Boolean;
  112. property Priority: TJclExceptNotifyPriority read FPriority;
  113. end;
  114. {$ENDIF BORLAND}
  115. TNotifierItem = class(TObject)
  116. private
  117. FNotifyMethod: TJclExceptNotifyMethod;
  118. FNotifyProc: TJclExceptNotifyProc;
  119. FNotifyProcEx: TJclExceptNotifyProcEx;
  120. FPriority: TJclExceptNotifyPriority;
  121. public
  122. constructor Create(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority); overload;
  123. constructor Create(const NotifyProc: TJclExceptNotifyProcEx; Priority: TJclExceptNotifyPriority); overload;
  124. constructor Create(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority); overload;
  125. procedure DoNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean; StackPointer: Pointer);
  126. property Priority: TJclExceptNotifyPriority read FPriority;
  127. end;
  128. var
  129. ExceptionsHooked: Boolean;
  130. Kernel32_RaiseException: procedure (dwExceptionCode, dwExceptionFlags,
  131. nNumberOfArguments: DWORD; lpArguments: PDWORD); stdcall;
  132. {$IFDEF BORLAND}
  133. SysUtils_ExceptObjProc: function (P: PExceptionRecord): Exception;
  134. {$ENDIF BORLAND}
  135. {$IFDEF FPC}
  136. SysUtils_ExceptProc: TExceptProc;
  137. {$ENDIF FPC}
  138. Notifiers: TThreadList;
  139. {$IFDEF BORLAND}
  140. Filters: TThreadList;
  141. {$ENDIF BORLAND}
  142. {$IFDEF HOOK_DLL_EXCEPTIONS}
  143. const
  144. JclHookExceptDebugHookName = '__JclHookExcept';
  145. type
  146. TJclHookExceptDebugHook = procedure(Module: HMODULE; Hook: Boolean); stdcall;
  147. TJclHookExceptModuleList = class(TObject)
  148. private
  149. FModules: TThreadList;
  150. protected
  151. procedure HookStaticModules;
  152. public
  153. constructor Create;
  154. destructor Destroy; override;
  155. class function JclHookExceptDebugHookAddr: Pointer;
  156. procedure HookModule(Module: HMODULE);
  157. procedure List(out ModulesList: TJclModuleArray);
  158. procedure UnhookModule(Module: HMODULE);
  159. end;
  160. var
  161. HookExceptModuleList: TJclHookExceptModuleList;
  162. JclHookExceptDebugHook: Pointer;
  163. exports
  164. JclHookExceptDebugHook name JclHookExceptDebugHookName;
  165. {$ENDIF HOOK_DLL_EXCEPTIONS}
  166. {$STACKFRAMES OFF}
  167. threadvar
  168. Recursive: Boolean;
  169. NewResultExc: Exception;
  170. //=== Helper routines ========================================================
  171. function RaiseExceptionAddress: Pointer;
  172. begin
  173. Result := GetProcAddress(GetModuleHandle(kernel32), 'RaiseException');
  174. Assert(Result <> nil);
  175. end;
  176. procedure FreeThreadObjList(var TheList: TThreadList);
  177. var
  178. I: Integer;
  179. begin
  180. with TheList.LockList do
  181. try
  182. for I := 0 to Count - 1 do
  183. TObject(Items[I]).Free;
  184. finally
  185. TheList.UnlockList;
  186. end;
  187. FreeAndNil(TheList);
  188. end;
  189. //=== { TFilterItem } ========================================================
  190. {$IFDEF BORLAND}
  191. constructor TFilterItem.Create(const ExceptFilterProc: TJclExceptFilterProc; APriority: TJclExceptNotifyPriority);
  192. begin
  193. FExceptFilterProc := ExceptFilterProc;
  194. FPriority := APriority;
  195. end;
  196. function TFilterItem.DoFilterException(ExceptRecord: PExceptionRecord; out ExceptObj: Exception): Boolean;
  197. begin
  198. if Assigned(FExceptFilterProc) then
  199. begin
  200. ExceptObj := FExceptFilterProc(ExceptRecord);
  201. Result := ExceptObj <> nil;
  202. end
  203. else
  204. Result := False;
  205. end;
  206. {$ENDIF BORLAND}
  207. //=== { TNotifierItem } ======================================================
  208. constructor TNotifierItem.Create(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority);
  209. begin
  210. inherited Create;
  211. FNotifyProc := NotifyProc;
  212. FPriority := Priority;
  213. end;
  214. constructor TNotifierItem.Create(const NotifyProc: TJclExceptNotifyProcEx; Priority: TJclExceptNotifyPriority);
  215. begin
  216. inherited Create;
  217. FNotifyProcEx := NotifyProc;
  218. FPriority := Priority;
  219. end;
  220. constructor TNotifierItem.Create(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority);
  221. begin
  222. inherited Create;
  223. FNotifyMethod := NotifyMethod;
  224. FPriority := Priority;
  225. end;
  226. procedure TNotifierItem.DoNotify(ExceptObj: TObject; ExceptAddr: Pointer;
  227. OSException: Boolean; StackPointer: Pointer);
  228. begin
  229. if Assigned(FNotifyProc) then
  230. FNotifyProc(ExceptObj, ExceptAddr, OSException)
  231. else
  232. if Assigned(FNotifyProcEx) then
  233. FNotifyProcEx(ExceptObj, ExceptAddr, OSException, StackPointer)
  234. else
  235. if Assigned(FNotifyMethod) then
  236. FNotifyMethod(ExceptObj, ExceptAddr, OSException);
  237. end;
  238. function GetFramePointer: Pointer;
  239. asm
  240. {$IFDEF CPU32}
  241. MOV EAX, EBP
  242. {$ENDIF CPU32}
  243. {$IFDEF CPU64}
  244. MOV RAX, RBP
  245. {$ENDIF CPU64}
  246. end;
  247. {$STACKFRAMES ON}
  248. {$IFDEF BORLAND}
  249. function DoExceptFilter(ExceptRecord: PExceptionRecord): Exception;
  250. var
  251. Priorities: TJclExceptNotifyPriority;
  252. I: Integer;
  253. begin
  254. if Recursive then
  255. Exit;
  256. if Assigned(Filters) then
  257. begin
  258. Recursive := True;
  259. try
  260. with Filters.LockList do
  261. try
  262. for Priorities := High(Priorities) downto Low(Priorities) do
  263. for I := 0 to Count - 1 do
  264. with TFilterItem(Items[I]) do
  265. if Priority = Priorities then
  266. if DoFilterException(ExceptRecord, Result) then
  267. Exit;
  268. finally
  269. Filters.UnlockList;
  270. end;
  271. // Nobody wanted to handle the external exception. Call the default handler.
  272. Result := SysUtils_ExceptObjProc(ExceptRecord);
  273. finally
  274. Recursive := False;
  275. end;
  276. end;
  277. end;
  278. {$ENDIF BORLAND}
  279. procedure DoExceptNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean; StackPointer: Pointer);
  280. var
  281. Priorities: TJclExceptNotifyPriority;
  282. I: Integer;
  283. begin
  284. if Recursive then
  285. Exit;
  286. if Assigned(Notifiers) then
  287. begin
  288. Recursive := True;
  289. NewResultExc := nil;
  290. try
  291. with Notifiers.LockList do
  292. try
  293. if Count = 1 then
  294. begin
  295. with TNotifierItem(Items[0]) do
  296. DoNotify( ExceptObj, ExceptAddr, OSException, StackPointer);
  297. end
  298. else
  299. begin
  300. for Priorities := High(Priorities) downto Low(Priorities) do
  301. for I := 0 to Count - 1 do
  302. with TNotifierItem(Items[I]) do
  303. if Priority = Priorities then
  304. DoNotify(ExceptObj, ExceptAddr, OSException, StackPointer);
  305. end;
  306. finally
  307. Notifiers.UnlockList;
  308. end;
  309. finally
  310. Recursive := False;
  311. end;
  312. end;
  313. end;
  314. procedure HookedRaiseException(ExceptionCode, ExceptionFlags, NumberOfArguments: DWORD;
  315. Arguments: PExceptionArguments); stdcall;
  316. const
  317. cDelphiException = $0EEDFADE;
  318. cNonContinuable = 1; // Delphi exceptions
  319. cNonContinuableException = $C0000025; // C++Builder exceptions (sounds like a bug)
  320. DelphiNumberOfArguments = 7;
  321. CBuilderNumberOfArguments = 8;
  322. begin
  323. if ((ExceptionFlags = cNonContinuable) or (ExceptionFlags = cNonContinuableException)) and
  324. (ExceptionCode = cDelphiException) and
  325. (NumberOfArguments in [DelphiNumberOfArguments, CBuilderNumberOfArguments])
  326. //TODO: The difference for Win64 is bigger than 100 Byte and the comment of JVCS revision 0.3 of
  327. // JclDebug.pas, where HookedRaiseException has been added by Petr, isn't very informative
  328. {$IFDEF CPU32}
  329. and (TJclAddr(Arguments) = TJclAddr(@Arguments) + SizeOf(Pointer))
  330. {$ENDIF CPU32}
  331. then
  332. begin
  333. DoExceptNotify(Arguments.ExceptObj, Arguments.ExceptAddr, False, GetFramePointer);
  334. end;
  335. Kernel32_RaiseException(ExceptionCode, ExceptionFlags, NumberOfArguments, PDWORD(Arguments));
  336. end;
  337. {$IFDEF BORLAND}
  338. function HookedExceptObjProc(P: PExceptionRecord): Exception;
  339. var
  340. NewResultExcCache: Exception; // TLS optimization
  341. begin
  342. Result := DoExceptFilter(P);
  343. DoExceptNotify(Result, P^.ExceptionAddress, True, GetFramePointer);
  344. NewResultExcCache := NewResultExc;
  345. if NewResultExcCache <> nil then
  346. Result := NewResultExcCache;
  347. end;
  348. {$ENDIF BORLAND}
  349. {$IFDEF FPC}
  350. procedure HookedExceptProc(Obj : TObject; Addr : Pointer; FrameCount:Longint; Frame: PPointer);
  351. var
  352. NewResultExcCache: Exception; // TLS optimization
  353. begin
  354. DoExceptNotify(Obj, Addr, True, GetFramePointer);
  355. NewResultExcCache := NewResultExc;
  356. if NewResultExcCache <> nil then
  357. SysUtils_ExceptProc(NewResultExcCache, Addr, FrameCount, Frame)
  358. else
  359. SysUtils_ExceptProc(Obj, Addr, FrameCount, Frame)
  360. end;
  361. {$ENDIF FPC}
  362. {$IFNDEF STACKFRAMES_ON}
  363. {$STACKFRAMES OFF}
  364. {$ENDIF ~STACKFRAMES_ON}
  365. // Do not change ordering of HookedRaiseException, HookedExceptObjProc and JclBelongsHookedCode routines
  366. function JclBelongsHookedCode(Address: Pointer): Boolean;
  367. begin
  368. Result := (TJclAddr(@HookedRaiseException) < TJclAddr(@JclBelongsHookedCode)) and
  369. (TJclAddr(@HookedRaiseException) <= TJclAddr(Address)) and
  370. (TJclAddr(@JclBelongsHookedCode) > TJclAddr(Address));
  371. end;
  372. {$IFDEF BORLAND}
  373. function JclAddExceptFilter(const FilterProc: TJclExceptFilterProc; Priority: TJclExceptNotifyPriority = npNormal): Boolean;
  374. begin
  375. Result := Assigned(FilterProc);
  376. if Result then
  377. with Filters.LockList do
  378. try
  379. Add(TFilterItem.Create(FilterProc, Priority));
  380. finally
  381. Filters.UnlockList;
  382. end;
  383. end;
  384. {$ENDIF BORLAND}
  385. function JclAddExceptNotifier(const NotifyProc: TJclExceptNotifyProc; Priority: TJclExceptNotifyPriority): Boolean;
  386. begin
  387. Result := Assigned(NotifyProc);
  388. if Result then
  389. with Notifiers.LockList do
  390. try
  391. Add(TNotifierItem.Create(NotifyProc, Priority));
  392. finally
  393. Notifiers.UnlockList;
  394. end;
  395. end;
  396. function JclAddExceptNotifier(const NotifyProc: TJclExceptNotifyProcEx; Priority: TJclExceptNotifyPriority): Boolean;
  397. begin
  398. Result := Assigned(NotifyProc);
  399. if Result then
  400. with Notifiers.LockList do
  401. try
  402. Add(TNotifierItem.Create(NotifyProc, Priority));
  403. finally
  404. Notifiers.UnlockList;
  405. end;
  406. end;
  407. function JclAddExceptNotifier(const NotifyMethod: TJclExceptNotifyMethod; Priority: TJclExceptNotifyPriority): Boolean;
  408. begin
  409. Result := Assigned(NotifyMethod);
  410. if Result then
  411. with Notifiers.LockList do
  412. try
  413. Add(TNotifierItem.Create(NotifyMethod, Priority));
  414. finally
  415. Notifiers.UnlockList;
  416. end;
  417. end;
  418. {$IFDEF BORLAND}
  419. function JclRemoveExceptFilter(const FilterProc: TJclExceptFilterProc): Boolean;
  420. var
  421. O: TFilterItem;
  422. I: Integer;
  423. begin
  424. Result := Assigned(FilterProc);
  425. if Result then
  426. with Filters.LockList do
  427. try
  428. for I := 0 to Count - 1 do
  429. begin
  430. O := TFilterItem(Items[I]);
  431. if @O.FExceptFilterProc = @FilterProc then
  432. begin
  433. O.Free;
  434. Items[I] := nil;
  435. end;
  436. end;
  437. Pack;
  438. finally
  439. Filters.UnlockList;
  440. end;
  441. end;
  442. {$ENDIF BORLAND}
  443. function JclRemoveExceptNotifier(const NotifyProc: TJclExceptNotifyProc): Boolean;
  444. var
  445. O: TNotifierItem;
  446. I: Integer;
  447. begin
  448. Result := Assigned(NotifyProc);
  449. if Result then
  450. with Notifiers.LockList do
  451. try
  452. for I := 0 to Count - 1 do
  453. begin
  454. O := TNotifierItem(Items[I]);
  455. if @O.FNotifyProc = @NotifyProc then
  456. begin
  457. O.Free;
  458. Items[I] := nil;
  459. end;
  460. end;
  461. Pack;
  462. finally
  463. Notifiers.UnlockList;
  464. end;
  465. end;
  466. function JclRemoveExceptNotifier(const NotifyProc: TJclExceptNotifyProcEx): Boolean;
  467. var
  468. O: TNotifierItem;
  469. I: Integer;
  470. begin
  471. Result := Assigned(NotifyProc);
  472. if Result then
  473. with Notifiers.LockList do
  474. try
  475. for I := 0 to Count - 1 do
  476. begin
  477. O := TNotifierItem(Items[I]);
  478. if @O.FNotifyProcEx = @NotifyProc then
  479. begin
  480. O.Free;
  481. Items[I] := nil;
  482. end;
  483. end;
  484. Pack;
  485. finally
  486. Notifiers.UnlockList;
  487. end;
  488. end;
  489. function JclRemoveExceptNotifier(const NotifyMethod: TJclExceptNotifyMethod): Boolean;
  490. var
  491. O: TNotifierItem;
  492. I: Integer;
  493. begin
  494. Result := Assigned(NotifyMethod);
  495. if Result then
  496. with Notifiers.LockList do
  497. try
  498. for I := 0 to Count - 1 do
  499. begin
  500. O := TNotifierItem(Items[I]);
  501. if (TMethod(O.FNotifyMethod).Code = TMethod(NotifyMethod).Code) and
  502. (TMethod(O.FNotifyMethod).Data = TMethod(NotifyMethod).Data) then
  503. begin
  504. O.Free;
  505. Items[I] := nil;
  506. end;
  507. end;
  508. Pack;
  509. finally
  510. Notifiers.UnlockList;
  511. end;
  512. end;
  513. procedure JclReplaceExceptObj(NewExceptObj: Exception);
  514. begin
  515. Assert(Recursive);
  516. NewResultExc := NewExceptObj;
  517. end;
  518. {$IFDEF BORLAND}
  519. function GetCppRtlBase: Pointer;
  520. const
  521. {$IFDEF COMPILER6} { Delphi/C++Builder 6 }
  522. CppRtlVersion = 60;
  523. {$ELSE ~COMPILER6}
  524. {$IFDEF RTL185} { Delphi/C++Builder 2007 were aiming for
  525. binary compatibility with BDS2006, which
  526. complicates things a bit }
  527. CppRtlVersion = 80;
  528. {$ELSE ~RTL185}
  529. { Successive RTLDLL version numbers in the remaining cases: CB2006 has cc3270mt.dll,
  530. CB2009 (= CB2006 + 2 releases) has cc3290mt.dll, CB2010 has cc32100mt.dll etc. }
  531. CppRtlVersion = 70 + Trunc(RtlVersion - 18.0) * 10;
  532. {$ENDIF ~RTL185}
  533. {$ENDIF ~COMPILER6}
  534. begin
  535. Result := Pointer(GetModuleHandle(PChar(Format('cc32%dmt.dll', [CppRtlVersion]))));
  536. { 'Result = nil' means that the C++ RTL has been linked statically or is not available at all;
  537. in this case TJclPeMapImgHooks.ReplaceImport() is a no-op. The base module is also being
  538. hooked separately, so we're covered. }
  539. end;
  540. function HasCppRtl: Boolean;
  541. begin
  542. Result := GetCppRtlBase <> TJclPeMapImgHooks.SystemBase;
  543. end;
  544. {$ENDIF BORLAND}
  545. function JclHookExceptions: Boolean;
  546. var
  547. RaiseExceptionAddressCache: Pointer;
  548. begin
  549. RaiseExceptionAddressCache := RaiseExceptionAddress;
  550. { Detect C++Builder applications and C++ packages loaded into Delphi applications.
  551. Hook the C++ RTL regardless of ExceptionsHooked so that users can call JclHookException() after
  552. loading a C++ package which might pull in the C++ RTL DLL. }
  553. {$IFDEF BORLAND}
  554. if HasCppRtl then
  555. TJclPeMapImgHooks.ReplaceImport(GetCppRtlBase, kernel32, RaiseExceptionAddressCache, @HookedRaiseException);
  556. {$ENDIF BORLAND}
  557. if not ExceptionsHooked then
  558. begin
  559. Recursive := False;
  560. with TJclPeMapImgHooks do
  561. Result := ReplaceImport(SystemBase, kernel32, RaiseExceptionAddressCache, @HookedRaiseException);
  562. if Result then
  563. begin
  564. @Kernel32_RaiseException := RaiseExceptionAddressCache;
  565. {$IFDEF BORLAND}
  566. SysUtils_ExceptObjProc := System.ExceptObjProc;
  567. System.ExceptObjProc := @HookedExceptObjProc;
  568. {$ENDIF BORLAND}
  569. {$IFDEF FPC}
  570. SysUtils_ExceptProc := System.ExceptProc;
  571. System.ExceptProc := @HookedExceptProc;
  572. {$ENDIF FPC}
  573. end;
  574. ExceptionsHooked := Result;
  575. end
  576. else
  577. Result := True;
  578. end;
  579. function JclUnhookExceptions: Boolean;
  580. begin
  581. {$IFDEF BORLAND}
  582. if HasCppRtl then
  583. TJclPeMapImgHooks.ReplaceImport (GetCppRtlBase, kernel32, @HookedRaiseException, @Kernel32_RaiseException);
  584. {$ENDIF BORLAND}
  585. if ExceptionsHooked then
  586. begin
  587. with TJclPeMapImgHooks do
  588. ReplaceImport(SystemBase, kernel32, @HookedRaiseException, @Kernel32_RaiseException);
  589. {$IFDEF BORLAND}
  590. System.ExceptObjProc := @SysUtils_ExceptObjProc;
  591. @SysUtils_ExceptObjProc := nil;
  592. {$ENDIF BORLAND}
  593. {$IFDEF FPC}
  594. System.ExceptProc := @SysUtils_ExceptProc;
  595. @SysUtils_ExceptProc := nil;
  596. {$ENDIF FPC}
  597. @Kernel32_RaiseException := nil;
  598. Result := True;
  599. ExceptionsHooked := False;
  600. end
  601. else
  602. Result := True;
  603. end;
  604. function JclExceptionsHooked: Boolean;
  605. begin
  606. Result := ExceptionsHooked;
  607. end;
  608. function JclHookExceptionsInModule(Module: HMODULE): Boolean;
  609. begin
  610. Result := ExceptionsHooked and
  611. TJclPeMapImgHooks.ReplaceImport(Pointer(Module), kernel32, RaiseExceptionAddress, @HookedRaiseException);
  612. end;
  613. function JclUnhookExceptionsInModule(Module: HMODULE): Boolean;
  614. begin
  615. Result := ExceptionsHooked and
  616. TJclPeMapImgHooks.ReplaceImport(Pointer(Module), kernel32, @HookedRaiseException, @Kernel32_RaiseException);
  617. end;
  618. {$IFDEF HOOK_DLL_EXCEPTIONS}
  619. // Exceptions hooking in libraries
  620. procedure JclHookExceptDebugHookProc(Module: HMODULE; Hook: Boolean); stdcall;
  621. begin
  622. if Hook then
  623. HookExceptModuleList.HookModule(Module)
  624. else
  625. HookExceptModuleList.UnhookModule(Module);
  626. end;
  627. function CallExportedHookExceptProc(Module: HMODULE; Hook: Boolean): Boolean;
  628. var
  629. HookExceptProcPtr: PPointer;
  630. HookExceptProc: TJclHookExceptDebugHook;
  631. begin
  632. HookExceptProcPtr := TJclHookExceptModuleList.JclHookExceptDebugHookAddr;
  633. Result := Assigned(HookExceptProcPtr);
  634. if Result then
  635. begin
  636. @HookExceptProc := HookExceptProcPtr^;
  637. if Assigned(HookExceptProc) then
  638. HookExceptProc(Module, True);
  639. end;
  640. end;
  641. {$ENDIF HOOK_DLL_EXCEPTIONS}
  642. function JclInitializeLibrariesHookExcept: Boolean;
  643. begin
  644. {$IFDEF HOOK_DLL_EXCEPTIONS}
  645. if IsLibrary then
  646. Result := CallExportedHookExceptProc(SystemTObjectInstance, True)
  647. else
  648. begin
  649. if not Assigned(HookExceptModuleList) then
  650. HookExceptModuleList := TJclHookExceptModuleList.Create;
  651. Result := True;
  652. end;
  653. {$ELSE HOOK_DLL_EXCEPTIONS}
  654. Result := True;
  655. {$ENDIF HOOK_DLL_EXCEPTIONS}
  656. end;
  657. function JclHookedExceptModulesList(out ModulesList: TJclModuleArray): Boolean;
  658. begin
  659. {$IFDEF HOOK_DLL_EXCEPTIONS}
  660. Result := Assigned(HookExceptModuleList);
  661. if Result then
  662. HookExceptModuleList.List(ModulesList);
  663. {$ELSE HOOK_DLL_EXCEPTIONS}
  664. Result := False;
  665. SetLength(ModulesList, 0);
  666. {$ENDIF HOOK_DLL_EXCEPTIONS}
  667. end;
  668. {$IFDEF HOOK_DLL_EXCEPTIONS}
  669. procedure FinalizeLibrariesHookExcept;
  670. begin
  671. FreeAndNil(HookExceptModuleList);
  672. if IsLibrary then
  673. CallExportedHookExceptProc(SystemTObjectInstance, False);
  674. end;
  675. //=== { TJclHookExceptModuleList } ===========================================
  676. constructor TJclHookExceptModuleList.Create;
  677. begin
  678. inherited Create;
  679. FModules := TThreadList.Create;
  680. HookStaticModules;
  681. JclHookExceptDebugHook := @JclHookExceptDebugHookProc;
  682. end;
  683. destructor TJclHookExceptModuleList.Destroy;
  684. begin
  685. JclHookExceptDebugHook := nil;
  686. FreeAndNil(FModules);
  687. inherited Destroy;
  688. end;
  689. procedure TJclHookExceptModuleList.HookModule(Module: HMODULE);
  690. begin
  691. with FModules.LockList do
  692. try
  693. if IndexOf(Pointer(Module)) = -1 then
  694. begin
  695. Add(Pointer(Module));
  696. JclHookExceptionsInModule(Module);
  697. end;
  698. finally
  699. FModules.UnlockList;
  700. end;
  701. end;
  702. procedure TJclHookExceptModuleList.HookStaticModules;
  703. var
  704. ModulesList: TStringList;
  705. I: Integer;
  706. Module: HMODULE;
  707. begin
  708. ModulesList := nil;
  709. with FModules.LockList do
  710. try
  711. ModulesList := TStringList.Create;
  712. if LoadedModulesList(ModulesList, GetCurrentProcessId, True) then
  713. for I := 0 to ModulesList.Count - 1 do
  714. begin
  715. Module := HMODULE(ModulesList.Objects[I]);
  716. if GetProcAddress(Module, JclHookExceptDebugHookName) <> nil then
  717. HookModule(Module);
  718. end;
  719. finally
  720. FModules.UnlockList;
  721. ModulesList.Free;
  722. end;
  723. end;
  724. class function TJclHookExceptModuleList.JclHookExceptDebugHookAddr: Pointer;
  725. var
  726. HostModule: HMODULE;
  727. begin
  728. HostModule := GetModuleHandle(nil);
  729. Result := GetProcAddress(HostModule, JclHookExceptDebugHookName);
  730. end;
  731. procedure TJclHookExceptModuleList.List(out ModulesList: TJclModuleArray);
  732. var
  733. I: Integer;
  734. begin
  735. with FModules.LockList do
  736. try
  737. SetLength(ModulesList, Count);
  738. for I := 0 to Count - 1 do
  739. ModulesList[I] := HMODULE(Items[I]);
  740. finally
  741. FModules.UnlockList;
  742. end;
  743. end;
  744. procedure TJclHookExceptModuleList.UnhookModule(Module: HMODULE);
  745. begin
  746. with FModules.LockList do
  747. try
  748. Remove(Pointer(Module));
  749. finally
  750. FModules.UnlockList;
  751. end;
  752. end;
  753. {$ENDIF HOOK_DLL_EXCEPTIONS}
  754. initialization
  755. Notifiers := TThreadList.Create;
  756. {$IFDEF BORLAND}
  757. Filters := TThreadList.Create;
  758. {$ENDIF BORLAND}
  759. {$IFDEF UNITVERSIONING}
  760. RegisterUnitVersion(HInstance, UnitVersioning);
  761. {$ENDIF UNITVERSIONING}
  762. finalization
  763. {$IFDEF UNITVERSIONING}
  764. UnregisterUnitVersion(HInstance);
  765. {$ENDIF UNITVERSIONING}
  766. {$IFDEF HOOK_DLL_EXCEPTIONS}
  767. FinalizeLibrariesHookExcept;
  768. {$ENDIF HOOK_DLL_EXCEPTIONS}
  769. FreeThreadObjList(Notifiers);
  770. {$IFDEF BORLAND}
  771. FreeThreadObjList(Filters);
  772. {$ENDIF BORLAND}
  773. end.