Writing WCX packer plugins for Total Commander (Ver. 2.21 SE)

This text is extracted from the original help file written in part by Jiri Barton.

The latest version of this reference and some packer plugins (part of them with source in C or Delphi) should be available on www.ghisler.com, addons section.

Copyright © 2000-2011 by Christian Ghisler, Ghisler Software GmbH. All Rights Reserved.

Table of Contents

  1. Overview
  2. What's new
  3. Functions
    1. mandatory:
    2. optional:
  4. Structures
  5. Callbacks
  6. Error Codes
  7. Unicode support
  8. New: 64-bit support
  9. Header files

Overview

This help file is about writing packer plugins for the file manager Total Commander, available on www.ghisler.com. It describes the functions you need to implement to add a specific packer to Total Commander. You should also look at the available sample packers (with source), which give you some insight on plugin programming. There are samples for Microsoft Visual C++ and Delphi.

A WCX is nothing more than a 32-bit Windows DLL renamed to *.WCX, which supports a list of specific functions. Total Commander loads this library dynamically at runtime with LoadLibrary(), and loads all available functions with GetProcAddress(). This means that not all functions described here must be implemented (see below). All functions use the STDCALL calling convention with no C++ name mangling (see below), exactly like in most standard system libraries in Windows.

The minimum functions needed for a read-only plugin are:

All the following functions are optional. If you want to support them, you need to implement GetPackerCaps too, to tell Total Commander which functions are supported. If GetPackerCaps isn't available, Total Commander assumes that the plugin only supports unpacking. Even with a read-only plugin, you may want to implement GetPackerCaps and return PK_CAPS_SEARCHTEXT to allow Total Commander to search for text in archives of this type.

The first group allows to create or modify existing archives:

The following optional functions are for packing in memory:

This is used by Total Commander to create TAR.Plugin files in one step. For example, the .BZ2 plugin supports these functions. Most plugins can pack multiple files into one archive, and therefore will not need to implement these functions.

The following function tells the plugin to check whether it can handle the specified unknown file or not:

How Total Commander calls the extraction functions:

Here is a simple pseudocode declaration how Total Commander calls the extraction functions:

  1. Loop to scan for files in the archive:
    OpenArchive()          with OpenMode==PK_OM_LIST
    repeat
       ReadHeader()
       ProcessFile(...,PK_SKIP,...)
    until error returned
    CloseArchive()
  2. Loop to extract files from the archive:
    OpenArchive()          with OpenMode==PK_OM_EXTRACT
    repeat
       ReadHeader()
       if WantToExtractThisFile()
          ProcessFile(...,PK_EXTRACT,...)
       else
          ProcessFile(...,PK_SKIP,...)
    until error returned
    CloseArchive()

Important note for C and C++ Programmers with Visual C++:

If using __stdcall the linker produces file names like "_ReadHeader@8"

To get a compatible plugin a "Def" file e.g. wcx.def has to be added to your project.

--Begin of wxc.def file to change export names for linker

EXPORTS

CloseArchive=_CloseArchive@4
DeleteFiles=_DeleteFiles@8
GetPackerCaps=_GetPackerCaps@0
OpenArchive=_OpenArchive@4
PackFiles=_PackFiles@20
ProcessFile=_ProcessFile@16
ReadHeader=_ReadHeader@8
SetChangeVolProc=_SetChangeVolProc@8
SetProcessDataProc=_SetProcessDataProc@8
ConfigurePacker=_ConfigurePacker@8

--End of wcx.def file

What's new

The following function has been added to plugin version 2.21 second edition (all functions are otherwise unchanged):

The following function has been added to plugin version 2.21. It's optional:

The following function has been added to plugin version 2.20. It's optional:

The following function has been added to plugin version 2.12. It's optional:

The following function has been added to plugin version 2.1. It's optional:

The following functions have been added in packer plugin version 2.0. All of them are optional. Plugins written with the first specification will continue to work without changes.

OpenArchive

OpenArchive should perform all necessary operations when an archive is to be opened.

HANDLE __stdcall OpenArchive (tOpenArchiveData *ArchiveData);

Description

OpenArchive should return a unique handle representing the archive. The handle should remain valid until CloseArchive is called. If an error occurs, you should return zero, and specify the error by setting OpenResult member of ArchiveData.

You can use the ArchiveData to query information about the archive being open, and store the information in ArchiveData to some location that can be accessed via the handle.

ReadHeader

Totalcmd calls ReadHeader to find out what files are in the archive.

int __stdcall ReadHeader (HANDLE hArcData, tHeaderData *HeaderData);

Description

ReadHeader is called as long as it returns zero (as long as the previous call to this function returned zero). Each time it is called, HeaderData is supposed to provide Totalcmd with information about the next file contained in the archive. When all files in the archive have been returned, ReadHeader should return E_END_ARCHIVE which will prevent ReaderHeader from being called again. If an error occurs, ReadHeader should return one of the error values or 0 for no error.

hArcData contains the handle returned by OpenArchive. The programmer is encouraged to store other information in the location that can be accessed via this handle. For example, you may want to store the position in the archive when returning files information in ReadHeader.

In short, you are supposed to set at least PackSize, UnpSize, FileTime, and FileName members of tHeaderData. Totalcmd will use this information to display content of the archive when the archive is viewed as a directory.

ProcessFile

ProcessFile should unpack the specified file or test the integrity of the archive.

int __stdcall ProcessFile (HANDLE hArcData, int Operation, char *DestPath, char *DestName);

Description

ProcessFile should return zero on success, or one of the error values otherwise.

hArcData contains the handle previously returned by you in OpenArchive. Using this, you should be able to find out information (such as the archive filename) that you need for extracting files from the archive.

Unlike PackFiles, ProcessFile is passed only one filename. Either DestName contains the full path and file name and DestPath is NULL, or DestName contains only the file name and DestPath the file path. This is done for compatibility with unrar.dll.

When Total Commander first opens an archive, it scans all file names with OpenMode==PK_OM_LIST, so ReadHeader() is called in a loop with calling ProcessFile(...,PK_SKIP,...). When the user has selected some files and started to decompress them, Total Commander again calls ReadHeader() in a loop. For each file which is to be extracted, Total Commander calls ProcessFile() with Operation==PK_EXTRACT immediately after the ReadHeader() call for this file. If the file needs to be skipped, it calls it with Operation==PK_SKIP.

