211 lines
8.0 KiB
ObjectPascal
211 lines
8.0 KiB
ObjectPascal
|
|
{**************************************************************************************************}
|
||
|
|
{ }
|
||
|
|
{ Project JEDI Code Library (JCL) extension }
|
||
|
|
{ }
|
||
|
|
{ The contents of this file are subject to the Mozilla Public License Version 1.0 (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 JclDebugThread.pas. }
|
||
|
|
{ }
|
||
|
|
{ The Initial Developer of the Original Code is documented in the accompanying help file JCL.chm. }
|
||
|
|
{ Portions created by these individuals are Copyright (C) of these individuals. }
|
||
|
|
{ }
|
||
|
|
{**************************************************************************************************}
|
||
|
|
{ }
|
||
|
|
{ Last modified: $Date:: 2009-07-30 12:08:05 +0200 (jeu., 30 juil. 2009) $ }
|
||
|
|
{ Revision: $Rev:: 2892 $ }
|
||
|
|
{ Author: $Author:: outchy $ }
|
||
|
|
{ }
|
||
|
|
{**************************************************************************************************}
|
||
|
|
|
||
|
|
unit JclDebugThread;
|
||
|
|
|
||
|
|
{$I jcl.inc}
|
||
|
|
|
||
|
|
interface
|
||
|
|
|
||
|
|
uses
|
||
|
|
{$IFDEF UNITVERSIONING}
|
||
|
|
JclUnitVersioning,
|
||
|
|
{$ENDIF UNITVERSIONING}
|
||
|
|
Windows, Classes, SysUtils;
|
||
|
|
|
||
|
|
procedure RegisterThread(ThreadID: DWORD; const ThreadName: string); overload;
|
||
|
|
procedure RegisterThread(Thread: TThread; const ThreadName: string; IncludeClassName: Boolean = True); overload;
|
||
|
|
|
||
|
|
procedure UnregisterThread(ThreadID: DWORD); overload;
|
||
|
|
procedure UnregisterThread(Thread: TThread); overload;
|
||
|
|
|
||
|
|
procedure ChangeThreadName(ThreadID: DWORD; const ThreadName: string); overload;
|
||
|
|
procedure ChangeThreadName(Thread: TThread; const ThreadName: string; IncludeClassName: Boolean = True); overload;
|
||
|
|
|
||
|
|
function ThreadNamesAvailable: Boolean;
|
||
|
|
|
||
|
|
{$IFDEF UNITVERSIONING}
|
||
|
|
const
|
||
|
|
UnitVersioning: TUnitVersionInfo = (
|
||
|
|
RCSfile: '$URL: https://jcl.svn.sourceforge.net/svnroot/jcl/tags/JCL-2.1-Build3536/jcl/experts/debug/JclDebugThread.pas $';
|
||
|
|
Revision: '$Revision: 2892 $';
|
||
|
|
Date: '$Date: 2009-07-30 12:08:05 +0200 (jeu., 30 juil. 2009) $';
|
||
|
|
LogPath: 'JCL\experts\debug';
|
||
|
|
Extra: '';
|
||
|
|
Data: nil
|
||
|
|
);
|
||
|
|
{$ENDIF UNITVERSIONING}
|
||
|
|
|
||
|
|
implementation
|
||
|
|
|
||
|
|
uses
|
||
|
|
JclDebug, JclPeImage, JclSysUtils,
|
||
|
|
ThreadExpertSharedNames;
|
||
|
|
|
||
|
|
type
|
||
|
|
PThreadRec = ^TThreadRec;
|
||
|
|
TThreadRec = record
|
||
|
|
Func: TThreadFunc;
|
||
|
|
Parameter: Pointer;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TJclDebugThreadNotifier = class(TObject)
|
||
|
|
public
|
||
|
|
procedure ThreadRegistered(ThreadID: DWORD);
|
||
|
|
end;
|
||
|
|
|
||
|
|
var
|
||
|
|
SharedThreadNames: TSharedThreadNames;
|
||
|
|
HookImports: TJclPeMapImgHooks;
|
||
|
|
Notifier: TJclDebugThreadNotifier;
|
||
|
|
Kernel32_CreateThread: function (lpThreadAttributes: Pointer;
|
||
|
|
dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine;
|
||
|
|
lpParameter: Pointer; dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; stdcall;
|
||
|
|
Kernel32_ExitThread: procedure (dwExitCode: DWORD); stdcall;
|
||
|
|
|
||
|
|
function NewCreateThread(lpThreadAttributes: Pointer;
|
||
|
|
dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine;
|
||
|
|
lpParameter: Pointer; dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; stdcall;
|
||
|
|
var
|
||
|
|
Instance: TObject;
|
||
|
|
begin
|
||
|
|
Result := Kernel32_CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId);
|
||
|
|
if (Result <> 0) and (lpParameter <> nil) then
|
||
|
|
try
|
||
|
|
Instance := PThreadRec(lpParameter)^.Parameter;
|
||
|
|
if Instance is TThread then
|
||
|
|
RegisterThread(TThread(Instance), '', True);
|
||
|
|
except
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure NewExitThread(dwExitCode: DWORD); stdcall;
|
||
|
|
var
|
||
|
|
ThreadID: DWORD;
|
||
|
|
begin
|
||
|
|
ThreadID := GetCurrentThreadId;
|
||
|
|
Kernel32_ExitThread(dwExitCode);
|
||
|
|
try
|
||
|
|
UnregisterThread(ThreadID);
|
||
|
|
except
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function CreateThreadName(const ThreadName, ThreadClassName: string): string;
|
||
|
|
begin
|
||
|
|
if ThreadClassName <> '' then
|
||
|
|
begin
|
||
|
|
if ThreadName = '' then
|
||
|
|
Result := Format('[%s]', [ThreadClassName])
|
||
|
|
else
|
||
|
|
Result := Format('[%s] "%s"', [ThreadClassName, ThreadName]);
|
||
|
|
end
|
||
|
|
else
|
||
|
|
Result := Format('"%s"', [ThreadName]);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure RegisterThread(ThreadID: DWORD; const ThreadName: string);
|
||
|
|
begin
|
||
|
|
if Assigned(SharedThreadNames) then
|
||
|
|
SharedThreadNames.RegisterThread(ThreadID, CreateThreadName(ThreadName, ''));
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure RegisterThread(Thread: TThread; const ThreadName: string; IncludeClassName: Boolean);
|
||
|
|
begin
|
||
|
|
if Assigned(SharedThreadNames) then
|
||
|
|
SharedThreadNames.RegisterThread(Thread.ThreadID, CreateThreadName(ThreadName, Thread.ClassName));
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure UnregisterThread(ThreadID: DWORD);
|
||
|
|
begin
|
||
|
|
if Assigned(SharedThreadNames) then
|
||
|
|
SharedThreadNames.UnregisterThread(ThreadID);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure UnregisterThread(Thread: TThread);
|
||
|
|
begin
|
||
|
|
if Assigned(SharedThreadNames) then
|
||
|
|
SharedThreadNames.UnregisterThread(Thread.ThreadID);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure ChangeThreadName(ThreadID: DWORD; const ThreadName: string);
|
||
|
|
begin
|
||
|
|
if Assigned(SharedThreadNames) then
|
||
|
|
SharedThreadNames[ThreadID] := CreateThreadName(ThreadName, '');
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure ChangeThreadName(Thread: TThread; const ThreadName: string; IncludeClassName: Boolean);
|
||
|
|
begin
|
||
|
|
if Assigned(SharedThreadNames) then
|
||
|
|
SharedThreadNames[Thread.ThreadID] := CreateThreadName(ThreadName, Thread.ClassName);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function ThreadNamesAvailable: Boolean;
|
||
|
|
begin
|
||
|
|
Result := Assigned(SharedThreadNames);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure Init;
|
||
|
|
begin
|
||
|
|
if IsDebuggerAttached and TSharedThreadNames.Exists then
|
||
|
|
begin
|
||
|
|
SharedThreadNames := TSharedThreadNames.Create(False);
|
||
|
|
HookImports := TJclPeMapImgHooks.Create;
|
||
|
|
with HookImports do
|
||
|
|
begin
|
||
|
|
HookImport(SystemBase, kernel32, 'CreateThread', @NewCreateThread, @Kernel32_CreateThread);
|
||
|
|
HookImport(SystemBase, kernel32, 'ExitThread', @NewExitThread, @Kernel32_ExitThread);
|
||
|
|
end;
|
||
|
|
{ TODO -oPV -cDesign : TJclDebugThread could hold its name. In case of that the name could be read in hooked CreateThread }
|
||
|
|
Notifier := TJclDebugThreadNotifier.Create;
|
||
|
|
JclDebugThreadList.OnThreadRegistered := Notifier.ThreadRegistered;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
//=== { TJclDebugThreadNotifier } ============================================
|
||
|
|
|
||
|
|
procedure TJclDebugThreadNotifier.ThreadRegistered(ThreadID: DWORD);
|
||
|
|
begin
|
||
|
|
with JclDebugThreadList do
|
||
|
|
SharedThreadNames.RegisterThread(ThreadID,
|
||
|
|
CreateThreadName(ThreadNames[ThreadID], JclDebugThreadList.ThreadClassNames[ThreadID]));
|
||
|
|
end;
|
||
|
|
|
||
|
|
initialization
|
||
|
|
{$IFDEF UNITVERSIONING}
|
||
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
||
|
|
{$ENDIF UNITVERSIONING}
|
||
|
|
Init;
|
||
|
|
|
||
|
|
finalization
|
||
|
|
FreeAndNil(HookImports);
|
||
|
|
FreeAndNil(SharedThreadNames);
|
||
|
|
FreeAndNil(Notifier);
|
||
|
|
{$IFDEF UNITVERSIONING}
|
||
|
|
UnregisterUnitVersion(HInstance);
|
||
|
|
{$ENDIF UNITVERSIONING}
|
||
|
|
|
||
|
|
end.
|