Creating Custom States and Columns Provider Shell Extension for Virtual Drive

The IT Hit User File System Engine provides integrated support for states and custom columns that can be displayed in Windows Explorer. Below we will describe how to implement custom states and columns shell extension for your Virtual Drive.

The functionality described in this article is available in IT Hit User File System v5 Beta and later versions.

Custom columns provider shell extension for virtual file system

You can run your custom states and columns handler inside your main app, which processes file system calls, or you can run it in a separate process as a COM exe server or surrogate COM. Running your handler inside your main app provides better performance, even though the difference may not be noticeable in most cases.

Note that unlike context menu on Windows 11, custom states and columns handler does not require application identity.

Follow the steps below to implement custom states and columns shell extension in Windows Explorer:

1. Register Custom States and Columns

When registering virtual drive you will define custom columns that will appear for every item:

StorageProviderSyncRootInfo storageInfo = new StorageProviderSyncRootInfo();
storageInfo.Path = await StorageFolder.GetFolderFromPathAsync(path);
...
var proDefinitions = storageInfo.StorageProviderItemPropertyDefinitions;
proDefinitions.Add(new StorageProviderItemPropertyDefinition 
{ 
    DisplayNameResource = "Lock Owner"   , Id = 2 
});                     
proDefinitions.Add(new StorageProviderItemPropertyDefinition 
{ 
    DisplayNameResource = "ETag"         , Id = 6 
});

StorageProviderSyncRootManager.Register(storageInfo);

 

2. Provide Data for Custom States and Columns 

Implement the IFileSystemItem.GetPropertiesAsync() method and return your columns data:

public async Task<IEnumerable<FileSystemItemPropertyData>> GetPropertiesAsync()
{
    IList<FileSystemItemPropertyData> props = new List<FileSystemItemPropertyData>();

    PlaceholderItem placeholder;
    if (Engine.Placeholders.TryGetItem(UserFileSystemPath, out placeholder))
    {

        // Read LockInfo.
        if (placeholder.Properties.TryGetValue("LockInfo", out IDataItem propLockInfo))
        {
            if (propLockInfo.TryGetValue<ServerLockInfo>(out ServerLockInfo lockInfo))
            {
                // Get Lock Owner.
                var propertyLockOwner = new FileSystemItemPropertyData()
                {
                    Id = 2,
                    Value = lockInfo.Owner,
                    IconResource = Path.Combine(Engine.IconsFolderPath, "Locked.ico")
                };
                props.Add(propertyLockOwner);
            }
        }
        ...
    }

    return props;
}

 

3. Create Custom States and Columns Handler Class

  • If you run the custom states and columns handler as COM exe or COM surrogate outside of the main app:

    Derive your custom states and columns handler class from CustomStateHandlerRpcBase class:

    [ComVisible(true)]
    [ProgId("VirtualDrive.CustomStateProvider")]
    [Guid("000562AA-2879-4CF1-89E8-0AEC9596FE19")]
    public class CustomStateProvider : CustomStateHandlerRpcBase
    {
    }

    The custom states and columns handler will find the Engine instance behind the drive on which this handler is running automatically and than call the IEngine.GetFileSystemItemAsync() and IFileSystemItem.GetPropertiesAsync() methods when data for custom states and columns are needed.

  • If you run the custom states and columns handler as COM exe inside main app:

    Derive your custom states and columns provider class from the CustomStateHandlerIntegratedBase class provided with the Engine and pass your Engine instance directly to the base class constructor: 

    [ComVisible(true)]
    [ProgId("VirtualDrive.CustomStateProvider")]
    [Guid("754F334F-095C-46CD-B033-B2C0523D2829")]
    public class CustomStateProvider : CustomStateHandlerIntegratedBase
    {
        CustomStateProvider() : base(Program.Engine)
        {
        }
    }

    The handler will call the Engine instance that you pass to the constructor and than call the IEngine.GetFileSystemItemAsync() and IFileSystemItem.GetPropertiesAsync() methods when data for custom properties are needed.

You do not need to implement any methods in your custom states and columns handler. 

You must decorate the class with the ComVisibleProgId and unique Guid attributes. You will use them when registering your COM custom states and columns handler shell extension. Note that you must create your own unique GUID for your provider handler. Do NOT use the GUID from this sample. 

 

4. Register Custom States and Columns Handler

  • If you are using packaged app or sparse package manifest:

    • If you run the customs columns handler as COM exe or COM surrogate outside of the main app:

      Add the desktop3:CustomStateHandler and the com:ExeServer code tags to your packaged app manifest or sparse package manifest:

      <Package 
          xmlns:desktop3="http://schemas.microsoft.com/appx/manifest/desktop/windows10/3"
          xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10" ...>
        <Applications>
          <Application ...>
            <Extensions>
              <desktop3:Extension Category="windows.cloudFiles">
                <desktop3:CloudFiles>
                  <desktop3:CustomStateHandler 
                    Clsid="000562AA-2879-4CF1-89E8-0AEC9596FE19"/>
                </desktop3:CloudFiles>
              </desktop3:Extension>
              <com:Extension Category="windows.comServer">
                <com:ComServer>
                  <com:ExeServer 
                    DisplayName="VirtualDrive.ShellExtension" 
                    Executable="VirtualDrive.ShellExtension\VirtualDrive.ShellExtension.exe">
                    <com:Class 
                      Id="000562AA-2879-4CF1-89E8-0AEC9596FE19" />
                  </com:ExeServer>
                </com:ComServer>
              </com:Extension>
            </Extensions>
          </Application>
        </Applications>
      </Package>

      Note that you must specify where your surrogate COM or your exe COM server is located in com:Extension tag.

    • If you run the custom properties handler as COM exe inside main app:

      Add just the desktop3:CustomStateHandler tag:

      <Package 
          xmlns:desktop3="http://schemas.microsoft.com/appx/manifest/desktop/windows10/3"
          xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10" ...>
        <Applications>
          <Application ...>
            <Extensions>
              <desktop3:Extension Category="windows.cloudFiles">
                <desktop3:CloudFiles>
                  <desktop3:CustomStateHandler 
                    Clsid="754F334F-095C-46CD-B033-B2C0523D2829"/>
                </desktop3:CloudFiles>
              </desktop3:Extension>
            </Extensions>
          </Application>
        </Applications>
      </Package>

    Note that in case of a packaged app or sparse package your COM will be automatically registered on app install and deleted on uninstall. You do NOT need to install/uninstall it manually using regsvr32.exe or Win 32 API.

  • If you run COM surrogate and using a regular installer:

    Use the ShellExtensionRegistrar.Register() method or regsvr32.exe or Win 32 API to register your COM.
    ShellExtensionRegistrar.Register()

 

5. Run COM exe Server

If you run your COM exe server (either inside the main app or in a separate process) you need to register your COM class using the LocalServer.RegisterWinRTClass() method call. Add the following code to your application:

using (var server = new LocalServer())
{
    server.RegisterWinRTClass<IStorageProviderItemPropertySource, CustomStateProvider>();
    await server.Run();
}

It will allow COM calls to find COM object inside your exe. This step is NOT required in case of a surrogate COM.

 6. Show Custom Columns in Windows Explorer

By default custom columns are hidden. To show custom columns use t he "More..." context menu on the columns header in Windows Explorer.

Next Article:

Setting and Detecting Conflict Status