Plugin Development - KeePass

KeePass

Settings

Plugin Development

How to develop plugins for KeePass 1.x.

This documentation applies to KeePass 1.x plugins (all versions ≥ 1.15). 1.x plugins are fundamentally different from 2.x plugins. 2.x plugins cannot be loaded by KeePass 1.x.

A detailed SDK documentation is available here: Plugin SDK Documentation.


Info  Requirements

Before you can start developing a KeePass plugin, you need the following prerequisites:

  • Latest KeePass source code package. You can get it from the KeePass website.
  • A C++ development IDE / compiler.
  • Windows Platform SDK.

The KeePass plugin API uses some concepts of the Component Object Model (COM) standard. If you don't have experience with COM, the following pages are recommended for reading:


Info  Step-by-Step Tutorial

Start your favorite IDE and create a new Win32 Project (application type DLL, empty project). In this tutorial the example plugin we're developing is called TestPlugin. Create two files in the new project: a C++ source file (TestPlugin.cpp) and a header file (TestPlugin.h).

In order to access the KeePass interfaces, you have to include a header file from the KeePass SDK: put an #include statement in the TestPlugin.h file, which includes the file KeePassLibCpp/SDK/KpSDK.h from the KeePass source code.

Windows DLLs may optionally implement a DllMain function. So if you want one (not required by KeePass though), implement a default one now in the TestPlugin.cpp file (just always return TRUE):

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	UNREFERENCED_PARAMETER(hinstDLL);
	UNREFERENCED_PARAMETER(fdwReason);
	UNREFERENCED_PARAMETER(lpvReserved);

	return TRUE;
}

The next step is to create a plugin class, which must implement the IKpPlugin interface. So, lookup the IKpPlugin interface (abstract C++ class) and design a class that implements all these methods. Details about the methods can be found in the Plugin SDK Documentation.

Now export a function, which KeePass will use to create an instance of your plugin class:
KP_EXPORT HRESULT KP_API KP_I_CREATEINSTANCE_DECL(REFIID riid, void** ppvObject, IKpUnknown* pAPI);
In this function, you'll need to create an instance of your plugin class and store an interface pointer of the type requested by KeePass (riid) in the ppvObject parameter. The pAPI parameter is an interface pointer to the KeePass API, which you should store for later use in case you're able to return a valid plugin interface.

KeePass is currently offered only as ANSI application, not Unicode. Therefore, go to Project -> Test Plugin Properties and choose Multi Byte as character set.

