Kernel Objects 

www.madshi.net

The win32 kernel supports a range of some important object types. E.g. we have Processes, Threads or Events and Mutexes. All those objects are supported by APIs, which are exported from kernel32.dll. First let's look at the full list of available kernel objects. Please note that some objects are only available in either the win9x or winNT family:

type
  TKernelObjType = (otUnknown,
                    otSemaphore,
                    otEvent,
                    otMutex,
                    otProcess,
                    otThread,
                    otTimer,
                    otFileMapping,
                    otFile,
                    otSnapshot,         // in winNT -> otFileMapping
                    otNotification,     // in winNT -> otFile
                    otComPort,          // in winNT -> otFile
                    otPipe,             // in winNT -> otFile
                    otMailslot,         // in winNT -> otFile
                    otSocket,           // in winNT -> otFile
                    otVxd,              // only win9x
                    otConsoleInput,     // only win9x
                    otConsoleOutput,    // only win9x
                    otDirectory,        // only winNT
                    otSymbolicLink,     // only winNT
                    otToken,            // only winNT
                    otWindowStation,    // only winNT
                    otDesktop,          // only winNT
                    otKey,              // only winNT
                    otPort,             // only winNT
                    otIoCompletion,     // only winNT
                    otKeyedEvent);      // only winXP

madKernel implements one base interface for all the listed kernel objects, which is named "IKernelObj". See also the IKernelObj Reference.

type IKernelObj = interface (IBasic) ['{72518460-8D63-11D3-A52E-00005A180D69}'];

The following methods are supported for all kernel objects:

property IKernelObj.ObjType    : TKernelObjType;  // object type
property IKernelObj.ObjTypeStr : string;          // object type name
property IKernelObj.ObjAddr    : pointer;         // address of this object's internal data structure

// Examples:
CurrentProcess.ObjType     ->  otProcess
CurrentThread .ObjTypeStr  ->  'Thread'

In the winNT family we can find out the name of the object. What the name is depends on that type of the object. E.g. for files this is the full file path. Unfortunately this method doesn't work for the win9x family.

property IKernelObj.ObjName : string;  // only winNT

// Example:
Handle(CreateMutex(nil, false, 'TestMutex')).KernelObj.ObjName  ->  '\BaseNamedObjects\TestMutex'

If we have a thread or process kernel object, we can also ask the ID of the object. Only threads and processes have an ID, all other objects don't:

property IKernelObj.ID : cardinal;

// Examples:
CurrentProcess.ID  ->  GetCurrentProcessID
CurrentThread .ID  ->  GetCurrentThreadID
NewMutex      .ID  ->  0

If we have an event kernel object, we can ask whether we have an auto event or a manual event. Obviously this method only works for event kernel objects:

function IKernelObj.IsAutoEvent : boolean;

// Examples:
NewEvent(false).IsAutoEvent  ->  false
Handle(CreateEvent(nil, false, true, nil)).KernelObj.IsAutoEvent  ->  true

Most win32 APIs only accept kernel object handles, so with this property you can get a handle to our kernel object. If no handle is available yet, a new one is created, e.g. by using OpenProcess for a process kernel object. The new handle will be opened with full access rights, if possible.

property IKernelObj.Handle : IHandle;

// Example:
TerminateProcess(CurrentProcess.Handle.Handle, 0);

If you want, you can list all handles which the current process has open for this specific kernel object:

property IKernelObj.Handles : IHandles;

Some kernel objects can be waited for, e.g. Events and Mutexes, but also Processes and Threads. So we have a second base interface, which implements general waiting functionality for those kind of kernel objects. This interface is called "IWaitableObj". See the IWaitableObj Reference. Additionally we have another base interface, which can hold a list of waitable objects. It's called "IWaitableObjs". See also the IWaitableObjs Reference.

type
  IWaitableObj  = interface (IKernelObj)       ['{72518461-8D63-11D3-A52E-00005A180D69}'];
  IWaitableObjs = interface (ICustomBasicList) ['{A1DB3222-8EB8-11D3-A52E-00005A180D69}'];

Now let's first define some types and constants which are needed for the following "WaitFor" methods/functions:

type
  TWaitForMessage   = (wfKey, wfMouseMove, wfMouseButton, wfPostMessage,
                       wfTimer, wfPaint, wfSendMessage, wfHotKey, wfAllPostMessage);
  TWaitForMessages  = set of TWaitForMessage;

const
  CWaitForAllEvents : TWaitForMessages = [wfKey, wfMouseMove, wfMouseButton,
                                          wfPostMessage, wfTimer, wfPaint,
                                                         wfHotKey];
  CWaitForAllInput  : TWaitForMessages = [wfKey, wfMouseMove, wfMouseButton,
                                          wfPostMessage, wfTimer, wfPaint,
                                          wfSendMessage, wfHotKey];
  CWaitForInput     : TWaitForMessages = [wfKey, wfMouseMove, wfMouseButton];
  CWaitForMouse     : TWaitForMessages = [       wfMouseMove, wfMouseButton];

There are several "WaitFor" methods/functions. You can wait for one "IWaitableObj" kernel object or for multiple "IWaitableObjs" kernel objects. "WaitFor" waits, until the specified objects enter "signaled state". E.g. a process is in signaled state, if it is not running anymore. If you set "waitAll = false", the wait is stopped as soon as one of the specified objects is signaled. In that case the index of the signaled object is returned.

function IWaitableObj. WaitFor (milliseconds   : cardinal  = INFINITE;
                                handleMessages : boolean   = true    ) : boolean;
function IWaitableObjs.WaitFor (waitAll        : boolean   = false;
                                milliseconds   : cardinal  = INFINITE;
                                handleMessages : boolean   = true;
                                index          : TPInteger = nil     ) : boolean;

function WaitFor (const objects  : array of IBasic;
                  waitAll        : boolean          = false;
                  milliseconds   : cardinal         = INFINITE;
                  handleMessages : boolean          = true;
                  msgs           : TWaitForMessages = [];
                  alertable      : boolean          = false   ) : integer; overload;
function WaitFor (const objects  : ICustomBasicList;
                  waitAll        : boolean          = false;
                  milliseconds   : cardinal         = INFINITE;
                  handleMessages : boolean          = true;
                  msgs           : TWaitForMessages = [];
                  alertable      : boolean          = false   ) : integer; overload;

// Examples:
NewProcess('calc.exe').WaitFor;
WaitFor([Process('calc.exe'), Process('notepad.exe')], true);                  

The "Notify" methods/functions don't wait, they return at once. But the specified kernel objects are watched for in the background. As soon as the wanted state is reached, a notification message is sent to the specified window:

function IWaitableObj. Notify (window  : cardinal;
                               msg     : cardinal       ) : boolean;
function IWaitableObjs.Notify (window  : cardinal;
                               msg     : cardinal;
                               waitAll : boolean = false) : boolean;

function Notify (window        : cardinal;
                 msg           : cardinal;
                 const objects : array of IBasic;
                 waitAll       : boolean;
                 msgs          : TWaitForMessages;
                 alertable     : boolean                 ) : boolean; overload;
function Notify (window        : cardinal;
                 msg           : cardinal;
                 const objects : ICustomBasicList;
                 waitAll       : boolean          = false;
                 msgs          : TWaitForMessages = [];
                 alertable     : boolean          = false) : boolean; overload;

// Example:
procedure ExplorerWasClosed(window, msg: cardinal; wParam, lParam: integer; var result: integer);
begin
  MessageBox(0, 'Explorer was closed!', 'info', 0);
end;

initialization
  Process('explorer.exe').Notify(MsgHandlerWindow, AddMsgHandler(ExplorerWasClosed));
end.

The "WouldWait" methods/functions test, whether we would have to wait for the specified objects:

function IWaitableObj. WouldWait : boolean;
function IWaitableObjs.WouldWait (waitAll: boolean) : boolean;

function WouldWait (const objects : array of IBasic;
                    waitAll       : boolean = false ) : boolean; overload;
function WouldWait (const objects : ICustomBasicList;
                    waitAll       : boolean = false ) : boolean; overload;

// Example:
boolVar := Mutex(aMutexHandle).WouldWait;

Finally we have an interface for all the kernel objects that have no own interface (yet).

type IOtherKernelObj = interface (IKernelObj) ['{4910FAC0-B61E-11D3-A530-00005A180D69}'];