Each time DestName is set to contain the filename to be extracted, tested, or skipped. To find out what operation out of these last three you should apply to the current file within the archive, Operation is set to one of the following:

ConstantValueDescription
PK_SKIP0Skip this file
PK_TEST1Test file integrity
PK_EXTRACT2Extract to disk

CloseArchive

CloseArchive should perform all necessary operations when an archive is about to be closed.

int __stdcall CloseArchive (HANDLE hArcData);

Description

CloseArchive should return zero on success, or one of the error values otherwise. It should free all the resources associated with the open archive.

The parameter hArcData refers to the value returned by a programmer within a previous call to OpenArchive.

SetChangeVolProc

This function allows you to notify user about changing a volume when packing files.

void __stdcall SetChangeVolProc (HANDLE hArcData, tChangeVolProc pChangeVolProc1);

Description

pChangeVolProc1 contains a pointer to a function that you may want to call when notifying user to change volume (e.g. insterting another diskette). You need to store the value at some place if you want to use it; you can use hArcData that you have returned by OpenArchive to identify that place.

SetProcessDataProc

This function allows you to notify user about the progress when you un/pack files.

void __stdcall SetProcessDataProc (HANDLE hArcData, tProcessDataProc pProcessDataProc);

Description

pProcessDataProc contains a pointer to a function that you may want to call when notifying user about the progress being made when you pack or extract files from an archive. You need to store the value at some place if you want to use it; you can use hArcData that you have returned by OpenArchive to identify that place.

PackFiles

PackFiles specifies what should happen when a user creates, or adds files to the archive.

int __stdcall PackFiles (char *PackedFile, char *SubPath, char *SrcPath, char *AddList, int Flags);

Description

PackFiles should return zero on success, or one of the error values otherwise.

PackedFile refers to the archive that is to be created or modified. The string contains the full path.

SubPath is either NULL, when the files should be packed with the paths given with the file names, or not NULL when they should be placed below the given subdirectory within the archive. Example:

SrcPath contains path to the files in AddList. SrcPath and AddList together specify files that are to be packed into PackedFile. Each string in AddList is zero-delimited (ends in zero), and the AddList string ends with an extra zero byte, i.e. there are two zero bytes at the end of AddList.

Flags can contain a combination of the following values reflecting the user choice from within Totalcmd:

ConstantValueDescription
PK_PACK_MOVE_FILES1Delete original after packing
PK_PACK_SAVE_PATHS2Save path names of files
PK_PACK_ENCRYPT4Ask user for password, then encrypt file with that password

DeleteFiles

DeleteFiles should delete the specified files from the archive

int __stdcall DeleteFiles (char *PackedFile, char *DeleteList);

Description

DeleteFiles should return zero on success, or one of the error values otherwise.

PackedFile contains full path and name of the archive.

DeleteList contains the list of files that should be deleted from the archive. The format of this string is the same as AddList within PackFiles.

GetPackerCaps

GetPackerCaps tells Totalcmd what features your packer plugin supports.

int __stdcall GetPackerCaps();

Description

Implement GetPackerCaps to return a combination of the following values:

ConstantValueDescription
PK_CAPS_NEW1Can create new archives
PK_CAPS_MODIFY2Can modify existing archives
PK_CAPS_MULTIPLE4Archive can contain multiple files
PK_CAPS_DELETE8Can delete files
PK_CAPS_OPTIONS16Has options dialog
PK_CAPS_MEMPACK32Supports packing in memory
PK_CAPS_BY_CONTENT64Detect archive type by content
PK_CAPS_SEARCHTEXT128Allow searching for text in archives created with this plugin
PK_CAPS_HIDE256Don't show packer icon, don't open with Enter but with Ctrl+PgDn
PK_CAPS_ENCRYPT512Plugin supports encryption.

Omitting PK_CAPS_NEW and PK_CAPS_MODIFY means PackFiles will never be called and so you don't have to implement PackFiles.

Omitting PK_CAPS_MULTIPLE means PackFiles will be supplied with just one file.

Leaving out PK_CAPS_DELETE means DeleteFiles will never be called.

Leaving out PK_CAPS_OPTIONS means ConfigurePacker will not be called.

PK_CAPS_MEMPACK enables the functions StartMemPack, PackToMem and DoneMemPack.

If PK_CAPS_BY_CONTENT is returned, Totalcmd calls the function CanYouHandleThisFile when the user presses Ctrl+PageDown on an unknown archive type.

Finally, if PK_CAPS_SEARCHTEXT is returned, Total Commander will search for text inside files packed with this plugin. This may not be a good idea for certain plugins like the diskdir plugin, where file contents may not be available.

If PK_CAPS_HIDE is set, the plugin will not show the file type as a packer. This is useful for plugins which are mainly used for creating files, e.g. to create batch files, avi files etc. The file needs to be opened with Ctrl+PgDn in this case, because Enter will launch the associated application.

Important note

If you change the return values of this function, e.g. add packing support, you need to reinstall the packer plugin in Total Commander, otherwise it will not detect the new capabilities.

ConfigurePacker

ConfigurePacker gets called when the user clicks the Configure button from within "Pack files..." dialog box in Totalcmd.

void __stdcall ConfigurePacker (HWND Parent, HINSTANCE DllInstance);

Description

Usually, you provide a user with a dialog box specifying a method and/or its parameters that should be applied in the packing process. Or, you just want to display a message box about what your plugin is, just like Christian Ghisler's DiskDir does.

In order to help you with a feedback, you can use a window handle of Totalcmd process, Parent. That is, you make your dialog box a child of Parent.

When creating a window, you may also need handle of the DLL (your DLL) that creates your dialog box, DllInstance.

You may decide not to implement this function. Then, make sure you omit PK_CAPS_OPTIONS from return values of GetPackerCaps.

StartMemPack

StartMemPack starts packing into memory. This function is only needed if you want to create archives in combination with TAR, e.g. TAR.BZ2. It allows Totalcmd to create a TAR.Plugin file in a single step.

int __stdcall StartMemPack (int Options, char *FileName);

