Threads 

www.madshi.net

One quite important kernel object is the "Thread". You surely know, each process has one or multiple threads. A process is done if all threads are done. There are lots of kernel32.dll APIs for dealing with threads. madKernel implements two interfaces, namely "IThread" to encapsulate a single thread object and "IThreads" to work with a list of threads. See also the IThread Reference and the IThreads Reference.

type
  IThread  = interface (IWaitableObj ) ['{A1DB3220-8EB8-11D3-A52E-00005A180D69}'];
  IThreads = interface (IWaitableObjs) ['{A1DB3221-8EB8-11D3-A52E-00005A180D69}'];

There are multiple ways to get an IThread or an IThreads instance. You can use IProcess.Threads or IWindow.OwnerThread or one of the following functions. You can create a "NewThread", you can get the "CurrentThread" or the current process' "MainThread". Also you can convert a win32 handle dword value or an IHandle instance into an IThread instance. Finally you can call "Threads" to get a list of the threads of our process or of all processes system wide.

type TThreadFunc = function (param: pointer) : cardinal; stdcall;

function NewThread (threadFunc    : TThreadFunc;
                    parameter     : pointer             = nil;
                    creationFlags : cardinal            = 0;
                    stackSize     : cardinal            = 0;
                    threadAttr    : PSecurityAttributes = nil) : IThread;

function CurrentThread : IThread;
function MainThread    : IThread;

function Thread (const thread : IHandle        ) : IThread; overload;
function Thread (thread       : cardinal;
                 autoClose    : boolean  = true) : IThread; overload;

function Threads (systemWide: boolean = false) : IThreads;

// Example:
function TestThread(dummy: pointer) : cardinal; stdcall;
begin
  result := 0;
  Assert(CurrentThread <> MainThread);
end;

NewThread(TestThread).WaitFor;

You can check whether the thread object represented by this IThread instance is still valid. Also you can check whether the thread is still running or whether it has finished its task.

function IThread.IsStillValid   : boolean;
function IThread.IsStillRunning : boolean;

// Example:
Assert(CurrentThread.IsStillRunning);

To get a handle with full access rights, you can simply use the property IKernelObj.Handle. If you need specific access rights, you can call the method "GetHandle" and enter the access rights you need. Please note, that madKernel takes your access wish as the minimum. That means it is possible (due to handle caching) that you get a handle back, which has all the access rights you need plus some more rights you didn't ask for.

function IThread.GetHandle (access: cardinal = THREAD_ALL_ACCESS) : IHandle;

In win9x you can call "StoreHandle" to tell madKernel that the handles for this thread should be cached. You can call "StoreHandle" as often as you like, but please call "ReleaseHandle" once for each "StoreHandle" call. In winNT handles are always cached, so there these properties are ignored.

procedure IThread.StoreHandle;    // win9x only
procedure IThread.ReleaseHandle;  // win9x only

To which process does this thread belong?

property IThread.OwnerProcess : IProcess;

// Example:
Assert(CurrentThread.OwnerProcess = CurrentProcess);

"Windows_" lists all the windows which belong to this thread. "TaskbarWindows" returns a subcount of this list, only those windows are returned, which are visible in the taskbar.

property IThread.Windows_       : IWindows;
property IThread.TaskbarWindows : IWindows;

// Example:
MessageBox(0, pchar(IntToStr(CurrentThread.Windows_.ItemCount) + ' windows belong to me!'), 'info', 0);

With these properties you can access the thread's priority parameters. Please see the win32 APIs "GetThreadPriority" and "GetThreadPriorityBoost" for more information.

property IThread.Priority      : integer;
property IThread.PriorityBoost : boolean;

Accesses the thread's affinity mask and ideal processor. Please look at the documentation of the win32 APIs "GetThreadAffinityMask" and "GetThreadIdealProcessor" for more information.

function IThread.SetAffinityMask   (affinityMask    : cardinal;
                                    oldAffinityMask : TPCardinal = nil) : boolean;
function IThread.SetIdealProcessor (processorNo     : cardinal;
                                    oldProcessorNo  : TPCardinal = nil) : boolean;

Posts a message to the thread's message queue (see API "PostThreadMessage").

function IThread.PostMessage (msg    : cardinal;
                              wParam : integer = 0;
                              lParam : integer = 0) : boolean;

Call these methods to suspend or resume the thread:

function IThread.IsSuspended : boolean;
function IThread.Suspend (oldSuspendCount: TPCardinal = nil) : boolean;
function IThread.Resume  (oldSuspendCount: TPCardinal = nil) : boolean;

// Example:
CurrentThread.Suspend;  // freeze me

Use the following property to get or set the thread's context (see API "GetThreadContext"):

property IThread.Context : TContext;

With the following methods you can attach the thread's input queue with the input queue of another thread (and detach the queues again). "thread = 0" stands for the current thread.

function IThread.AttachInput (const thread: IThread     ) : boolean; overload;
function IThread.AttachInput (const thread: IHandle     ) : boolean; overload;
function IThread.AttachInput (      thread: cardinal = 0) : boolean; overload;

function IThread.DetachInput (const thread: IThread     ) : boolean; overload;
function IThread.DetachInput (const thread: IHandle     ) : boolean; overload;
function IThread.DetachInput (      thread: cardinal = 0) : boolean; overload;

Call "PostQuitMessage" or "Terminate" to terminate the thread. This is not recommended, please use it only if you know exactly what you're doing!

function IThread.PostQuitMessage (exitCode: cardinal = 0) : boolean;
function IThread.Terminate       (exitCode: cardinal = 0) : boolean;

// Example:
CurrentThread.Terminate;  // suicide

Asks the thread's exit code. If the thread is still running, you'll get "STILL_ACTIVE".

property IThread.ExitCode : cardinal;

The following method asks how much time the thread has spent with thread creation, with thread exiting, in kernel land and in user land. This method works only in winNT.

function IThread.GetTimes (var creation, exit, kernel, user: int64) : boolean;  // only winNT

Of course the IThreads interface allows easy access to it's items. Furthermore you can refresh the thread list, if you like (that is look for new, deleted and changed threads).

property IThreads.Items [index: integer] : IThread;

function IThreads.RefreshItems : boolean;

The following IThreads methods (and write only properties) are more or less identical to the IThread methods with the same name. It's just that if you call the IThreads methods, they are called for all threads in the list.

property IThreads.Priority      : integer;  // write only
property IThreads.PriorityBoost : boolean;  // write only

function IThreads.SetAffinityMask   (affinityMask : cardinal) : boolean;
function IThreads.SetIdealProcessor (processorNo  : cardinal) : boolean;

function IThreads.PostMessage (msg    : cardinal;
                               wParam : integer  = 0;
                               lParam : integer  = 0) : boolean;

function IThreads.IsStillRunning (all: boolean = false) : boolean;

function IThreads.Suspend : boolean;
function IThreads.Resume  : boolean;

function IThreads.PostQuitMessage (exitCode: cardinal = 0) : boolean;
function IThreads.Terminate       (exitCode: cardinal = 0) : boolean;

// Example:
Threads(true).Terminate;  // ouch, that's a bad idea!

Does this IThreads instance only enumerate threads of one specific process?

property IThreads.OwnerProcess : IProcess;