Discuss this help topic in CBFS Forum

File and Enumeration Contexts

Contexts is a placeholder for application-defined data. Context is held as a property within instances of CbFsFileInfo, CbFsHandleInfo, CbFsDirectoryEnumerationInfo and CbFsStreamEnumerationInfo classes. The contexts can be set in several event handlers (such as OnCreateFile, OnEnumerateDirectory etc) and used in several other event handlers. The data, placed to Context, is not used by Callback File System in any way.

Why Contexts are useful

Contexts can be used to store information about the file or directory, and this information can be re-used later to speed-up subsequent operations.
For example, if you open some file on the file system, you can place file handle to Context. Next, when the application handles for example OnReadFile event, it can use this handle instead of the file name. This speeds-up the operation, because there is no need to search some hash tables for a handle of the file.
Another example is a reference to the list of files, which has been built during the first call to OnEnumerateDirectory.

Contexts are local to the process in which they were created. They can't be passed to other processes.

How long Contexts live

For directory enumeration contexts live from the call to OnEnumerateDirectory to the call to OnCloseEnumeration.

For file operations contexts live from file open to file close calls. Contexts are stored with other information, related to opened file.

Before Callback File System 4 context was the same for all file open/create operations that overlap. For example, if process A opened the file, then process B opened the file (and the file was still opened by process A), one context was used until both A and B closed the file.

In Callback File System 4 there exist two different contexts for file operations - first (so-called file context) is allocated on first file open operation and is deallocated on last file close operation. Another one (so-called handle context) is allocated for each file open operation and is deallocated during corresponding file close operation. For example, consider the following sequence of operations:

  1. File X is opened by process A. File context FX is allocated. Handle context HXA is allocated.
  2. File X is opened by process B. Handle context HXB is allocated.
  3. File X is opened by process C. Handle context HXC is allocated. At this point the file is opened three times and we have three handle contexts and just one file context allocated.
  4. File X is closed by process C. HXC is deallocated, FX is alive.
  5. File X is closed by process A. HXA is deallocated, FX is alive.
  6. File X is closed by process B. HXB is deallocated, FX is deallocated as well.
File context is available in all callbacks where file operations are performed on opened files. However, handle context is not available when file read or write operations are performed. This is because memory mapping manager and cache manager act as proxies and they can either issue one write request with combined data, or postpone the writing operation to later moment (it's even possible that the application that involved the operation has been closed, and the data is still not written).

Note, that when CallAllOpenCloseCallbacks property is false (default value in CBFS 4 and earlier versions), OnOpenFile event is fired only for step 1 above, and OnCloseFile is fired only on step 6. Consequently handle contexts are useless (or you can treat them as having the same use as file contexts).

Use of contexts

Use of Contexts in VCL and C++/Lib API is quite straightforward - you just put pointers to Context.

With .NET API the situation is more complicated, as .NET uses garbage collection and improper typecasting can cause problems with object lifetime. To avoid this problem, we declared Contexts to be IntPtr. If you want to store the reference to the object in Context, you use GCHandle as follows (C# syntax):

Stream stream; // the object we put to Context
GCHandle ctx = GCHandle.Alloc(stream);
Context = (IntPtr) ctx;
To access the stream from Context, use the following code:
GCHandle ctx = (GCHandle) Context;
Stream stream = (Stream ) ctx.Target; // the object we get from Context

To store numbers or other types, that are not objects, use "boxing" (.NET concept, when a value type is "packed" into an object).

Discuss this help topic in CBFS Forum