Description

StartMemPack should return a user-defined handle (e.g. pointer to a structure) on success, zero otherwise.

FileName refers to the name of the file being packed - some packers store the name in the local header.

Options can contain a combination of the following values:

ConstantValueDescription
MEM_OPTIONS_WANTHEADERS1The output stream should include the complete headers (beginning+end)

PackToMem

PackToMem packs the next chunk of data passed to it and/or returns the compressed data to the calling program. It is implemented together with StartMemPack and DoneMemPack

int __stdcall PackToMem (int hMemPack, char* BufIn, int InLen, int* Taken, char* BufOut, int OutLen, int* Written, int SeekBy);

Description of the fields

PackToMem should return MEMPACK_OK (=0) on success, MEMPACK_DONE (=1) when done, or one of the error values otherwise.

hMemPack is the handle returned by StartMemPack()

BufIn is a pointer to the data which needs to be packed

InLen contains the number of bytes pointed to by BufIn

Taken has to receive the number of bytes taken from the buffer. If not the whole buffer is taken, the calling program will pass the remaining bytes to the plugin in a later call.

BufOut is a pointer to a buffer which can receive packed data

OutLen contains the size of the buffer pointed to by BufOut

Written has to receive the number of bytes placed in the buffer pointed to by BufOut

SeekBy may be set to the offset from the current output position by which the file pointer has to be moved BEFORE accepting the data in BufOut. This allows the plugin to modify a file header also AFTER packing, e.g. to write a CRC to the header.

Description of the function

PackToMem is the most complex function of the packer plugin. It is called by Total Commander in a loop as long as there is data to be packed, and as there is data to retrieve. The plugin should do the following:

  1. As long as there is data sent through BufIn, take it and add it to your internal buffers (if there is enough space).
  2. As soon as there is enough data in the internal input buffers, start packing to the output buffers.
  3. As soon as there is enough data in the internal output buffers, start sending data to BufOut.
  4. When InLen is 0, there is no more data to be compressed, so finish sending data to BufOut until no more data is in the output buffer.
  5. When there is no more data available, return 1.
  6. There is no obligation to take any data through BufIn or send any through BufOut. Total Commander will call this function until it either returns 1, or an error.

DoneMemPack

DoneMemPack ends packing into memory. This function is used together with StartMemPack and PackToMem.

int __stdcall DoneMemPack (int hMemPack);

Description

Return value: DoneMemPack should return zero if successful, or one of the error codes otherwise.

hMemPack is the handle returned by StartMemPack.

It may be called in two different cases:

  1. The packing functions have completed successfully, or
  2. The user has aborted the packing operation.
The plugin should free all data allocated when packing.

CanYouHandleThisFile

CanYouHandleThisFile allows the plugin to handle files with different extensions than the one defined in Total Commander. It is called when the plugin defines PK_CAPS_BY_CONTENT, and the user tries to open an archive with Ctrl+PageDown.

BOOL __stdcall CanYouHandleThisFile (char *FileName);

Description

CanYouHandleThisFile should return true (nonzero) if the plugin recognizes the file as an archive which it can handle. The detection must be by contents, NOT by extension. If this function is not implemented, Totalcmd assumes that only files with a given extension can be handled by the plugin.

Filename contains the fully qualified name (path+name) of the file to be checked.

PackSetDefaultParams

PackSetDefaultParams is called immediately after loading the DLL, before any other function. This function is new in version 2.1. It requires Total Commander >=5.51, but is ignored by older versions.

Declaration

void __stdcall PackSetDefaultParams(PackDefaultParamStruct* dps);

Description of parameters

dpsThis structure of type PackDefaultParamStruct currently contains the version number of the plugin interface, and the suggested location for the settings file (ini file). It is recommended to store any plugin-specific information either directly in that file, or in that directory under a different name. Make sure to use a unique header when storing data in this file, because it is shared by other file system plugins! If your plugin needs more than 1kbyte of data, you should use your own ini file because ini files are limited to 64k.

Return value: the function has no return value.

Important note

This function is only called in Total Commander 5.51 and later. The plugin version will be >= 2.1.

PkSetCryptCallback

PkSetCryptCallback is called when loading the plugin. The passed values should be stored in the plugin for later use. This function is only needed if you want to use the secure password store in Total Commander.

Declaration

void __stdcall PkSetCryptCallback(tPkCryptProc pPkCryptProc, int CryptoNr, int Flags);

Description of parameters

pPkCryptProcPointer to the crypto callback function. See PkCryptProc for a description of this function
CryptoNrA parameter which needs to be passed to the callback function
FlagsFlags regarding the crypto connection. Currently only PK_CRYPTOPT_MASTERPASS_SET is defined. It is set when the user has defined a master password.

Return value: this function does not return any value.

Remarks

You can use this callback function to store passwords in Total Commander's secure password store. The user will be asked for the master password automatically.

ReadHeaderEx

Totalcmd calls ReadHeaderEx to find out what files are in the archive. This function is always called instead of ReadHeader if it is present. It only needs to be implemented if the supported archive type may contain files >2 GB. You should implement both ReadHeader and ReadHeaderEx in this case, for compatibility with older versions of Total Commander.

int __stdcall ReadHeaderEx (HANDLE hArcData, tHeaderDataEx *HeaderDataEx);

Description

ReadHeaderEx is called as long as it returns zero (as long as the previous call to this function returned zero). Each time it is called, HeaderDataEx is supposed to provide Totalcmd with information about the next file contained in the archive. When all files in the archive have been returned, ReadHeaderEx should return E_END_ARCHIVE which will prevent ReaderHeaderEx from being called again. If an error occurs, ReadHeaderEx should return one of the error values or 0 for no error.

hArcData contains the handle returned by OpenArchive. The programmer is encouraged to store other information in the location that can be accessed via this handle. For example, you may want to store the position in the archive when returning files information in ReadHeaderEx.

In short, you are supposed to set at least PackSize, PackSizeHigh, UnpSize, UnpSizeHigh, FileTime, and FileName members of tHeaderDataEx. Totalcmd will use this information to display content of the archive when the archive is viewed as a directory.

GetBackgroundFlags

GetBackgroundFlags is called to determine whether a plugin supports background packing or unpacking.

