.NET Interface for Total Commander plugins

Provided interface allows to create Total Commander plugins using .NET supporting languages.

This product is provided under the MIT License.

  1. Main features.
  2. Common considerations.
  3. Content of the downloaded folders.
  4. How to create new TC plugin with provided .NET Interface.
  5. How it works.
  6. Advanced notes.
  7. Deployment.
  8. Source code.

1. Main features.

If you are .NET developer and have a good idea for Total Commander plugin - provided .NET Interface is the right choice for you.
With this interface you can concentrate on the main functionality of your plugin without having to worry about most of mundane tasks of TC plugin building.

Main features:


2. Common considerations.

2.1. Definition of terms (to avoid misunderstanding).

Hereinafter term plugin will be used in two different contexts:

2.2. .NET Framework version.

Provided interface allows creating managed plugins using .NET Framework version 2.0 and higher.
However, some steps in development process depend on .NET Framework version that developer uses to create new managed plugin.

Actually differences are related to CLR (Common Language Runtime) versions rather than .NET Framework versions.
The .NET Framework 4.0, 4.5, and later releases include CLR 4, but the .NET Framework 2.0, 3.0, and 3.5 include CLR 2.0. (There is no intermediate CLR version.)

Plugin wrapper - intermediate layer between Total Commander and managed plugin - has to be built in different ways depending on CLR version.
It's a reason to use separate wrapper builders for CLR2 and CLR4 in provided interface.
Please take your attention to item 9 in New plugin creation process section. It describes how to select proper wrapper builder.

2.3. Paths to .NET Framework utilities.

Provided interface uses some .NET Framework utilities in its work: ilasm.exe (IL Assembler), ildasm.exe (IL Disassembler), and gacutil.exe (Global Assembly Cache Tool).
Paths to these utilities depend on versions of .NET Framework and Visual Studio installed on developer computer.
WARNING: Please check if utilities' paths in provided interface correspond to the files on your computer and fix them if required in the following script and configuration files:


3. Content of the downloaded folders.

3.1. Folder 'Documentation'.

ReadMe.html This file
license.txt File with the MIT License text.
dotNetPluginMethods.xlsx Excel spreadsheet with description of each TC plugin method and specifics of their implementation in managed plugins.
Also it contains description of all settings used for plugin configuration.

3.2. Folder 'TcPluginCore'.

This folder contains files required for plugin developer to create and debug new managed plugins:
Assemblies in this folder were compiled in Debug configuration.

TcPluginInterface.dll TC plugin interface assembly; contains interfaces and base classes for TC plugins.
TcPluginInterface.pdb Debugging information for above assembly.
TcPluginTools.dll TC plugin utility assembly; contains classes for plugin loading, callbacks and error handling.
TcPluginTools.pdb Debugging information for above assembly.
TcWpfListerHandlerBuilder.dll Lister handler assembly used to create WPF based Lister plugins.
GAC_Install.bat Script to install core assemblies into the GAC (Global Assembly Cache). The assemblies are installed into CRL2 cache located in the %windir%\assembly folder.
GAC_Uninstall.bat Script to remove core assemblies from the GAC.
TcPluginLib.msi Windows Installer package - alternative way to install/remove TcPluginInterface and TcPluginTools assemblies into the GAC.
WpfLsHandlerBuilder.msi Windows Installer package - alternative way to install/remove TcWpfListerHandlerBuilder assembly into the GAC.
Totalcmd.exe.config Total Commander configuration file; it contains tracing settings for new plugin debugging.
Subfolder clr2 Contains the following files required to build TC plugin wrappers if managed plugin is developed with CLR2 (.NET Framework versions 2.0 - 3.5).
WrapperBuilder.exe Console program to build plugin wrapper DLL that exports required TC plugin methods.
WrapperBuilder.exe.config WrapperBuilder configuration specific for CLR2.
WfxWrapper.dll Template assembly for TC File System plugin wrapper.
WlxWrapper.dll Template assembly for TC Lister plugin wrapper.
WcxWrapper.dll Template assembly for TC Packer plugin wrapper.
WdxWrapper.dll Template assembly for TC Content plugin wrapper.
QSWrapper.dll Template assembly for TC QuickSearch plugin wrapper.
Subfolder clr4 Contains the files required to build TC plugin wrappers if managed plugin is developed with CLR4 (.NET Framework versions 4.0 and higher).
Files are the same as in clr2 subfolder except for the following:
WrapperBuilder.exe.config WrapperBuilder configuration specific for CLR4.

