Contexts

It is often necessary for an application to associate certain information with a given registry key. To assist developers in doing so in a convenient and performant manner, the CBRegistry component provides context parameters in a number of events.

A context carries an application-defined value that identifies or points to some application-defined data, and registry key has a separate context associated with it. The CBRegistry component treats context values as opaque; it stores the context values passed to it by the application, and ensures that the correct values are exposed again whenever some event fires for a particular registry key; but does not otherwise attempt to use said values in any way.

Context Lifetimes

Contexts in CBRegistry are available in all events that correspond to registry key operations. They are created before the BeforeCreateKey or BeforeOpenKey event fires, and are deleted after the AfterCloseKey event fires (or, more accurately, after the CleanupKeyContext event fires).

Context Use-Cases

Contexts are most helpful when used to store information associated with a registry key that can be used to speed up later events. For example, the only time the CBRegistry component exposes the name of a registry key is during the BeforeCreateKey and BeforeOpenKey events. Applications that wish to use the key name in later events can store it in the key context and then access it in the desired events later. A similar strategy can also be applied for events related to registry key values.

More generally, applications are free to obtain and store whatever information they wish using contexts, so long as their event handlers comply with the restrictions described by the Recursive Calls topic.

Using Contexts

In .NET, context parameters are IntPtr-typed. You can store either an Int64 value or a reference to the object in those parameters. Storing Int64 values is used to store keys of Dictionary records, so that the stored values act as "handles" to the real data, stored in the Dictionary.

To store an integer value, use the IntPtr(Int64) constructor. To retrieve the stored value, use the IntPtr.ToInt64 method.

To store a reference to an object in a context, create a GCHandle instance using said object (so that it won't be garbage collected), then use it to obtain an IntPtr:

Stream stream; // The object whose reference will be stored in the context.
Context = GCHandle.ToIntPtr(GCHandle.Alloc(stream));

Then, to access the object in a later event using the IntPtr stored in the context, do the following:

// Guard against empty context.
Stream stream = (Context != IntPtr.Zero) ? (Stream) GCHandle.FromIntPtr(Context).Target : null;

Finally, before a Context is discarded, be sure to free the GCHandle instance so that the referenced object can be garbage collected if needed.

// Guard against empty context.
if (Context != IntPtr.Zero) GCHandle.FromIntPtr(Context).Free();

The above scheme can also be used to store primitives (via "boxing") and other value types (non-objects).

Copyright (c) 2022 Callback Technologies, Inc. - All rights reserved.
CBFS Filter 2020 .NET Edition - Version 20.0 [Build 8317]