Musings from Bruderstein

Alien in Berlin.

Top 10 Hints for Writing a Notepad++ Plugin, and Getting It Listed

This is a list of the things that many people forget or don’t do properly when writing a Notepad++ plugin. None of them stop things working, but they all make either the user experience a bit less friendly, or cause stability problems, or make hosting the plugin in the Plugin Manager just that little bit more tricky.

1. Version number.

I know, this is a stupid thing, but you’d be amazed how many plugins don’t advertise their version number, or advertise it wrong. At the time of writing, 54 from 115 listed, do or have had a plugin DLL that either doesn’t report a version number or reports the wrong number. This means plugin manager needs to keep an MD5 sum of the DLL to identify the version, and the user can’t see the version from explorer, which makes manual upgrades that bit trickier.

Plugins can be written in any number of languages, but for C/C++ it’s very easy to add a version number. Sadly, this isn’t quite so easy in the Express versions of Visual Studio, however, you can add a resource text file (a .rc file), and copy the code in from below, changing the obvious details.

Project Resource File myproject.rc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
VS_VERSION_INFO VERSIONINFO
 FILEVERSION 1,2,34,5
 PRODUCTVERSION 1,2,34,5
 FILEFLAGSMASK 0x37L
#ifdef _DEBUG
 FILEFLAGS 0x21L
#else
 FILEFLAGS 0x20L
#endif
 FILEOS 0x4L
 FILETYPE 0x0L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "080904b0"
        BEGIN
            VALUE "Comments", "Source code at git://github.com/davegb3/nppPluginManager.git"
            VALUE "CompanyName", "Dave Brotherstone.  davegb@pobox.com"
            VALUE "FileDescription", "My Great Plugin for Notepad++.  A free (GPL) Plugin for doing great things"
            VALUE "FileVersion", "1, 2, 34, 5"
            VALUE "InternalName", "MyGreatPlugin"
            VALUE "LegalCopyright", "Copyright (C) 2009-11 Dave Brotherstone"
            VALUE "OriginalFilename", "MyGreatPlugin.dll"
            VALUE "ProductName", "My Great Plugin"
            VALUE "ProductVersion", "1, 2, 34, 5"
#ifdef UNICODE
            VALUE "SpecialBuild", "UNICODE"
#else
          VALUE "SpecialBuild", "ANSI"
#endif
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x809, 1200
    END
END

The key piece of information is on the second line - the FILEVERSION. This is what plugin manager uses to assign the version number. The other version numbers (PRODUCTVERSION and the "ProductVersion" and "FileVersion" numbers in the BLOCK) don’t have to be the same, but it usually makes sense to have them the same number.

Adding the (same!) version number to the about box is also a nice way for your users to know they’ve got the new version. Take a look at the Plugin Manager Source, specifically version.rc and PluginManagerVersion.h (under pluginManager/src) to see how to put the version number in basically one place and have it always consistent.

2. Use the config directory.

Depending on the installation, where the plugins should save their configuration can change. Use the NPPM_GETPLUGINSCONFIGDIR message to get the currently configured plugin directory. Attempting to write to the Program Files\Notepad++\plugins\config directory rarely results in a happy afternoon.

It’s also worth considering what happens when you upgrade your plugin. Adding a config version number into your config can help you port the config later, if you need to, depending on how complex your config is. If you need to install a config file at install time, it can be worth it to deploy a standard /default/ config with your plugin, and have your plugin copy that to the plugin config directory if it doesn’t find a config file. This means that future versions don’t overwrite the users configuration, but you’ve got a standard one for if you do. Remember your plugin might start with a new version and an old config file, which could be where the version number in the config file helps.

3. DLL_PROCESS_ATTACH and NPPN_SHUTDOWN.

Sadly, there was an error in an old C/C++ template, that meant that some people built a potentially buggy plugin before they’d even started.

So, here’s the thing: DllMain gets called for every process/thread that loads it. For every DLL_PROCESS_ATTACH call there’s a DLL_PROCESS_DETACH. The same with DLL_THREAD_ATTACH and DLL_THREAD_DETACH. Plugin Manager will effectively call these when it performs the LoadLibrary() to call getPluginName().

setInfo() gets called only when Notepad++ successfully loads the DLL (with LoadLibrary()). What this means, is that any allocation performed in DLL_PROCESS_ATTACH must be deallocated in DLL_PROCESS_DETACH. And the same with the THREAD equivalents. And, more importantly, anything that gets allocated in setInfo() or similar calls (getPluginName() etc), can only be deallocated from a Notepad++ call. Probably the best one to use for this is beNotified(), using notification NPPN_SHUTDOWN, which gets called (surprisingly) when Notepad++ shuts down.