Store this folder on developer's computer and install main assemblies into the GAC using GAC_Install.bat or .msi files.
This folder should be accessible for any new managed plugin project. I recommend you to add it as subfolder to new project folder.

3.3. Folder 'Deployment'.

This folder contains two Windows Installer packages: TcPluginLib.msi and WpfLsHandlerBuilder.msi.
First package contains core assemblies required in order for any TC plugin developed with provided .NET Interface to work.
Second package contains TcWpfListerHandlerBuilder assembly required for WPF based Lister TC plugins.
Assemblies in these packages were compiled in Release configuration.

You have to provide these packages to the end user with your plugin. See details of package installation below.

3.4. Folder 'TcPluginSamples'.

This folder contains two sample solutions containing several sample TC plugins created with provided .NET Interface.
It’s good start point - please look through these samples before starting your own plugin.

Solution TcPluginSamples contains sample TC plugins created with .NET Framework 2.0.

FSSample File System plugin sample - just local file system
FSSampleWithContent The same as above with Content features
ListerSample Lister plugin sample - simple text viewer with trace log.
Created with Windows Forms user interface.
ListerSampleWpf Lister plugin sample - simple text viewer with trace log.
Created with Windows Presentation Foundation (WPF) user interface.
(.NET Framework 3.5 required)
PackerSampleCS Sample Packer plugin - MD5/SHA1 checksum generator/checker (idea of Stanislaw Y. Pusep)
PackerSampleZip Sample Packer plugin - simple ZIP packer using ICSharpCode.SharpZipLib assembly
ContentSample Simple Content plugin, shows additional file information columns
QSSample Simple QuickSearch plugin

Solution TcPluginSamples_clr4 contains sample TC plugins created with .NET Framework 4.0 or higher (CLR4).

FSSample_clr4 File System plugin sample - just local file system
ListerSample_clr4 Lister plugin sample created with Windows Forms user interface.
Simple file viewer with additional tab which displays TC calls to the plugin


4. How to create new TC plugin with provided .NET Interface.

4.1. Requirements.

  1. First of all - you have to have a good idea for new TC plugin.
    Otherwise provided .NET Interface will not help you! :-)

  2. IDE to create .NET assemblies. I used Microsoft Visual Studio (2008 and 2013).

    You can also try alternative IDEs, such as SharpDevelop, MonoDevelop or Embarcadero RAD Studio.

  3. Content of the downloaded TcPluginCore folder.
    You can use it to create any number of different managed plugins.

    Install required core assemblies into the GAC using GAC_Install.bat or .msi files.

4.2. New plugin creation process.

Below are steps to create new File System (wfx) plugin called YourPlugin.
Creation of another TC plugins (wlx, wdx or wcx) is similar.

  1. Create new .NET project YourPlugin.

  2. I recommend you to add the folder TcPluginCore from this distributive as subfolder to new project folder.
    Actually you can keep this folder anywhere on your hard drive; check paths in the steps 3 and 9 in this case.

  3. Add reference to assembly TcPluginCore\TcPluginInterface.dll.
    Set Copy Local property for the assembly to False.

  4. Create a class implementing your plugin functionality.
    Inherit it from base class FsPlugin defined in TcPluginInterface assembly.
    (Use base classes ListerPlugin, PackerPlugin, ContentPlugin, or QSPlugin for another plugin types.)

  5. Create constructor receiving one parameter of type StringDictionary - plugin settings dictionary.
    Do not forget to call the base constructor.

    public YourPlugin(StringDictionary pluginSettings)
        : base(pluginSettings)
    { <...Your code here...> };

  6. Create override methods for all mandatory and some optional methods of plugin interface. (You can find all method descriptions in the dotNetPluginMethods.xlsx spreadsheet.)
    Any optional methods not overridden in your plugin will be excluded from the plugin wrapper.

  7. Special cases:
  8. Build your project in Debug mode.

  9. Build plugin wrapper - intermediate layer between Total Commander and your managed plugin with proper .wfx extension.
    This wrapper is a real TC plugin using your managed plugin as a class library.

    Use WrapperBuilder.exe console application to do it (run "WrapperBuilder /?" for parameter details).

  10. After the build your output folder will usually contain the following files:

  11. Configure plugin - see details in Plugin Configuration section.
  12. After the full cycle of debugging rebuild your project in Release mode for deployment.