int __stdcall GetBackgroundFlags(void);

Description

GetBackgroundFlags should return one of the following values:

ConstantValueDescription
BACKGROUND_UNPACK1Calls to OpenArchive, ReadHeader(Ex), ProcessFile and CloseArchive are thread-safe (unpack in background)
BACKGROUND_PACK2Calls to PackFiles are thread-safe (pack in background)
BACKGROUND_MEMPACK4Calls to StartMemPack, PackToMem and DoneMemPack are thread-safe

Notes

To make your packer plugin thread-safe, you should remove any global variables which aren't the same for all pack or unpack operations. For example, the path to the ini file name can remain global, but something like the compression ratio, or file handles need to be stored separately.

Packing: The PackFiles function is just a single call, so you can store all variables on the stack (local variables of that function).

Unpacking: You can allocate a struct containing all the variables you need across function calls, like the compression method and ratio, and state variables, and return a pointer to this struct as a result to OpenArchive. This pointer will then passed to all other functions like ReadHeader as parameter hArcData.

Pack in memory: You can do the same in StartMemPack as described under Unpacking.

tHeaderData

tHeaderData is a structure used in ReadHeader.

typedef struct {
	char ArcName[260];
	char FileName[260];
	int Flags;
	int PackSize;
	int UnpSize;
	int HostOS;
	int FileCRC;
	int FileTime;
	int UnpVer;
	int Method;
	int FileAttr;
	char* CmtBuf;
	int CmtBufSize;
	int CmtSize;
	int CmtState;
} tHeaderData;

Description

ArcName, FileName, PackSize, UnpSize contain the name of the archive, the name of the file within the archive, size of the file when packed, and the size of the file when extracted, respectively.

HostOS is there for compatibility with unrar.dll only, and should be set to zero.

FileCRC is the 32-bit CRC (cyclic redundancy check) checksum of the file. If not available, set to zero.

The Cmt* values can be used to transfer file comment information. They are currently not used in Total Commander, so they may be set to zero.

FileAttr can be set to any combination of the following values:

ValueDescription
0x1Read-only file
0x2Hidden file
0x4System file
0x8Volume ID file
0x10Directory
0x20Archive file
0x3FAny file

FileTime contains the date and the time of the file's last update. Use the following algorithm to set the value:

FileTime = (year - 1980) << 25 | month << 21 | day << 16 | hour << 11 | minute << 5 | second/2;

Make sure that:

tHeaderDataEx

tHeaderDataEx is a structure used in ReadHeaderEx.

typedef struct {
	char ArcName[1024];
	char FileName[1024];
	int Flags;
	unsigned int PackSize;
	unsigned int PackSizeHigh;
	unsigned int UnpSize;
	unsigned int UnpSizeHigh;
	int HostOS;
	int FileCRC;
	int FileTime;
	int UnpVer;
	int Method;
	int FileAttr;
	char* CmtBuf;
	int CmtBufSize;
	int CmtSize;
	int CmtState;
	char Reserved[1024];
} tHeaderDataEx;

Description

ArcName, FileName, PackSize, UnpSize contain the name of the archive, the name of the file within the archive, size of the file when packed, and the size of the file when extracted, respectively. PackSizeHigh, UnpSizeHigh contain the upper 32 bit of a 64-bit size number. Set to 0 if the file is smaller than 4 GB.

HostOS is there for compatibility with unrar.dll only, and should be set to zero.

FileCRC is the 32-bit CRC (cyclic redundancy check) checksum of the file. If not available, set to zero.

The Cmt* values can be used to transfer file comment information. They are currently not used in Total Commander, so they may be set to zero.

FileAttr can be set to any combination of the following values:

ValueDescription
0x1Read-only file
0x2Hidden file
0x4System file
0x8Volume ID file
0x10Directory
0x20Archive file
0x3FAny file

FileTime contains the date and the time of the file's last update. Use the following algorithm to set the value:

FileTime = (year - 1980) << 25 | month << 21 | day << 16 | hour << 11 | minute << 5 | second/2;

Make sure that:

Reserved may be used in the future for additional data - you MUST set it to 0 for now to avoid problems with future versions of TC.

Note

The Unicode version of this structure uses WCHAR[1024] for ArcName and FileName. "Reserved" is unchanged.

tOpenArchiveData

tOpenArchiveData is used in OpenArchive.

typedef struct {
	char* ArcName;
	int OpenMode;
	int OpenResult;
	char* CmtBuf;
	int CmtBufSize;
	int CmtSize;
	int CmtState;
} tOpenArchiveData;

Description

ArcName contains the name of the archive to open.

OpenMode is set to one of the following values:

ConstantValueDescription
PK_OM_LIST0Open file for reading of file names only
PK_OM_EXTRACT1Open file for processing (extract or test)

OpenResult used to return one of the error values if an error occurs.

The Cmt* variables are for the file comment. They are currently not used by Total Commander, so may be set to NULL.

Notes

If the file is opened with OpenMode==PK_OM_LIST, ProcessFile will never be called by Total Commander.

The Unicode version of this function uses WCHAR* instead of char* for the text fields.

PackDefaultParamStruct

PackDefaultParamStruct is passed to PackSetDefaultParams to inform the plugin about the current plugin interface version and ini file location.

Declaration

typedef struct {
	int size;
	DWORD PluginInterfaceVersionLow;
	DWORD PluginInterfaceVersionHi;
	char DefaultIniName[MAX_PATH];
} PackDefaultParamStruct;

Description of struct members

sizeThe size of the structure, in bytes. Later revisions of the plugin interface may add more structure members, and will adjust this size field accordingly.
PluginInterfaceVersionLowLow value of plugin interface version. This is the value after the comma, multiplied by 100! Example. For plugin interface version 2.1, the low DWORD is 10 and the high DWORD is 2.
PluginInterfaceVersionHiHigh value of plugin interface version.
DefaultIniNameSuggested location+name of the ini file where the plugin could store its data. This is a fully qualified path+file name, and will be in the same directory as the wincmd.ini. It's recommended to store the plugin data in this file or at least in this directory, because the plugin directory or the Windows directory may not be writable!

tProcessDataProc