As a hint, if your plugin doesn’t work when Plugin Manager has been opened, then it’s likely you’ve got an issue with where things are allocated and deallocated.

Allocate in Deallocate in
DllMain: DLL_PROCESS_ATTACHDllMain: DLL_PROCESS_DETACH
DllMain: DLL_THREAD_ATTACHDllMain: DLL_THREAD_DETACH
setInfo() - ideally don’t use this for allocation, instead consider beNotified() with message NPPN_READYbeNotified() message NPPN_SHUTDOWN
getName()Don’t. getName() gets called by Plugin Manager and shouldn’t do any allocation
beNotified() message NPPN_READYbeNotified() message NPPN_SHUTDOWN

4. /MT everything.

For C/C++ projects, this says “statically link the C++ runtime code with the DLL”. This has two effects, one is that it removes the requirement for the Visual C++ Runtime library to be installed, which is just kinda nice for the users. If you use /MD and they don’t have it installed, they’ll get a message that LoadLibrary() failed, and that the plugin is not compatible. The second effect is that it makes everything work. Ok, it’s not actually that dramatic, but Notepad++ itself is compiled with this switch mixing /MT with /MD is a bit like mixing potassium and water - you might get some interesting effects.

Also, make sure you do a release build, not just a debug version.

5. Zipped download.

Just a little point, but if your plugin consists of a single DLL, still go to the trouble of making a ZIP file available. If you’ve nowhere to host it, and you don’t want to setup a sourceforge or other OSS hosting service project for it, send your stuff to me and I’ll add it somewhere sensible. Although Plugin Manager since 1.0.0 can download any file for copying, it’s happiest with ZIP files. 7zip makes good, small ZIP files.

6. Release the code.

My first Notepad++ plugin was my first outing to opensourceville, and I can highly recommend releasing the source to your plugin. Even if you believe the code to be awful, and you don’t want anyone to see it, I know I still feel like that about anything I release. Opening the code means people can take a glance over it, and maybe suggest improvements or even bug fixes. I learnt a mountain of stuff from very helpful developers in the Notepad++ project. Also, IANAL, but if your plugin is C/C++, and you use the headers and various support C++ files from the Notepad++ project, it’s against the terms of the Notepad++ license not to.

Keep your source code in a separate download. Less than 1% of people want the source. They’re your most valuable 1%, they’ll help you, support you, provide suggestions and fixes, even new features, so look after them, but don’t ignore the other 99% that just want the plugin. By look after them, I mean respect the feedback they give you, even if you’re more experienced than them, and make sure your source download is a reasonable size. See the Plugin Manager ignore list for a reasonable list of files not to include in your zip. Obviously providing a link to a github, bitbucket or other source hosting provider is either just as good, or even better, depending on how you see the world.

7. Don’t worry about Unicode and ANSI releases.

Windows 95 just will not die. We’ve tried everything, drowning it, soaking it in non-pH balanced oils next to industrial magnets, flame throwers, everything, but it’s still around. However, ANSI users account for, as far as we can tell, less than 1% of the total users of Notepad++. We know from the forums that a few still use it due to a plugin or two that only works for the ANSI version, and some due to some sadistic need to still use a nearly-twenty-year-old operating system. Notepad++ itself has discontinued the ANSI build, so don’t feel compelled to make one too.

8. Release announcement.

Announce your plugin on the Plugin Development forum. Do this for each and every new version. Ideally, create a new post for each new version, rather than adding your announcement to an old thread. This keeps the forum clean, and means people get to see easier that a new version is out.

9. List it.

If you’ve not got an account at the Plugin Manager Admin web site, then just click register on the site to get one. It may take a day or so to be approved, but once you get the email that it’s been approved, you can log in and add (or update) your plugin. I’ll try and do a post about adding your plugin and what steps to take really soon. Like really really soon. Promise.

And if you’ve not done step 8, do that first. No, really, do that first.

10. Enjoy it.

This should be number 1. Sounds a bit corny, but it’s true. Notepad++ has millions of users, and if even a tiny percentage of those download your plugin, you can still be looking at tens of thousands of downloads. One developer actually complained that something must be wrong, as his equivalent plugin for Visual Studio had received no where near the same number of downloads. Personally, I find it a really nice feeling that lots of people are using my code.

Comments