5. How it works.

Assembly TcPluginInterface.dll in current .NET Interface provides following class hierarchy:

Plan Setup Screen Sample

This layer provides base plugin functionality: configuration, communication with plugin loader, error handling and tracing capabilities, event handlers for callback functions, life cycle management.

Developer implements own managed class derived from one of the base plugin classes. It uses all flexibility and power of .NET Framework to implement main features provided in new plugin.

Managed plugin communicates with TC through plugin wrapper. This wrapper is a real TC plugin which methods are called by TC.
Wrapper templates (separate for each plugin type) are managed class libraries that translate TC calls into managed calls providing parameters marshaling and cross Application Domain boundary communication.
During the development stage WrapperBuilder program prepares wrapper template to become a real TC plugin:

Following scheme describes how TC, wrapper and managed plugin interact with each other.
Code in blue block is part of TC code, red block is managed plugin code, and black blocks contain code from provided .NET Interface

Plan Setup Screen Sample

  1. Total Commander calls some plugin function from the wrapper.
If this is the first call to plugin, steps 2 - 5 will be executed.
  1. Plugin Wrapper calls Plugin Loader to create an object for communication with TC.
  2. Plugin Loader reads configuration (file YourPlugin.wfx.config - see details) and defines assembly and class required for loading.
    Then it creates application domain (AD) for new managed plugin, loads required assembly there, and tries to create remote object of required class in the new AD.
    Working in separate AD provides isolation and security boundaries for executing managed code.
  3. Managed plugin constructor is called inside the plugin AD. It creates instance of required class. This instance is remote object whose methods will be called by plugin wrapper.
    Then plugin returns a proxy for created object over AD boundary.
    So, managed plugin assembly is loaded only into plugin-specific AD domain, not into the default one.
    Default AD only contains the code from the provided .NET Interface.
  4. Plugin Loader stores returned proxy object in the Plugin Wrapper.
    This proxy is used to transfer any subsequent TC calls to the managed plugin.
  1. Proxy calls appropriate method from the managed plugin, marshaling passed parameters to the managed types.
  2. Managed plugin executes calling method and returns result.
  3. Proxy transforms returned value to the required unmanaged type/structure, and returns result to TC.

6. Advanced notes.

6.1. Plugin Configuration.

Main plugin constructor receives one parameter of type StringDictionary - plugin settings, containing string key-value pairs.
Plugin settings are loaded from YourPlugin.w?x.config file (*.w?x64.config for 64-bit plugin version).
Loaded key-value pairs are stored in the property Settings of the TcPlugin class.

public StringDictionary Settings { get; private set; }

See Configuration tab of dotNetPlugins.xlsx spreadsheet for information about settings used by plugin loader and wrappers.

Each configuration key uses default value, so simple plugins can be written with no configuration file at all.
You can add more key-value pairs in your plugin specific configuration and access them via Settings property.

Following is an example of configuration file for simple Lister plugin:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="pluginAssembly" value="YourPlugin.dll"/>
    <add key="pluginClass" value="YourPluginTest"/>
    <add key="pluginTitle" value="Test Local File System"/>
    <add key="writeTrace" value="true"/>
    <add key="showAuthorInfo" value="false"/>
  </appSettings>
</configuration>

6.2. Tracing.

Tracing is available for debugging purposes. It works only if you build you plugin in Debug configuration, not in Release one.

To switch tracing ON set the following parameters in the plugin configuration file to true:

You also need to set tracing parameters in Total Commander. Use file Totalcmd.exe.config file in the TcPluginCore folder to do it.
Copy this file to the main TC folder. If you already have a file with this name, just add system.diagnostics section into it.
Then configure the output file for tracing (attribute initializeData in listeners.traceListener key) and desired trace level (attribute value in switches.DotNetPlugins key, see trace level details in the file)

Following plugin actions are traced:

Single trace line looks like:

<date> <time_with_milliseconds>  - <plugin_id>: <message>

<plugin_id> depends on plugin type and useTitleForTrace boolean configuration parameter:

Plugin type useTitleForTrace  <plugin_id>
Lister, Packer, Content(wdx) Plugin Title (read from configuration)
File System, Content (part of FS) true Plugin Title
File System, Content (part of FS) false (default) FS Plugin Number (initialized during FsInit call)

So real trace lines look like:

03/14/14 14:27:43.166 - 33: SetCryptCallback: CryptoNumber=1819109126, Flags=1,781CBB4
04/07/14 12:04:47.649 - .NET Lister: SendCommand (401858, NewParams, Ansi, Center): OK

6.3. File System plugins: managing the lifetime cycle.

Base managed plugin classes are inherited from the MarshalByRefObject class to allow communication across application domain boundaries.

Some of MarshalByRefObject methods allow control over the lifetime policy for managed plugin instance.

If your plugin has sensitive and secure connection to some external server (SFTP, Database, etc.) it can be helpful to limit connection time in idle state.
This feature is implemented in the base File System plugin class FsPlugin.

FsPlugin-overridden method InitializeLifetimeService associates a lifetime lease object with the plugin. This lease object is periodically examined by the application domain lease manager.
After the lease expires the managed plugin instance become not accessible. It works like the timeout feature for remote objects.

Plugin sets lease properties via plugin configuration parameters:

Parameter values must be valid time interval strings (see help for System.TimeSpan for details).

You can override InitializeLifetimeService method in your File System plugin to implement more complex lifetime strategy, including leases's sponsor support.

See more details about remote object lifecycle in Microsoft documentation.

I didn't implement this feature for other plugin types (lister, packer or content) in the provided interface because it seems useless for plugins other than File System.
But it can be easy implemented for any plugin type if needed.

6.4. File System plugins: content features.

IFsPlugin interface doesn't have methods for corresponding FsContent* functions. It has property of type ContentPlugin instead.

You can implement Content features for your File System plugin in different ways:

Plugin loader uses configuration parameters contentAssembly and contentClass (in addition to pluginAssembly and pluginClass) to search for class that implements content methods.
If plugin loader finds no such class, wrapper builder will exclude all FsContent* methods from created File System plugin wrapper.

6.5. Lister plugins: WinForms vs. WPF.

You can use both Windows Forms and Windows Presentation Foundation (WPF) user interface to create managed Lister plugin.

You need to create your own visual container class and return an instance of this class as a result of call to Load managed plugin method.
Derive your container class from:

Then WLX wrapper embeds this container as a child into the parent Total Commander Lister window, creates handle for the container and returns this handler back to TC.

WLX wrapper uses IListerHandlerBuilder interface to create handler for the visual container created in managed Lister plugin.
Implementation of this interface for WinForms is included in TcPluginInterface assembly.
But for WPF this interface is implemented in separate TcWPFListerHandlerBuilder assembly.

If you use WPF to create managed Lister plugin, you need to inform WLX wrapper to use corresponding handler builder class.
Set listerHandlerBuilderClass configuration key to "WPF" in the plugin configuration file to do it.