tProcessDataProc is a typedef of the function that notifies the user about the progress when un/packing files.

typedef int (__stdcall *tProcessDataProc)(char *FileName, int Size);

Description

SetProcessDataProc has provided you with a pointer to a function with this declaration. When you want to notify the user about the progress when un/packing files, call this function with appropriate parameters. The function itself is part of Totalcmd - you only specify what Totalcmd should display. In addition, Totalcmd displays the Cancel button that allows the user to abort the un/packing process. If the user has clicked on Cancel, the function returns zero.

FileName can be used to pass a pointer to the currently processed filename (0 terminated string), or NULL if it is not available.

Set Size to the number of bytes processed since the previous call to the function. For plugins which unpack in CloseArchive: Set size to negative percent value (-1..-100) to directly set first percent bar, -1000..-1100 for second percent bar (-1000=0%).

Note

The keyword or constant __stdcall must be set according to the compiler that you will use to make the library. For example, this is STDCALL for cygwin and __stdcall for MSC.

tChangeVolProc

tChangeValueProc is a typedef of the function that asks the user to change volume.

typedef int (__stdcall *tChangeVolProc)(char *ArcName, int Mode);

Description

SetChangeVolProc has provided you with a pointer to a function with this declaration. When you want the user to be asked about changing volume, call this function with appropriate parameters. The function itself is part of Totalcmd - you only specify the question. Totalcmd then asks the user, and you get the answer as the result of the call to this function. If the user has aborted the operation, the function returns zero.

ArcName specifies the filename of the archive that you are processing, and will receive the name of the next volume.

Set Mode to one of the following values, according to what you want Totalcmd to ask the user:

ConstantValueDescription
PK_VOL_ASK0Ask user for location of next volume
PK_VOL_NOTIFY1Notify app that next volume will be unpacked

Note

The keyword or constant __stdcall must be set according to the compiler that you will use to make the library. For example, this is STDCALL for cygwin and __stdcall for MSC.

tPkCryptProc

tPkCryptProc is a callback function, which the plugin can call to store passwords in the secure password store, read them back, or copy them to a new connection.

Declaration

typedef int (__stdcall *tPkCryptProc)(int CryptoNumber, int mode, char* ArchiveName, char* Password, int maxlen);

Description of parameters

CryptoNumberHere the plugin needs to pass the crypto number received through the PkSetCryptCallback function.
modeThe mode of operation:
  • PK_CRYPT_SAVE_PASSWORD: Save password to password store
  • PK_CRYPT_LOAD_PASSWORD: Load password from password store
  • PK_CRYPT_LOAD_PASSWORD_NO_UI: Load password only if master password has already been entered
  • PK_CRYPT_COPY_PASSWORD: Copy password to new connection. Here the second string parameter "Password" is not a password, but the name of the target archive name
  • PK_CRYPT_MOVE_PASSWORD: As above, but delete the source password
  • PK_CRYPT_DELETE_PASSWORD: Delete the password of the given archive name
ArchiveNameName of the archive for this operation. The plugin can give any name here which can be stored in Windows ini files. The plugin should encode names which cannot be stored in ini files, or give a reference code or so instead of the file name.
PasswordOperation-specific, usually the password to be stored/retrieved, or the target name when copying/moving a connection
maxlenMaximum length, in characters, the password buffer can store when calling one of the load functions

Return value

Total Commander returns one of these values:

FS_FILE_OKSuccess
E_ECREATEEncrypt/Decrypt failed
E_EWRITECould not write password to password store
E_EREADPassword not found in password store
E_NO_FILESNo master password entered yet

Note

When showing the details of an existing archive, you should call PK_CRYPT_LOAD_PASSWORD_NO_UI first. In case of error E_NO_FILES, show a button "Edit password". Only call PK_CRYPT_LOAD_PASSWORD when the user clicks that button, or tries to decrypt the archive. This way the user doesn't have to enter the master password if he just wanted to make some other changes to the archive settings.

Error Codes

Use the following values when you want to inform Totalcmd that an error ocurred.

ConstantValueDescription
0Success
E_END_ARCHIVE10No more files in archive
E_NO_MEMORY11Not enough memory
E_BAD_DATA12CRC error in the data of the currently unpacked file
E_BAD_ARCHIVE13The archive as a whole is bad, e.g. damaged headers
E_UNKNOWN_FORMAT14Archive format unknown
E_EOPEN15Cannot open existing file
E_ECREATE16Cannot create file
E_ECLOSE17Error closing file
E_EREAD18Error reading from file
E_EWRITE19Error writing to file
E_SMALL_BUF20Buffer too small
E_EABORTED21Function aborted by user
E_NO_FILES22No files found
E_TOO_MANY_FILES23Too many files to pack
E_NOT_SUPPORTED24Function not supported

Unicode Support

With Total Commander 7.5 (packer plugin interface 2.20), Unicode support has been added to all plugin types. In principle, you need to implement the same functions as for ANSI, with two differences: The function name is changed from FunctionName to FunctionNameW, and Ansi strings are changed to wide char names.

Total Commander will call the Unicode functions on all NT-based systems (Windows NT, 2000, XP) if they are present. If not, or on Windows 9x/ME, Total Commander will call the Ansi functions.

The following functions of the packer plugin interface support Unicode:

The following functions do not exist in a Unicode form and must be implemented as Ansi:

What's the easiest way to support Unicode in an existing plugin?

  1. Get my sample plugin fsplugin (file system plugins section) even if you write a different type of plugin!
  2. Add the files cunicode.h and cunicode.cpp to your project. They contain various functions to make Unicode support easier.
  3. Convert your existing functions to Unicode and rename them to FunctionNameW. For all file functions like CreateFile, do not call their Unicode counterpart CreateFileW directly. Instead, call the functions from cunicode.cpp like CreateFileT. These functions automatically call the right Unicode or Ansi function, and even support file name lengths >259 characters!
  4. For each converted function like FunctionNameW, recreate a function FunctionName which you call this way:
int __stdcall FunctionName(char* SomeString1, char* SomeString2)
{
	WCHAR SomeString1W[wdirtypemax], SomeString2W[wdirtypemax];
	return FunctionNameW(
		awfilenamecopy(SomeString1W, SomeString1),
		awfilenamecopy(SomeString2W, SomeString2));
}
The Macro awfilenamecopy will convert the Ansi string SomeString1 to Unicode and store it in SomeString1W. This variable must not be a pointer, because awfilenamecopy uses "countof" to get the target length.