It is recommended (but not required) to statically link with the runtime library (and MFC, if you're using it). To do this, go to Project -> Test Plugin Properties -> C/C++ -> Code Generation and choose a runtime library not ending with '-DLL'.

The last step before building your plugin is to add a version information resource. So, go to the 'Resources' tab of the plugin project and add a resource of type 'Version'. Here, set the product name to KeePass Plugin. All other fields can be freely set to strings of your choice.

Example. You can find a fully documented and extended version of this simple plugin on the KeePass plugins web page ("Test Plugin").


Info  Plugin Conventions and Recommendations

Conventions:

  • The DLL file must have a version information resource, in which the product name is set to KeePass Plugin. KeePass uses this to determine whether the DLL is a KeePass plugin or not (i.e. if you don't create a version information resource with this string, KeePass will not load your DLL file).

  • A KeePass plugin must export the following function:
    KP_EXPORT HRESULT KP_API KP_I_CREATEINSTANCE_DECL(REFIID riid, void** ppvObject, IKpUnknown* pAPI);

    KeePass will call this function to create an instance of your plugin class. You have to return an interface of type riid in the ppvObject parameter, if your plugin class supports this interface (return S_OK). Otherwise set ppvObject to NULL and return E_NOINTERFACE. You may store the pAPI interface pointer for later use. KeePass guarantees that the pointer is valid as long as it has a pointer to your plugin class instance.

    Important: explicitly check for which interface KeePass is asking (riid), otherwise your plugin is not upward-compatible and might crash in future KeePass versions.

  • A KeePass plugin may optionally export the following functions:
    KP_EXPORT HRESULT KP_API KP_I_INITIALIZELIB_DECL(IKpUnknown* pAPI);
    KP_EXPORT HRESULT KP_API KP_I_RELEASELIB_DECL(IKpUnknown* pAPI);


    KeePass will call the first function after the DLL is loaded, and the second one shortly before the DLL is unloaded.

    You should not store the pAPI interface pointer for later use. Consider the pointers as temporary; they might become invalid as soon as you return from the KP_I_INITIALIZELIB_DECL or KP_I_RELEASELIB_DECL function. The pAPI interface pointer values passed to KP_I_INITIALIZELIB_DECL and KP_I_RELEASELIB_DECL are not guaranteed to be the same as each other, or as the pointer value passed to KP_I_CREATEINSTANCE_DECL.

  • The protocol is DllMain (if present), KP_I_INITIALIZELIB_DECL (if present), KP_I_CREATEINSTANCE_DECL, plugin interface methods, KP_I_RELEASELIB_DECL (if present).

  • KeePass is using the multi-byte character set. Therefore, make sure that you're also compiling your plugin in multi-byte character set mode, not Unicode.

Recommendations:

  • All plugin files should begin with a common prefix. For example, if your plugin is called VariousImport, the DLL file might be named VariousImport.dll and its help file VariousImport.html. If you don't use a common prefix, users might run into overwriting problems when installing multiple plugins, because all plugins must be copied into the KeePass application directory. For example, if there's a plugin that ships with a ReadMe.txt file and another plugin that also ships with such a file, the latter overwrites the readme file of the first, or the user chooses not to overwrite and the readme file of the second plugin is not available. Using a common prefix avoids this problem.

  • The version information block should at least be available in English (USA) language.

  • There are two implementations of the IKpConfig interface. One implementation is identified by CLSID_KpConfig, the other by CLSID_KpConfig_ReadOnly. The first one supports both reading and writing, the second one only reading. It is highly recommended that you use the second implementation, if you only want to read configuration items.

    Trying to write using the CLSID_KpConfig_ReadOnly implementation will assert if KeePass is compiled in Debug mode, and will fail in Release mode (and might possibly destroy parts of the current configuration).

Info  Upgrading Plugins from ≤ 1.14 to ≥ 1.15

When upgrading a plugin from KeePass ≤ 1.14 to ≥ 1.15, it is highly recommended that you create a new project file, start from scratch and copy / fill out the interface methods with the old code.

Notes:

  • There is no KeePass.lib file anymore. The new plugin architecture is based on interfaces. Including the KpSDK.h header file is all you need to do.

    Do not compile with any of the files in the KeePass source code or include any other header file than KpSDK.h.

  • Previously a command line prefix was registered by setting the cmdLineArgPrefix member of the plugin information structure. In the new architecture, the command line prefix must be returned by the GetProperty member method of the plugin interface when being called with the KPPS_COMMANDLINEARGPREFIX parameter.

    Your GetProperty might look like this:
    STDMETHODIMP_(LPCTSTR) CYourPluginImpl::GetProperty(LPCTSTR lpName)
    {
    	if(lpName == NULL) return NULL;
    
    	if(_tcscmp(lpName, KPPS_COMMANDLINEARGPREFIX) == 0)
    		return _T("mypluginprefix.");
    
    	return NULL;
    }

    The plugin should not access the KeePass command line until after all plugins have been loaded. This is because at that time KeePass of course can't call the GetProperty method of your plugin yet and consequently doesn't know the prefix yet (and this will lead to 'unknown command line option' warnings). Instead, perform command line dependent initialization when KeePass calls your OnMessage handler method with the KPM_DELAYED_INIT code.


Info  Detailed Documentation of All Interfaces

See the Plugin SDK Documentation.


Info  Plugin Framework for C++

Thanks to Bill Rubin, there's an optional Plugin Framework available, facilitating the development of KeePass plugins in C++. The features in detail:

  1. Complete implementation of the IID_IKpUnknown interface.
  2. Implementation of the IID_IKpPlugin interface, except for the OnMessage member function, which is always application-specific.
  3. Ability to get smart pointers to any other KeePass interface, including IKpAPI, IKPCommandLine, IKpCommandLineOption, IKpConfig, IKpDatabase, IKpFullPathName, and IKpUtilities.
  4. Implementation of the KpCreateInstance function, which the plugin must export from its DLL.
  5. Comprehensive error checking of COM handshaking for Items 3 and 4. If an error is found, PFK displays a message box containing all information about the error. Without this feature, a plugin will, in most cases, silently fail to load. In other cases, it will fail to perform its function.
  6. Convenience utilities to display a message box, translate a Windows error code into a natural language string, and declare a standard string independently of character type.
  7. Compile-time checking of constructor invocations for smart COM pointers. Without this feature, it is difficult for a developer to interpret compiler error messages caused by using the wrong smart COM pointer constructor.
  8. The PFK code avoids defining macros. Instead templates, inline functions, typedefs, and other C++ constructs maintain safe design practices, with no runtime penalty.

There's also a Test Plugin available using the Plugin Framework.