In Windows we have different kind of handles. There are kernel object
handles, GDI handles and Window handles, to name the most important types of
handles. The "handle" functionality in madKernel is about kernel object
handles. In short we're talking about those handles which can be closed with
the kernel32 API "CloseHandle".
What exactly is a kernel object handle? Internally it consists of two
elements, namely an access mask and a pointer to a kernel object. That's it.
A kernel handle gives you access to a specific kernel object with specific
access rights. Now madKernel encapsulates a lot of handle functionality in
an interface with the name (you won't believe it) "IHandle". See also the
IHandle Reference. Furthermore there is also an interface for a list
of handles. See the IHandles Reference.
 |
type
IHandle = interface (IBasic) ['{86522220-8323-11D3-A52D-00005A180D69}'];
IHandles = interface (ICustomBasicList) ['{B3017220-8338-11D3-A52D-00005A180D69}'];
|
|
There are several ways to get a valid IHandle instance. You can use the
property IKernelObj.Handle to get a handle to a specific kernel
object, or you can use IProcess.Handles to get a list of handles
that a specific process has open. Or you can use one of the following two
functions. The function "Handle" accepts a typical win32 handle dword value.
If you set "autoClose" to "true", the handle is kind of eaten by the
interface. That means, if the interface is freed, the win32 handle is
automatically closed. The function "Handles" enumerates all handles which
are opened by the current process or by all processes system wide.
 |
function Handle (handle : cardinal;
autoClose : boolean = true) : IHandle; overload;
function Handles (systemWide: boolean = false) : IHandles;
Handle(CreateMutex(nil, false, nil)).KernelObj.ObjTypeStr -> 'Mutant'
MessageBox(0, pchar('We have ' + IntToStr(Handles.ItemCount) + ' open kernel handles.'), 'info', 0);
|
|
Now let's look at the basic properties of the "IHandle" interface. We can
ask the win32 handle value, the access mask of this handle and the type of
the referenced kernel object. Finally we can also get an interface instance
of the kernel object which is represented by this handle:
 |
property IHandle. Handle : cardinal;
property IHandle. Access : cardinal;
property IHandle. ObjType : TKernelObjType;
property IHandle. KernelObj : IKernelObj;
var hnd : IHandle;
begin
hnd := Handle(CreateMutex(nil, false, nil));
MessageBox(0, pchar('New mutex access: ' + IntToHex(hnd.Access, 1)), 'info', 0);
end;
|
|
If you set the "AutoClose" property to "true", the win32 handle dword value
(which is encapsulated by the IHandle instance) gets closed as soon as the
IHandle instance gets freed. If the "AutoClose" property is set to "false",
the win32 handle survives the IHandle instance. In this case you eventually
have to close the win32 handle manually by calling "CloseHandle".
 |
property IHandle. AutoClose : boolean;
|
|
Sometimes it might be possible, that the win32 handle is closed behind our
back. So with the property "IsStillValid" we can check, whether our IHandle
instance still makes sense.
 |
function IHandle. IsStillValid : boolean;
|
|
Sometimes you need to duplicate a handle. With the methods "Duplicate" you
can easily do so. You can duplicate a handle into your own process or in any
target process (if you have enough privileges). "targetProcess = 0"
stands for the current process.
 |
function IHandle. Duplicate (autoClose : boolean;
access : cardinal;
inheritHandles : boolean;
const targetProcess : IProcess ) : IHandle; overload;
function IHandle. Duplicate (autoClose : boolean;
access : cardinal;
inheritHandles : boolean;
const targetProcess : IHandle ) : IHandle; overload;
function IHandle. Duplicate (autoClose : boolean = true;
access : cardinal = 0;
inheritHandles : boolean = true;
targetProcess : cardinal = 0 ) : IHandle; overload;
|
|
You can wait for one or for multiple handles. If you set "waitAll" to "false",
the wait is over, if one handle (or rather the object represented by the
handle) is signaled. In that case the index of the signaled handle (object)
is returned.
 |
function IHandle. WaitFor (milliseconds : cardinal = INFINITE;
handleMessages : boolean = true ) : boolean;
function IHandles. WaitFor (waitAll : boolean = false;
milliseconds : cardinal = INFINITE;
handleMessages : boolean = true;
index : TPInteger = nil ) : boolean;
|
|
The "Notify" methods don't wait, they return at once. But the specified
handles (respectively the represented 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 IHandle. Notify (window : cardinal;
msg : cardinal ) : boolean;
function IHandles. Notify (window : cardinal;
msg : cardinal;
waitAll : boolean = false) : boolean;
|
|
The "WouldWait" methods test, whether we would have to wait for the
specified handles (kernel objects):
 |
function IHandle. WouldWait : boolean;
function IHandles. WouldWait (waitAll: boolean) : boolean;
|
|
Of course the IHandles interfaces has properties/methods to get easy access
to the items. Also you can easily refresh the handle list, that is you can
look for new, deleted or changed handles.
 |
property IHandles. Items [index: integer] : IHandle;
function IHandles. RefreshItems : boolean;
|
|
If this IHandle(s) instance resulted from an enumeration, we can ask the
enumeration parameters.