64-bit support

With Total Commander 8, Total Commander is now also available in 64-bit. Since plugins are simple dlls, and 64-bit programs can only load 64-bit dlls, a plugin needs to be re-compiled with a 64-bit compiler to work with 64-bit Total Commander.

IMPORTANT: A 64-bit plugin must have the same name as the 32-bit plugin and be in the same directory, but '64' must be appended to the extension. Example: filesystem.wcx → filesystem.wcx64. 64-bit-only plugins must also end with '64'.

Since all 64-bit Windows versions support Unicode, it's sufficient to write a 64-bit Unicode-only plugin. However, if your existing 32-bit plugin only supports ANSI functions, you can port it without modifications to 64 bit. Total Commander 64-bit also supports the ANSI functions if it cannot find the Unicode functions. 64-bit Unicode plugins do not have an extension starting with 'u', they have the normal 'wcx64' extension.

Some porting notes:

All integer parameters in plugin functions remain 32-bit (e.g. in C: int, long, DWORD; Delphi: integer, dword), only pointers and handles are 64-bit wide now. Recompiling a program or dll with a 64-bit compiler usually takes care of this automatically without needing many changes. Problems can arise when converting pointers to integer or vice versa. Make sure to use 64-bit integer variables (e.g. size_t in C, ptrint or ptruint in Lazarus) for such operations.

What's the easiest way to convert an existing plugin to 64-bit?

1. If the plugin was written in C or C++:

If you have Visual Studio Professional 2005 or later, a 64-bit compiler is already included. If you use the free Express version or Visual Studio 2003, you need to install the Windows Software Development Kit (SDK) in addition to Visual Studio Express.

Here is how to add 64-bit support to an existing plugin:

  1. Check the toolbars in Visual Studio: There are comboboxes for the compile type (Debug or Release), and one which shows "Win32".
  2. Open the one showing "Win32", and click on the "Configuration manager".
  3. Click on "New" in the Active Solution Platform list (right side). A new dialog box "New Solution Platform" will be shown.
  4. In the upper field, choose "x64"
  5. In the lower field, "copy from", choose "Win32"
  6. Make sure the checkbox "Create new project platform" is on
  7. Click OK. See also: How to: Configure Visual C++ Projects to Target 64-Bit Platforms
  8. Now you can switch in the same project between Win32 and x64. Switch to x64.
  9. Open the settings of your project.
  10. You should set the following options:
    • C++ - Code generation - runtime library: Multi-threaded-Debug (/Mtd) ← not multithreaded debug dll !
    • Linker - General - output file: wcx/pluginname.wcx64

Download links:

  1. Visual Studio Express C++ edition
  2. Windows Software Development Kit (SDK)

2. If the plugin was written in Delphi or Free Pascal:

There is a 64-bit Lazarus/Free Pascal available, which can be used to create 64-bit dlls. Total Commander itself was compiled with Lazarus as a 64-bit application. There are menu items in the "Tools" menu to convert Delphi projects and forms to Lazarus.

Lazarus/Free Pascal works a bit differently from Delphi, so some functions may need to be changed. Here are the problems encountered when porting Total Commander:

  1. Free pascal is different → Use {$MODE Delphi} in all *.pas files to handle functions the Delphi way
  2. strnew creates a NIL pointer when the passed pchar is 0 bytes long. → Use your own strnew function.
  3. Windows messages below WM_USER are not passed to the windows procedure. → Use SetWindowLongPtr to subclass the window
  4. The calculation p-buffer is not working when p is a pwidechar, buffer an array of widechar → use p-pwidechar(@bufffer)
  5. INVALID_HANDLE_VALUE is incorrectly set to 0 instead of -1 in lcltype.pp! → Put "windows" at end of "uses" command.

Download links:

You should download and install the 64-bit daily snapshot from lazarus.freepascal.org. Click on "Daily snapshots", then on e.g. Lazarus + fpc 2.4.4 win64. The final releases are very outdated, so the snapshots usually work much better.

Header for C(++)

/* Contents of file wcxhead.h */
/* It contains definitions of error codes, flags and callbacks */

/* Error codes returned to calling application */
#define E_END_ARCHIVE     10            /* No more files in archive */
#define E_NO_MEMORY       11            /* Not enough memory */
#define E_BAD_DATA        12            /* CRC error in the data of the currently unpacked file */
#define E_BAD_ARCHIVE     13            /* The archive as a whole is bad, e.g. damaged headers */
#define E_UNKNOWN_FORMAT  14            /* Archive format unknown */
#define E_EOPEN           15            /* Cannot open existing file */
#define E_ECREATE         16            /* Cannot create file */
#define E_ECLOSE          17            /* Error closing file */
#define E_EREAD           18            /* Error reading from file */
#define E_EWRITE          19            /* Error writing to file */
#define E_SMALL_BUF       20            /* Buffer too small */
#define E_EABORTED        21            /* Function aborted by user */
#define E_NO_FILES        22            /* No files found */
#define E_TOO_MANY_FILES  23            /* Too many files to pack */
#define E_NOT_SUPPORTED   24            /* Function not supported */

/* flags for unpacking */
#define PK_OM_LIST          0
#define PK_OM_EXTRACT       1

/* flags for ProcessFile */
#define PK_SKIP             0            /* Skip this file */
#define PK_TEST             1            /* Test file integrity */
#define PK_EXTRACT          2            /* Extract to disk */

/* Flags passed through ChangeVolProc */
#define PK_VOL_ASK          0            /* Ask user for location of next volume */
#define PK_VOL_NOTIFY       1            /* Notify app that next volume will be unpacked */

/* Flags for packing */

/* For PackFiles */
#define PK_PACK_MOVE_FILES  1    /* Delete original after packing        */
#define PK_PACK_SAVE_PATHS  2    /* Save path names of files             */
#define PK_PACK_ENCRYPT     4    /* Ask user for password, then encrypt  */

