Contexts

It is often necessary for an application to associate certain information with a given file/directory, file handle, or enumeration operation. To assist developers in doing so in a convenient and performant manner, the CBFilter 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 each file/directory, file handle, and enumeration operation has a separate context associated with it. The CBFilter 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 file, handle, or enumeration; but does not otherwise attempt to use said values in any way.

Note that contexts are not available in the CBMonitor component.

Context Lifetimes

Contexts in CBFilter can be grouped into a few categories, each of which is subject to a different lifetime:

  • File contexts and directory contexts, which are associated with an open file or directory.
  • Handle contexts, which are associated with a specific open file or directory handle.
  • Enumeration contexts, which are associated with an ongoing enumeration.

File/directory contexts are created the first time a file or directory is opened, and live until the last handle to that file or directory is closed. Handle contexts, on the other hand, are created every time a file or directory is opened, and only live until the associated file handle is closed. For example, consider the following sequence of operations:

Operation on File X Context Creations/Deletions Active Contexts
1. Opened by process A File context FX and handle context HXA created FX, HXA
2. Opened by process B Handle context HXB created FX, HXA, HXB
3. Closed by process B Handle context HXB deleted FX, HXA
4. Opened by process C Handle context HXC created FX, HXA, HXC
5. Closed by process A Handle context HXA deleted FX, HXC
6. Closed by process C File context FX and handle context HXC deleted

File/directory contexts are available in all Control Events corresponding to operations performed on some open file or directory, and handle contexts have similar availability. Enumeration contexts are created anytime a new enumeration operation begins, and live until the enumeration operation ends.

All contexts, when created, are created before their corresponding "first event" fires (e.g., AfterOpenFile, AfterEnumerateDirectory, etc.); and when deleted, are deleted after their corresponding "last event" fires (e.g., AfterCloseFile, AfterCloseEnumeration, etc.). However, if a context's "first event" fails, whether expectedly (e.g., due to Security Checks) or otherwise (see Error Reporting and Handling), then that context's value is immediately discarded since its corresponding "last event" won't ever fire. (Contexts are not available in the BeforeOpenFile or BeforeCreateFile events since it is not known yet whether such requests will succeed.)

Note: the components offer a special event, CleanupContext, which is the ultimate last event for the open file lifecycle. This event lets you dispose of the data, associated with file and handle contexts. It is recommended that contexts are deleted not in a AfterCloseFile event, but in CleanupContext. This will guarantee that there is no race condition between file closing and re-opening, where such race condition can lead to an invalid context value come into play.

Context Use-Cases

Contexts are most helpful when used to store information associated with a file/directory or file handle. Typically, contexts are initialized during their corresponding "first event", and then used in subsequent events to speed up those operations.

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 Avoiding Deadlocks and Recursive Calls topics.

Note: Although contexts usually come into play when the file is opened, the complex architecture of Windows filesystem filter stack makes it possible that some event, related to the opened file, fires ahead of the "first event" (i.e. before AfterCreateFile/AfterOpenFile/AfterEnumerateDirectory).

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]