Another problem with WPF based Lister plugins is that I cannot find how to make WPF visual container serializable. (Sorry, I'm not familiar with WPF development ☹)
As a result this visual container cannot be passed over Application Domain boundary. It requires to load WPF based Lister managed plugin into default Application Domain.
Set startInDefaultDomain configuration key to "true" to do it.

Finally you have to have the following keys in the plugin configuration file for WPF based lister plugins:

    <add key="startInDefaultDomain" value="true"/>
    <add key="listerHandlerBuilderClass" value="WPF"/>

6.6. WinForms Lister plugins: keyboard handling.

Content of the managed Lister plugin visual container is displayed inside the Total Commander Lister window.
We want managed plugin to send some keyboard shortcuts into the TC Lister menu, such as:

When WLX wrapper loads visual container into the Lister window, it also sets keyboard handler that intercepts keyboard combinations defined in Lister menu and send them into parent window.

If new managed Lister plugin is WinForms based, visual container if derived from System.Windows.Forms.UserControl class.
Unfortunately, this class avoids getting the focus unless you take special programming tricks. It also won't respond meaningfully to keyboard messages even when it does have focus.

It's a reason to have special property in managed Lister plugin

public object FocusedControl { get; set; }

to notify WLX wrapper what visual control of WinForms based plugin will send keyboard shortcuts to the parent window.
Just set this property to the topmost visual control inside the visual container returned by Load method call.

Otherwise, if FocusedControl == null, WinForms based managed Lister plugin will not communicate to parent TC Lister window.


7. Deployment.

The following components have to be installed on the client computer to run newly-created plugin:

  1. Microsoft .NET Framework,
  2. Core assemblies from the provided .NET Plugin Interface,
  3. Content of the output folder of the new plugin project.

Components 1 and 2 are required for any plugin built with provided Interface and should only be installed once on client computer.
Component 3 is specific for concrete plugin.

7.1. Microsoft .NET Framework.

Required version depends on version used to create new managed plugin.
Core .NET Interface assemblies require .NET Framework 2.0.
TcWpfListerHandlerBuilder assembly for WPF based Lister plugins requires .NET Framework 3.5.

In most cases .NET Framework is already installed on the client computer.
Client can download required version from the official site.

7.2. Core .NET Plugin Interface assemblies.

The preferred place for these assemblies is .NET Global Assembly Cache (GAC) on client computer.

Two assemblies from provided .NET Plugin Interface - TcPluginInterface and TcPluginTools - are required in order for any TC plugin developed with provided .NET Interface to work.
Deployment folder contains Windows Installer package TcPluginLib.msi used to install/remove the Release versions of required assemblies into the GAC.

Assembly TcWpfListerHandlerBuilder is required for WPF based Lister plugins developed with provided interface.
Windows Installer package WpfLsHandlerBuilder.msi is used to install/remove the Release version of this assembly into the GAC.

Installation package(s) has to be executed on the client computer before plugin installation.

If required assemblies were already installed on client computer before (during another managed plugin installation) - installation packages will show Repair/Remove screen after start.
In this case client can ignore installation.

7.3. Plugin folder.

After the full cycle of debugging plugin developer will have the following files in the output (Release) folder:

Also there can be some additional files, depending on new plugin architecture:

You have to provide whole plugin folder for the client to deploy.

You also can add appropriate pluginst.inf file, pack your plugin folder, and deploy it using TC plugin auto-installation.


8. Source code for provided .NET Interface.

Source code is available at project's SourceForge page.

Code is provided under the MIT License.

Folder TcPluginCore contains full source code for Microsoft Visual Studio solution containing the following projects:

TcPluginInterface Class library with interfaces and base classes for managed plugins.
TcPluginTools Class library with classes for plugin loading, callbacks and error handling.
WfxWrapper Class library with template for File System plugin wrapper.
WlxWrapper Class library with template for Lister plugin wrapper.
TcWpfListerHandlerBuilder Class library with plugin handler builder used to create WPF based Lister plugins.
WcxWrapper Class library with template for Packer plugin wrapper.
WdxWrapper Class library with template for Content plugin wrapper
QSWrapper Class library with template for QuickSearch plugin wrapper
WrapperBuilder Console application to build plugin wrapper DLL.
TcPluginLibSetup Windows Installer project. Creates MSI file installing main plugin assemblies (TcPluginInterface and TcPluginTools) into the GAC.
WpfLsHandlerBuilderSetup Windows Installer project. Creates MSI file installing TcWpfListerHandlerBuilder assembly into the GAC.

After solution build folder TcPluginCore\Build\Debug will contain the same files as in the distributed TcPluginCore folder.
Folder TcPluginCore\Build\Release will contain the same files as in the distributed Deployment folder.



Please contact to author if you have some questions.

Please let me know if this product is helpful for your development.
Also please send me links to the new TC plugins created with provided interface.