/* Returned by GetPackCaps */
#define PK_CAPS_NEW         1    /* Can create new archives              */
#define PK_CAPS_MODIFY      2    /* Can modify exisiting archives        */
#define PK_CAPS_MULTIPLE    4    /* Archive can contain multiple files   */
#define PK_CAPS_DELETE      8    /* Can delete files                     */
#define PK_CAPS_OPTIONS    16    /* Has options dialog                   */
#define PK_CAPS_MEMPACK    32    /* Supports packing in memory           */
#define PK_CAPS_BY_CONTENT 64    /* Detect archive type by content       */
#define PK_CAPS_SEARCHTEXT 128   /* Allow searching for text in archives */
                                 /* created with this plugin}            */
#define PK_CAPS_HIDE       256   /* Show as normal files (hide packer    */
                                 /* icon), open with Ctrl+PgDn, not Enter*/
#define PK_CAPS_ENCRYPT    512   /* Plugin supports PK_PACK_ENCRYPT option*/

#define BACKGROUND_UNPACK   1    /* Which operations are thread-safe?    */
#define BACKGROUND_PACK     2
#define BACKGROUND_MEMPACK  4

/* Flags for packing in memory */
#define MEM_OPTIONS_WANTHEADERS 1  /* Return archive headers with packed data */

/* Errors returned by PackToMem */
#define MEMPACK_OK          0    /* Function call finished OK, but there is more data */
#define MEMPACK_DONE        1    /* Function call finished OK, there is no more data  */

#define PK_CRYPT_SAVE_PASSWORD 1
#define PK_CRYPT_LOAD_PASSWORD 2
// Load password only if master password has already been entered!
#define PK_CRYPT_LOAD_PASSWORD_NO_UI 3
// Copy encrypted password to new archive name
#define PK_CRYPT_COPY_PASSWORD 4
// Move password when renaming an archive
#define PK_CRYPT_MOVE_PASSWORD 5
// Delete password
#define PK_CRYPT_DELETE_PASSWORD 6

// The user already has a master password defined
#define PK_CRYPTOPT_MASTERPASS_SET 1

typedef struct {
	char ArcName[260];
	char FileName[260];
	int Flags;
	int PackSize;
	int UnpSize;
	int HostOS;
	int FileCRC;
	int FileTime;
	int UnpVer;
	int Method;
	int FileAttr;
	char* CmtBuf;
	int CmtBufSize;
	int CmtSize;
	int CmtState;
} tHeaderData;

typedef struct {
	char ArcName[1024];
	char FileName[1024];
	int Flags;
	unsigned int PackSize;
	unsigned int PackSizeHigh;
	unsigned int UnpSize;
	unsigned int UnpSizeHigh;
	int HostOS;
	int FileCRC;
	int FileTime;
	int UnpVer;
	int Method;
	int FileAttr;
	char* CmtBuf;
	int CmtBufSize;
	int CmtSize;
	int CmtState;
	char Reserved[1024];
} tHeaderDataEx;


typedef struct {
	WCHAR ArcName[1024];
	WCHAR FileName[1024];
	int Flags;
	unsigned int PackSize;
	unsigned int PackSizeHigh;
	unsigned int UnpSize;
	unsigned int UnpSizeHigh;
	int HostOS;
	int FileCRC;
	int FileTime;
	int UnpVer;
	int Method;
	int FileAttr;
	char* CmtBuf;
	int CmtBufSize;
	int CmtSize;
	int CmtState;
	char Reserved[1024];
} tHeaderDataExW;

typedef struct {
	char* ArcName;
	int OpenMode;
	int OpenResult;
	char* CmtBuf;
	int CmtBufSize;
	int CmtSize;
	int CmtState;
} tOpenArchiveData;

typedef struct {
	WCHAR* ArcName;
	int OpenMode;
	int OpenResult;
	WCHAR* CmtBuf;
	int CmtBufSize;
	int CmtSize;
	int CmtState;
} tOpenArchiveDataW;

typedef struct {
	int size;
	DWORD PluginInterfaceVersionLow;
	DWORD PluginInterfaceVersionHi;
	char DefaultIniName[MAX_PATH];
} PackDefaultParamStruct;

/* Definition of callback functions called by the DLL
Ask to swap disk for multi-volume archive */
typedef int (__stdcall *tChangeVolProc)(char *ArcName,int Mode);
typedef int (__stdcall *tChangeVolProcW)(WCHAR *ArcName,int Mode);
/* Notify that data is processed - used for progress dialog */
typedef int (__stdcall *tProcessDataProc)(char *FileName,int Size);
typedef int (__stdcall *tProcessDataProcW)(WCHAR *FileName,int Size);
typedef int (__stdcall *tPkCryptProc)(int CryptoNr,int Mode,
             char* ArchiveName,char* Password,int maxlen);
typedef int (__stdcall *tPkCryptProcW)(int CryptoNr,int Mode,
             WCHAR* ArchiveName,WCHAR* Password,int maxlen);

Header file for Delphi

{ Contents of file wcxhead.pas }
{ It contains definitions of error codes, flags and callbacks }

unit wcxhead;

interface

