exceptdlg.delphi32.pas
来自「最新版 JCL+JVCL控件!非常不错的控件资源。包含了所能用到的大部分功能!」· PAS 代码 · 共 729 行 · 第 1/2 页
PAS
729 行
{**************************************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
{ you may not use this file except in compliance with the License. You may obtain a copy of the }
{ License at http://www.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
{ and limitations under the License. }
{ }
{ The Original Code is ExceptDlg.pas. }
{ }
{ The Initial Developer of the Original Code is Petr Vones. }
{ Portions created by Petr Vones are Copyright (C) of Petr Vones. }
{ }
{**************************************************************************************************}
{ }
{ Last modified: $Date:: 2007-11-13 14:17:59 +0100 (mar., 13 nov. 2007) $ }
{ Revision: $Rev:: 2213 $ }
{ Author: $Author:: outchy $ }
{ }
{**************************************************************************************************}
unit %MODULENAME%;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, AppEvnts,
JclSysUtils,%if SendEMail JclMapi,%endif JclDebug;
const
UM_CREATEDETAILS = WM_USER + $100;
type
T%FORMNAME% = class(%ANCESTORNAME%)
%if SendEMail SendBtn: TButton;%endif
TextLabel: TMemo;
OkBtn: TButton;
DetailsBtn: TButton;
BevelDetails: TBevel;
DetailsMemo: TMemo;
%if SendEMail procedure SendBtnClick(Sender: TObject);%endif
procedure FormPaint(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure DetailsBtnClick(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure FormDestroy(Sender: TObject);
procedure FormResize(Sender: TObject);
private
private
FDetailsVisible: Boolean;
FThreadID: DWORD;
%if ActiveControls FLastActiveControl: TWinControl;%endif
FNonDetailsHeight: Integer;
FFullHeight: Integer;
%if LogFile FSimpleLog: TJclSimpleLog;
procedure ReportToLog;%endif
function GetReportAsText: string;
procedure SetDetailsVisible(const Value: Boolean);
procedure UMCreateDetails(var Message: TMessage); message UM_CREATEDETAILS;
protected
procedure AfterCreateDetails; dynamic;
procedure BeforeCreateDetails; dynamic;
procedure CreateDetails; dynamic;
procedure CreateReport;
function ReportMaxColumns: Integer; virtual;
function ReportNewBlockDelimiterChar: Char; virtual;
procedure NextDetailBlock;
procedure UpdateTextLabelScrollbars;
public
procedure CopyReportToClipboard;
class procedure ExceptionHandler(Sender: TObject; E: Exception);
class procedure ExceptionThreadHandler(Thread: TJclDebugThread);
class procedure ShowException(E: TObject; Thread: TJclDebugThread);
property DetailsVisible: Boolean read FDetailsVisible
write SetDetailsVisible;
property ReportAsText: string read GetReportAsText;
%if LogFile property SimpleLog: TJclSimpleLog read FSimpleLog;%endif
end;
T%FORMNAME%Class = class of T%FORMNAME%;
var
%FORMNAME%Class: T%FORMNAME%Class = T%FORMNAME%;
implementation
{$R *.dfm}
uses
ClipBrd, Math,
JclBase, JclFileUtils, JclHookExcept, JclPeImage, JclStrings, JclSysInfo, JclWin32;
resourcestring
RsAppError = '%s - application error';
RsExceptionClass = 'Exception class: %s';
RsExceptionMessage = 'Exception message: %s';
RsExceptionAddr = 'Exception address: %p';
RsStackList = 'Stack list, generated %s';
RsModulesList = 'List of loaded modules:';
RsOSVersion = 'System : %s %s, Version: %d.%d, Build: %x, "%s"';
RsProcessor = 'Processor: %s, %s, %d MHz';
RsMemory = 'Memory: %d; free %d';
RsScreenRes = 'Display : %dx%d pixels, %d bpp';
RsActiveControl = 'Active Controls hierarchy:';
RsThread = 'Thread: %s';
RsMissingVersionInfo = '(no version info)';
%if AllThreads RsMainThreadCallStack = 'Call stack for main thread';
RsThreadCallStack = 'Call stack for thread %s';%endif
var
%FORMNAME%: T%FORMNAME%;
//============================================================================
// Helper routines
//============================================================================
// SortModulesListByAddressCompare
// sorts module by address
function SortModulesListByAddressCompare(List: TStringList;
Index1, Index2: Integer): Integer;
var
Addr1, Addr2: Cardinal;
begin
Addr1 := Cardinal(List.Objects[Index1]);
Addr2 := Cardinal(List.Objects[Index2]);
if Addr1 > Addr2 then
Result := 1
else if Addr1 < Addr2 then
Result := -1
else
Result := 0;
end;
//============================================================================
// TApplication.HandleException method code hooking for exceptions from DLLs
//============================================================================
// We need to catch the last line of TApplication.HandleException method:
// [...]
// end else
// SysUtils.ShowException(ExceptObject, ExceptAddr);
// end;
procedure HookShowException(ExceptObject: TObject; ExceptAddr: Pointer);
begin
if JclValidateModuleAddress(ExceptAddr)
and (ExceptObject.InstanceSize >= Exception.InstanceSize) then
T%FORMNAME%.ExceptionHandler(nil, Exception(ExceptObject))
else
SysUtils.ShowException(ExceptObject, ExceptAddr);
end;
//----------------------------------------------------------------------------
function HookTApplicationHandleException: Boolean;
const
CallOffset = $86;
CallOffsetDebug = $94;
type
PCALLInstruction = ^TCALLInstruction;
TCALLInstruction = packed record
Call: Byte;
Address: Integer;
end;
var
TApplicationHandleExceptionAddr, SysUtilsShowExceptionAddr: Pointer;
CALLInstruction: TCALLInstruction;
CallAddress: Pointer;
WrittenBytes: Cardinal;
function CheckAddressForOffset(Offset: Cardinal): Boolean;
begin
try
CallAddress := Pointer(Cardinal(TApplicationHandleExceptionAddr) + Offset);
CALLInstruction.Call := $E8;
Result := PCALLInstruction(CallAddress)^.Call = CALLInstruction.Call;
if Result then
begin
if IsCompiledWithPackages then
Result := PeMapImgResolvePackageThunk(Pointer(Integer(CallAddress) + Integer(PCALLInstruction(CallAddress)^.Address) + SizeOf(CALLInstruction))) = SysUtilsShowExceptionAddr
else
Result := PCALLInstruction(CallAddress)^.Address = Integer(SysUtilsShowExceptionAddr) - Integer(CallAddress) - SizeOf(CALLInstruction);
end;
except
Result := False;
end;
end;
begin
TApplicationHandleExceptionAddr := PeMapImgResolvePackageThunk(@TApplication.HandleException);
SysUtilsShowExceptionAddr := PeMapImgResolvePackageThunk(@SysUtils.ShowException);
if Assigned(TApplicationHandleExceptionAddr) and Assigned(SysUtilsShowExceptionAddr) then
begin
Result := CheckAddressForOffset(CallOffset) or CheckAddressForOffset(CallOffsetDebug);
if Result then
begin
CALLInstruction.Address := Integer(@HookShowException) - Integer(CallAddress) - SizeOf(CALLInstruction);
Result := WriteProtectedMemory(CallAddress, @CallInstruction, SizeOf(CallInstruction), WrittenBytes);
end;
end
else
Result := False;
end;
//============================================================================
// Exception dialog with Send
//============================================================================
var
ExceptionShowing: Boolean;
//=== { T%FORMNAME% } ===============================================
procedure T%FORMNAME%.AfterCreateDetails;
begin
%if SendEMail SendBtn.Enabled := True;%endif
end;
//----------------------------------------------------------------------------
procedure T%FORMNAME%.BeforeCreateDetails;
begin
%if SendEMail SendBtn.Enabled := False;%endif
end;
//----------------------------------------------------------------------------
function T%FORMNAME%.ReportMaxColumns: Integer;
begin
Result := 78;
end;
%if SendEMail//----------------------------------------------------------------------------
procedure T%FORMNAME%.SendBtnClick(Sender: TObject);
begin
with TJclEmail.Create do
try
ParentWnd := Application.Handle;
Recipients.Add(%StrValue EMailAddress);
Subject := %StrValue EMailSubject;
Body := ReportAsText;
SaveTaskWindows;
try
Send(True);
finally
RestoreTaskWindows;
end;
finally
Free;
end;
end;
%endif
//----------------------------------------------------------------------------
procedure T%FORMNAME%.CopyReportToClipboard;
begin
ClipBoard.AsText := ReportAsText;
end;
//----------------------------------------------------------------------------
procedure T%FORMNAME%.CreateDetails;
begin
Screen.Cursor := crHourGlass;
DetailsMemo.Lines.BeginUpdate;
try
CreateReport;
%if LogFile ReportToLog;%endif
DetailsMemo.SelStart := 0;
SendMessage(DetailsMemo.Handle, EM_SCROLLCARET, 0, 0);
AfterCreateDetails;
finally
DetailsMemo.Lines.EndUpdate;
OkBtn.Enabled := True;
DetailsBtn.Enabled := True;
OkBtn.SetFocus;
Screen.Cursor := crDefault;
end;
end;
//----------------------------------------------------------------------------
procedure T%FORMNAME%.CreateReport;
var
%if ModuleList SL: TStringList;
I: Integer;
ModuleName: TFileName;
NtHeaders32: PImageNtHeaders32;
NtHeaders64: PImageNtHeaders64;
ModuleBase: Cardinal;
ImageBaseStr: string;%endif
%if ActiveControls C: TWinControl;%endif
%if OSInfo CpuInfo: TCpuInfo;
ProcessorDetails: string;%endif
%if StackList StackList: TJclStackInfoList;
%if AllThreads ThreadList: TJclDebugThreadList;
AThreadID: DWORD;%endif %endif
PETarget: TJclPeTarget;
begin
SL := TStringList.Create;
try
%if StackList // Stack list
StackList := JclGetExceptStackList(FThreadID);
if Assigned(StackList) then
begin
DetailsMemo.Lines.Add(Format(RsStackList, [DateTimeToStr(StackList.TimeStamp)]));
StackList.AddToStrings(DetailsMemo.Lines, %BoolValue ModuleName, %BoolValue ModuleOffset, %BoolValue CodeDetails, %BoolValue VirtualAddress);
NextDetailBlock;
end;
%if AllThreads // Main thread
if FThreadID <> MainThreadID then
begin
StackList := JclCreateThreadStackTraceFromID(%BoolValue RawData, MainThreadID);
if Assigned(StackList) then
begin
DetailsMemo.Lines.Add(RsMainThreadCallStack);
DetailsMemo.Lines.Add(Format(RsStackList, [DateTimeToStr(StackList.TimeStamp)]));
StackList.AddToStrings(DetailsMemo.Lines, %BoolValue ModuleName, %BoolValue ModuleOffset, %BoolValue CodeDetails, %BoolValue VirtualAddress);
NextDetailBlock;
end;
end;
// All threads
ThreadList := JclDebugThreadList;
ThreadList.Lock.Enter; // avoid modifications
try
for I := 0 to ThreadList.ThreadIDCount - 1 do
begin
AThreadID := ThreadList.ThreadIDs[I];
if (AThreadID <> FThreadID) then
begin
StackList := JclCreateThreadStackTrace(%BoolValue RawData, ThreadList.ThreadHandles[I]);
if Assigned(StackList) then
begin
DetailsMemo.Lines.Add(Format(RsThreadCallStack, [ThreadList.ThreadInfos[AThreadID]]));
DetailsMemo.Lines.Add(Format(RsStackList, [DateTimeToStr(StackList.TimeStamp)]));
StackList.AddToStrings(DetailsMemo.Lines, %BoolValue ModuleName, %BoolValue ModuleOffset, %BoolValue CodeDetails, %BoolValue VirtualAddress);
NextDetailBlock;
end;
end;
end;
finally
ThreadList.Lock.Leave;
end;
%endif
%endif
%if OSInfo // System and OS information
DetailsMemo.Lines.Add(Format(RsOSVersion, [GetWindowsVersionString, NtProductTypeString,
Win32MajorVersion, Win32MinorVersion, Win32BuildNumber, Win32CSDVersion]));
GetCpuInfo(CpuInfo);
ProcessorDetails := Format(RsProcessor, [CpuInfo.Manufacturer, CpuInfo.CpuName,
RoundFrequency(CpuInfo.FrequencyInfo.NormFreq)]);
if not CpuInfo.IsFDIVOK then
ProcessorDetails := ProcessorDetails + ' [FDIV Bug]';
if CpuInfo.ExMMX then
ProcessorDetails := ProcessorDetails + ' MMXex';
if CpuInfo.MMX then
ProcessorDetails := ProcessorDetails + ' MMX';
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?