const       {Error codes returned to calling application}
  E_END_ARCHIVE=     10;       {No more files in archive}
  E_NO_MEMORY=       11;       {Not enough memory}
  E_BAD_DATA=        12;       {CRC error in the data of the currently unpacked file}
  E_BAD_ARCHIVE=     13;       {The archive as a whole is bad, e.g. damaged headers}
  E_UNKNOWN_FORMAT=  14;       {Archive format unknown}
  E_EOPEN=           15;       {Cannot open existing file}
  E_ECREATE=         16;       {Cannot create file}
  E_ECLOSE=          17;       {Error closing file}
  E_EREAD=           18;       {Error reading from file}
  E_EWRITE=          19;       {Error writing to file}
  E_SMALL_BUF=       20;       {Buffer too small}
  E_EABORTED=        21;       {Function aborted by user}
  E_NO_FILES=        22;       {No files found}
  E_TOO_MANY_FILES=  23;       {Too many files to pack}
  E_NOT_SUPPORTED=   24;       {Function not supported}

  {Unpacking flags}
  PK_OM_LIST=           0;
  PK_OM_EXTRACT=        1;

  {Flags for ProcessFile}
  PK_SKIP=              0;     {Skip file (no unpacking)}
  PK_TEST=              1;     {Test file integrity}
  PK_EXTRACT=           2;     {Extract file to disk}

  {Flags passed through ChangeVolProc}
  PK_VOL_ASK=           0;     {Ask user for location of next volume}
  PK_VOL_NOTIFY=        1;     {Notify app that next volume will be unpacked}

  {Packing flags}

  {For PackFiles}
  PK_PACK_MOVE_FILES=   1;    {Delete original after packing}
  PK_PACK_SAVE_PATHS=   2;    {Save path names of files}
  PK_PACK_ENCRYPT=      4;    {Ask user for password, then encrypt}


  {Returned by GetPackCaps}
  PK_CAPS_NEW=          1;    {Can create new archives}
  PK_CAPS_MODIFY=       2;    {Can modify exisiting archives}
  PK_CAPS_MULTIPLE=     4;    {Archive can contain multiple files}
  PK_CAPS_DELETE=       8;    {Can delete files}
  PK_CAPS_OPTIONS=     16;    {Supports the options dialogbox}
  PK_CAPS_MEMPACK=     32;    {Supports packing in memory}
  PK_CAPS_BY_CONTENT=  64;    {Detect archive type by content}
  PK_CAPS_SEARCHTEXT= 128;    {Allow searching for text in archives
                              { created with this plugin}
  PK_CAPS_HIDE=       256;    { Show as normal files (hide packer icon) }
                              { open with Ctrl+PgDn, not Enter }
  PK_CAPS_ENCRYPT=    512;    { Plugin supports PK_PACK_ENCRYPT option }

  BACKGROUND_UNPACK=1;        { Which operations are thread-safe? }
  BACKGROUND_PACK=2;
  BACKGROUND_MEMPACK=4;       { For tar.pluginext in background }

  {Flags for packing in memory}
  MEM_OPTIONS_WANTHEADERS=1;  {Return archive headers with packed data}

  {Errors returned by PackToMem}
  MEMPACK_OK=           0;    {Function call finished OK, but there is more data}
  MEMPACK_DONE=         1;    {Function call finished OK, there is no more data}

  {Flags for PkCryptProc callback}
  PK_CRYPT_SAVE_PASSWORD=1;
  PK_CRYPT_LOAD_PASSWORD=2;
  PK_CRYPT_LOAD_PASSWORD_NO_UI=3;   { Load password only if master password has already been entered!}
  PK_CRYPT_COPY_PASSWORD=4;         { Copy encrypted password to new archive name}
  PK_CRYPT_MOVE_PASSWORD=5;         { Move password when renaming an archive}
  PK_CRYPT_DELETE_PASSWORD=6;       { Delete password}


#define PK_CRYPTOPT_MASTERPASS_SET 1   // The user already has a master password defined


type
  {Definition of callback functions called by the DLL}
  {Ask to swap disk for multi-volume archive}
  PChangeVolProc=^TChangeVolProc;
  TChangeVolProc=function(ArcName:pchar;Mode:longint):longint; stdcall;
  PChangeVolProcW=^TChangeVolProcW;
  TChangeVolProcW=function(ArcName:pwidechar;Mode:longint):longint; stdcall;
  {Notify that data is processed - used for progress dialog}
  PProcessDataProc=^TProcessDataProc;
  TProcessDataProc=function(FileName:pchar;Size:longint):longint; stdcall;
  PProcessDataProcW=^TProcessDataProcW;
  TProcessDataProcW=function(FileName:pwidechar;Size:longint):longint; stdcall;
  PPkPluginCryptProc=^TPkPluginCryptProc;
  TPkPluginCryptProc=function(CryptoNr:integer;mode:integer;ArchiveName,
    Password:pchar;maxlen:integer):integer; stdcall;
  PPkPluginCryptProcW=^TPkPluginCryptProcW;
  TPkPluginCryptProcW=function(CryptoNr:integer;mode:integer;ArchiveName,
    Password:pwidechar;maxlen:integer):integer; stdcall;


type
  THeaderData=packed record
    ArcName:array [0..259] of char;
    FileName:array [0..259] of char;
    Flags,
    PackSize,
    UnpSize,
    HostOS,
    FileCRC,
    FileTime,
    UnpVer,
    Method,
    FileAttr:longint;
    CmtBuf:pchar;
    CmtBufSize,
    CmtSize,
    CmtState:longint;
  end;

  THeaderDataEx=packed record
    ArcName:array [0..1023] of char;
    FileName:array [0..1023] of char;
    Flags:longint;
    PackSize,
    PackSizeHigh,
    UnpSize,
    UnpSizeHigh:DWORD;
    HostOS,
    FileCRC,
    FileTime,
    UnpVer,
    Method,
    FileAttr:longint;
    CmtBuf:pchar;
    CmtBufSize,
    CmtSize,
    CmtState:longint;
    Reserved:array[0..1023] of char;
  end;

  THeaderDataExW=packed record
    ArcName:array [0..1023] of widechar;
    FileName:array [0..1023] of widechar;
    Flags:longint;
    PackSize,
    PackSizeHigh,
    UnpSize,
    UnpSizeHigh:DWORD;
    HostOS,
    FileCRC,
    FileTime,
    UnpVer,
    Method,
    FileAttr:longint;
    CmtBuf:pchar;
    CmtBufSize,
    CmtSize,
    CmtState:longint;
    Reserved:array[0..1023] of char;
  end;

  tOpenArchiveData=packed record
    ArcName:pchar;
    OpenMode,
    OpenResult:longint;
    CmtBuf:pchar;
    CmtBufSize,
    CmtSize,
    CmtState:longint;
  end;

  tOpenArchiveDataW=packed record
    ArcName:pwidechar;
    OpenMode,
    OpenResult:longint;
    CmtBuf:pwidechar;
    CmtBufSize,
    CmtSize,
    CmtState:longint;
  end;

  tPackDefaultParamStruct=record
    size,
    PluginInterfaceVersionLow,
    PluginInterfaceVersionHi:longint;
    DefaultIniName:array[0..259] of char;
  end;
  pPackDefaultParamStruct=^tPackDefaultParamStruct;

implementation

end.