Previous Page  Developer's Guide: SDIPlugin API Specs  5.3 Next Page

Stardock DesktopX Plugin interface (SDPlugin)


SDPlugins are designed to allow you to extend the functionality of applications that support them such as DesktopX and ObjectBar. Examples of such Plugins would be virtual desktops, MP3 players, analog clocks, email checkers, Recycle bin checkers, printer status monitors, games, disk space monitors, CPU graphs, and anything else that needs to make calls to the system or the Internet and talk back to the object it is attached to or other objects running on the system. An SDPlugin will always attach itself to the object (or bar) that it is part of. And when exported for use by others, it will be included. This is done to decrease support.

This guide is designed to document how SDPlugins are created. It is designed for software developers who are already familiar with a programming language and how to create DLLs.

General information

Plugins must be compiled with stdcall calling convention.

If you are compiling with MS Visual C++ you should also use “Multithread DLL” run-time library for Release version and “Debug Multithread DLL” run-time library for the Debug version, since the required modules are distributed with DX anyway and this way the plug-in will be a lot smaller.

The constants mentioned in this document are declared in the SDPlugin.h (click to download) header files.

Plugin Lifetime

This chapter discusses the differences between the plugin lifetime and the instance (of the plugin) lifetime.

The plugin dll is loaded when an object needs the plugin. The SD_INITIALIZE_MODULE is sent through the SDMessage callback (see below).

The plugin dll is unloaded when the last object using the plugin is deleted or the plugin removed, so no object needs the plugin. SD_INITIALIZE_MODULE is sent through the SDMessage.

When a plugin is selected to be used in an object an instance of the plugin is created and associated to the object. More instances can be added to an object (i.e. an EmailCheck plugin can be added more times to handle more email accounts).

When an instance of the plugin is created the plugin receive SD_CREATE_PLUGIN. Note that at this point the plugin should not start its action (i.e. a clock plugin should not start displaying the hour), i.e. the DX object is not yet created. This message is sent for the plugin to initialize instance data, allocate variables and such.

The plugin instance actually starts working when it received the SD_INITIALIZE_PLUGIN message. In fact now it receives the object HWND and the rect size of the object. It means that the object has been created and the plugin should start “running”.

After the instance is created (SD_CREATE_PLUGIN ) but before it is running (SD_INITIALIZE_PLUGIN), the plugin can be configured. This can happen in three ways:

  • Directly by the user: the user just clicked “Configure” in the plugin section of the object properties. At this point SD_CONFIGURED is sent to the plugin and it would tipically open a config dialog to configure the instance settings.
  • When an object is loaded from the disk (i.e. the user loads a theme or package with objects using your plugin). Now the instance config data is typically loaded config data files that the plugin previously saved in the theme directory and the host saved them in themes or packages. The plugin knows that it should retrieve the saved instance config data when it receives the SD_LOAD_DATA message.
  • By the host through SD_DUPLICATE_PLUGIN message. This is probably the only hard part of the whole specs. Basically SD_DUPLICATE_PLUGIN is used overall by DesktopX to support special operations, when an object is duplicated and when an object is configured. On cloning an object, a new object is created and so, new plugin instances are created. These new instances need to be configured the same as the original object/instances. In fact SD_DUPLICATE_PLUGIN contains references of the original plugin instance to copy (in the sense of “configuring” it). Why is SD_DUPLICATE_PLUGIN needed on configuring an object?? When you open the object properties panel, you can still use all existing objects and so the object you are configuring. You can change its graphics, settings, add, remove, configure plugins and the configured object is still working the same way. All changes are applied when you actually click “Apply” or “Ok”. If you click “Cancel” the changes are ignored. To support this mechanism DesktopX performs these tasks:
    • When the user opens the object properties panel, a new “dummy” plugin instance is created. SD_DUPLICATE_PLUGIN is sent to configure this new instance as the original instance.
    • This “dummy” instance is used to be configured, but will never actually receive SD_INITIALIZE_PLUGIN.
    • When the user click Apply, the original object is destroyed with all his plugin instances. A new object is created with new settings (graphics, options, and new added plugins, removed plugins or differently configured plugin instances). New plugin instances are now created for the object. These are the final “real” instances and they are configured with SD_DUPLICATE_PLUGIN: the new plugin instance need to be configured the same as the “dummy” instances used in the properties panel.
    • The host destroys the “dummy” instances, while the new instances receive SD_INITIALIZE_PLUGIN and start working.

This could sounds difficult, but it is just to explain how the host works with plugins, you can simply ignore all of the above explanations and just follow and respect the plugin messages, just keep in mind that SD_CREATE_PLUGIN initializes the plugin config data (you would set this to default settings), SD_CONFIG configures this data through the user’s input, SD_DUPLICATE_PLUGIN configures a plugin instance the same way as another plugin instance, SD_LOAD_DATA configures the plugin instance from disk saved data. SD_INITIALIZE_PLUGIN finally “starts” the plugin (i.e. a timer starts running, the email check plugin starts checking for emails, etc.).

When the plugin instance is removed by the object, or the object is deleted, SD_TERMINATE_PLUGIN is sent for each instance.

Note that there are plugin’s own configuration data and config data needed by the host. I.e. if the plugin is a drawing plugin, that is, it draws the object graphics (i.e. analog clock plugin), the host needs to know it to correctly allocate the graphic buffers to be used by the plugin. So, the host needs to know if the plugin will sublcass the object. Plugins pass this information to the host in two cases:

  • When the plugin instance is created (SD_CREATE_PLUGIN). The plugin should typically set default flags and/or flags that won’t change.
  • When the plugin is configured (SD_CONFIG). The plugin can here change the current passed flags (saved in SD_CREATE_PLUGIN).

This way plugin options are “per instance” rather than “per plugin”. So, an instance could be set as drawing plugin, another instance of the same plugin could be not a drawing plugin (same for the subclass option and the other ones).

As mentioned plugins can store data in extern files (config files, graphics, etc). These files should be stored in the directory returned by SD_GET_THEME_DIRECTORY.


SDMessage is used by the host to send to the plugin callback messages.

BOOL SDMessage(DWORD objID, DWORD *pluginIndex, UINT messageID, DWORD param1, DWORD param2)

objID is used by the host to uniquely identify the objects. It must be passed to the host when using callbacks.

pluginIndex is an object identifier used by the plugin. It is modified by the plugin itself and is passed by the host on all messages. The plugin can use it to directly reference the plugin local data and settings.

param1 and param2 are message-specific parameters.

Returns TRUE if the message is supported and is processed.
Returns FALSE if the message is unsupported or not processed.

This is the list of the messages the host will send:


BOOL (__stdcall *SDHostMessage)(UINT, DWORD, DWORD) = BOOL (__stdcall *)(UINT, DWORD, DWORD) param1;

HINSTANCE dllInstance = (HINSTANCE) param2;

Called once when the plugin module is loaded by the host. (By name, should be called when a plugin is going to use this module and no other plugins already exist that use this module.)

The param1 contains a pointer to the SDHostMessage function the plugin will use to communicate with the host. See below for more information.


Called once when the plugin module is unloaded by the host. (When the last plugin that uses this module is destroyed, this should be called).


The host queries the plugin to obtain information about it. All this data is returned in the structure which is passed in the param1. This structure is allocated by the host.

DWORD host; // set by the host to identify whether the host is OB or DX:
DWORD host_version; // set by the host, specifies the version of the program
// 100 = 1.0, 101 = 1.01, 110 = 1.10
char plugin_name[256];
char plugin_author[256];
DWORD plugin_version; // 100 = 1.0, 101 = 1.01, 110 = 1.10
DWORD supported_platforms; // SD_OS_ALL or any combination of the following values:
// SD_OS_95, SD_OS_98, SD_OS_ME, SD_OS_NT4, SD_OS_2000, SD_OS_XP

Returns TRUE if the plugin is host compatible. Returns FALSE if the plugin isn't compatible with the host.

Note: this message could be simply used to query some info about the plugin, so the host can send it without then really initialize the plugin (SD_INITIALIZE_MODULE). If the plugin absolutely needs to initialize even if only to be queried, it should use DllMain and not SD_INITIALIZE_MODULE.


char *szCustomState = (char*) param1;
int iCustomStateIndex = (int) param2;

Copy in szCustomState the name of the required cutom state. Return FALSE to cancel or stop the enumeration. By returning FALSE, szCustomState will be ignored.


if(iCustomStateIndex == 0)
strcpy(szCustomState, “No Mail, Default”);
else if(iCustomStateIndex == 1)
strcpy(szCustomState, “No Mail, Mouseover”);
else if(iCustomStateIndex == 2)
strcpy(szCustomState, “You’ve Got Mail, Default”);
else if(iCustomStateIndex == 3)
strcpy(szCustomState, “You’ve Got Mail, Mouseover”);
return FALSE;
return TRUE;


This message is sent if the plugin is a drawing plugin. The plugin has been already associated to the object and it is already configured. The host must now know the desidered object size.

SIZE *sz = (SIZE *) param1;


pluginIndex should be initalized here, allocating memory and storing pointers to that memory in it as needed.

DWORD *plugin_flags = (DWORD*) Param1; // any combination of the flags described in “Plugin flags” below.


pluginIndex should be de-initalized here; any memory it associated with it should be freed, along with any other local data that was allocated.

Param1 & param2 both mean nothing.


DWORD objID_OriginalPlugin = (DWORD) param1;
DWORD *pluginIndex_OriginalPlugin = (DWORD*) param2;


CopyMemory((*pluginIndex), (*pluginIndex_OriginalPlugin), sizeof(MYSTRUCT)); //copies over the memory from the old plugin’s PIN data to the new plugin’s PIN data, so they’re identical
//Note further processing might be needed if more pointers/handles are located within the memory pointed to by pluginIndex!
//OR, if all that’s stored in pluginIndex is a number or something that has nothing to do with memory:
(*pluginIndex) = (*pluginIndex_OriginalPlugin);


HWND hwnd; // the object ‘s hwnd
RECT rcObject; // the portion of the window occupied by the object
SD_PLUGIN_INIT *init_data = (SD_PLUGIN_INIT *) param1;

The plugin should initialize here plugin local data, timers, etc.


This message should just be used to notify the plugin that it is being uninitalized. The plugin must here destroy timers, etc.


char *szInstanceID = (char *) param1;
DWORD *plugin_flags = (DWORD*) Param2;

This message is sent when the plugin should load the configuration data for the object. In szInstanceID the host writes the identifier of the plugin object instance that the plugin itself wrote in SD_SAVE_DATA. This is needed to identify plugin instances into different objects.


char *szInstanceID = (char *) param1;
BOOL bExport = (BOOL) param2;

This message is sent when the plugin should save the configuration data, i.e. before unloading or before exporting. The plugin will need to send SD_REGISTER_FILE messages to the host for each file that needs to be associated with the object.

The plugin should write in szInstanceID an instance identifier that the plugin will use on SD_LOAD_DATA time to identify the different instances if the plugin is working for more objects.

If the user is exporting, the plugin could choose to save config data differently, i.e. an email notify plugin won't save the email login/pass information, since the objects could be distributed around the web.


DWORD *plugin_flags = (DWORD*) param1; // as defined in SD_CREATE_PLUGIN, the plugin can here modify its flags depending on the configuration.
HWND hwndParent = (HWND) param2;

The plugin needs to popup a dialog to be configured.


char *szNewState = (char *) param1;

Here the developer can either return FALSE which would allow the host change the item/object’s state to the new state, or the developer can return TRUE and prevent the host from changing the item’s state.

If the developer desires to return TRUE to prevent the host from changing the item’s state, the plugin will probably want to set it manually to one its custom states or another standard state. To do this the plugin must copy the new state string identifier in szNewState. If an empty string is set, the plugin will simply cancel the state changing request, nor the original state nor another modified one is applied.

In SDPlugin.h are defined these standard state identifiers.

#define SD_OBJECT_STATE_MOUSEDOWN "Mouse down"
#define SD_OBJECT_STATE_MOUSEUP "Mouse up"
#define SD_OBJECT_STATE_MOUSEOVER "Mouse over"
#define SD_OBJECT_STATE_AWAY "Mouse away"
#define SD_OBJECT_STATE_COMMAND "Command executed"


HDC hdc;
HBITMAP hbitmap;
RECT rcObject; // bounding rectangle of the area occupied by the object in the HDC
// if the host is DesktopX, this rectangle will be always (0, 0, width, height)
char *szCurrentState; // current state identifier
int *iRender; // indicates the rendering method the host should use.
// SD_RENDER_OPAQUE: the host will perform a plain rectangular rendering
// SD_RENDER_TRANSPARENT: the host will render considering pink areas (255, 0, 255) as transparent
// SD_RENDER_PER_PIXEL_ALPHA: the host will render using the 4th channel for 256-levels transparency information.

SD_DRAW_INFO *drawInfo = (SD_DRAW_INFO *) param1;

The plugin must draw into the passed HDC. Note that the plugin should’t consider what is already in the HDC. It should draw a complete frame, eventually using pink area (255, 0, 255) when it needs transparency. After the plugin returns the host will actually draw in the background, but this is not plugin task.

If the host is DesktopX, the HBITMAP will always be a 32 bit memory DIB. In this case the plugin can directly modify the color arrays and if needed set *bPerPixelAlpha TRUE to enable the host to render in per-pixel alpha blending mode. The bitmap so drawn is then passed to the DesktopX renderer, that is, the plugin will never draw to the HDC directly.

If the plugin draws in opaque mode (not using per pixel information), it can use pink regions (RGB = 255, 0, 0) to indicate transparent regions.


This message is sent to plugins registered to perform a custom action with the SD_FLAG_CUSTOM_ACTION flag.


The plugin receives this message from the host if it has registered itself to hook window messages.

MSG *msg = (MSG *) param1;// note that in the current implementation the time and pt members are not used.

DWORD* returnValue = (DWORD*) param2;

The message is not yet processed by the host. The plugin can simply retrieve the info or modify the message parameters.

Returns TRUE if the message must be not processed by the host. The plugin must write in returnValue the desidered value to return to the window proc.
Returns FALSE if the message must be processed by the host.


The host sends this message to all plugins when the current mode is changed. Param1 is TRUE if the host is in User mode, FALSE if it is in Edit/Design mode.

Plugin flags

The flags are used to configure the plugin instance. The default flag is set on SD_CREATE, then it can be configured on SD_CONFIGURE and SD_LOAD_DATA.

These are the possible values:

SD_FLAG_DRAW = The plugin will draw directly the object appearance, it is a drawing plugin (i.e. analog clock). If a drawing plugin is selected for an object, the user won't be able to directly setup the messages/states in the GUI. The appearance will be only configurable through the plugin configuration dialog.
SD_FLAG_SET_SIZE = This flag can be used by drawing plugins, if the plugin needs to determine the initial object size, instead of using the one specified by the user in the object size settings (that will be disabled). In this case the plugin will receive SD_GET_OBJECT_SIZE when the host needs to know those values to create or modify the window or do other internal adjustments.
SD_FLAG_CUSTOM_ACTION = The plugin will perform a custom action (i.e. control Winamp). It is a command plugin. If a command plugin is selected for an object, the command class will switch automatically to "Custom class" and the "Configure" button will open the plugin configuration panel. See SD_COMMAND below for more details.
SD_FLAG_CUSTOM_STATES = The plugin has custom states that it wants host to observe*/
SD_FLAG_ONLY_CUSTOM_STATES = Items/objects using this plugin should only show the custom states when the users is editing the item's appearance.
SD_FLAG_FILTER_STATE_CHANGES = The plugin receives SD_STATE_CHANGING SD_STATE_CHANGED that inform the plugin about state messages. The plugin can modify and/or remove them as they get sent.
SD_FLAG_SUBCLASS = The plugin should set this flag to indicate if it wants to get and set messages from the window procedure sent from the host through SD_WINDOW_MESSAGE. Note that the plugin MUST NOT subclass the object window. This hooking service should be used instead.
SD_FLAG_DRAW_PPALPHA = The plugin informs the host that it will use the 4th byte of the 32bit HBITMAP to supply per-pixel alpha blending information.
SD_FLAG_MORE_INSTANCES = By default the host will avoid that more instances of the same plugin are added to an object. This flag will make it possible to add more than one instance.

Typical order of messages

 Message  What it does
SD_QUERY_PLUGIN_INFO Can be called only for querying purpose, in this case the other messages won’t be called.
SD_INITIALIZE_MODULE Only called if this is the first plugin from this DLL being loaded.
SD_CREATE_PLUGIN Setup pluginIndex, allocate memory
SD_CONFIGURE These are the two ways a plugin instance can be configured, infact the plugin can here modify the plugin flags.
SD_QUERY_CUSTOM_STATES Custom states actually depend on the plugin configuration, so these should be set when the plugin is first time created and when it is configured.

Custom states actually depend on the plugin configuration, so these should be set when the plugin is first time created and when it is configured.
SD_SAVE_DATA Save plugin data to object directory or plugin directory. Can be called at any time between SD_INITIALIZE and SD_TERMINATE.
SD_TERMINATE_PLUGIN Uninit plugin, stoping any times it has, etc..
SD_DESTROY_PLUGIN Destroy pluginIndex, deallocate memory.
SD_DEINIALIZE_MODULE Only called if this is the last plugin from this DLL being unloaded.

In order to support Apply/Cancel/Ok in the object configuration panel the plugin is requested to duplicate its plugin data through SD_DUPLICATE_PLUGIN.


SDHostMessage is used by the plugin to communicate with the host or request certain tasks (i.e. move an object, get the theme directory, etc.).

The host will export this function as:

DWORD SDHostMessage (UINT messageID, DWORD param1, DWORD param2)

The pointer to this function is passed to the plugin in the SD_INITIALIZE_MODULE message.

Here is a list of supported SDHostMessage messages:


char *szDir;
param1 = (DWORD) szDir;
param2 = objID;

The object directory is the directory that contains the plugin custom files if needed, the files that will be registered through SD_REGISTER_FILE.
This folders should be part of the theme, and should be the folder where the object/theme is being saved to. E.g. something like DesktopX\CurrentTheme\ etc etc.. This is there to save object/theme-specific “settings” which change from object to object, theme to theme, like the color of a clock or something like that.


char *szDir;
param1 = (DWORD) szDir;

Host would fill szDir with the path of the common plugins folder for DX or for OB… like DesktopX\SDPlugins\ or ObjectBar\Plugins\. The plugin could then create subfolders for itself as needed, so it can store data common to everything that uses this plugin.

Again this is here so the plugin knows where to save general “data”, which should be common no matter what theme/object uses it, such as for the command line plugin’s history, etc.


typedef struct SD_IMAGE_INFO_
char fileName[MAX_PATH]; // input: full path
HBITMAP hb; // output: receives a 32 bit bitmap
int width; // input: set this to the extract icon size if the file is an icon. output: image width
int height; // output: image height
BOOL bHasAlpha; // output: true if the image contains per pixel alpha values, false otherwise

Param1 = (DWORD) ⅈ

Loads a .ico, .bmp, .jpg or .png (all formats supported) file to a 32 bit bitmap.


Param1 = objID;

Sends a request to update/redraw the object (i.e. the clock plugin would call this each second).

The plugin will receive back a SD_DRAW message when the host is ready to have the object redrawn.

Depending by the implementation of the host, the plugin could receive SD_DRAW back immediately or not.


Param1 = objID;
Param2 = (DWORD) szCurState;

The host copies in szCurState the current state identifier.


Would pass a string to the host to set the current state to that custom state if it exists.

Param1 = objID;
Param2 = (DWORD) szNewState;


Param1 = objID;
Param2 = (RECT*) lprcContainerRect;

The plugin should pass a pointer to a RECT struct (lprcContainerRect) in Param2, which the host will fill with the rectangular area of the screen occupied by the container HWND, the object/bar that owns the plugin whose ID is passed in Param1.


Param1 = objID;
Param2 = (RECT*) lprcObjectRect;

The plugin should pass a pointer to a RECT struct (lprcObjectRect) in Param2, which the host will fill the rectangular area occupied by the object in the HWND (the container). This is could not be the same rectangle passed on SD_DRAW, because that rectangle refers to the area occupied in the passed HDC.

Allows the plugin to process mouse handling and such if it subclasses, and could also be used for some other purposes as well.


Param1 = objID;
Param2 = (char*) newText;


char szFilename[MAX_PATH];
Param1 = (DWORD) szFilename;

The plugin sends this message to the host while processing the SD_SAVE_DATA message. This message will need to be sent once for every file that needs to be registered. The plugin will need to register all files that will need to be saved on every SD_SAVE_DATA message.


Param1 = objID;
Param2 = (char*) newText;


param1 = (DWORD) (char*) szObjectDefinition;
param2 = (DWORD) IOversion;

The plugin creates an object. It must make sure all required graphic and sound files are present in the theme directory.

szObjectDefinition contains the multiline (\r separated) object string from “OBJECT” to “!OBJECT”.

IOversion is the version of the IO syntax used in szObjectDefinition. You should check what version is used by the latest DX version and use the same number and latest syntax. You can find the version number in the first line of theme.ini.


HBITMAP *hb = (HBITMAP *) param1;

Converts a double height 32 bit bitmap to a single height 32 bit bitmap with packed alpha.


typedef struct SD_SOP_INFO_
int x;
int y;
int width;
int height;
DWORD flags; // any combo of these flags:
// SD_SOP_MOVE: x and y are used
// SD_SOP_SIZE: width and height are used (drawing plugins only)
// SD_SOP_SHOW: shows the object
// SD_SOP_HIDE: hides the object
// SD_SOP_ONTOP: sets the object on top of its zorder class, i.e. if it is a desktop level object, it will be placed on top of all other desktop level objects

Param1 = objID; // object ID
Param2 = (SD_SOP_INFO*) &sop; // destination


Enumerates all the loaded objects.

BOOL (CALLBACK *EnumObjectsProc) (DWORD ObjID, DWORD lParam);
To continue enumeration, the callback function must return TRUE; to stop enumeration, it must return FALSE.
param1 = (DWORD) EnumObjectsProc;
param2 = (DWORD) appDefinedValue;


Param1 = objID; // object ID
Param2 = (DWORD) (BOOL) bDeleteChildren; // set TRUE to delete also all children of the object referred by objID

Returns TRUE if no errors occurred, returns FALSE if: - the objID was invalid or – the object was being configured.


char szObjectGUIID[100];
Param1 = (DWORD) objID;
Param2 = (DWORD) szObjectGUIID; // out

The host copies in szObjectGUIID the object ID defined by the user in the GUI.


char szObjectGUIID[100];
Param1 = (DWORD) objID;
Param2 = (DWORD) szObjectGUIID; // inThe host sets the object ID defined in the GUI as szObjectGUIID. This message is the opposite of SD_OBJID_FROM_GUIID.


Returns TRUE if DesktopX is in User mode, FALSE if it is in Design/Edit mode.


Mail Notification Plugin SDK Sample

This plugin is a complete SDPlugin sample that features config load/save, messaging and configuration.

You can download the plugin here (.ZIP).


Scriptable Mail Notification Plugin SDK Sample

This plugin is a scriptable version of the old SDMailNotify plugin. For an user perspective discussion please refer to the Developer's Guide: Scripting - Scriptable plugins section. For a real sample you can import the widget MailCheck.exe into DesktopX Builder and check how its script works with regards to plugin interoperability.

You can download the plugin source code here (.ZIP).

There are a few things to note while you look at the source code:

  • There is no configuration management. It is left to the actual script.
  • It uses the special SD_SCRIPTABLE_PLUGIN_EVENT message to fire script events. This is supposed to solve threading issues as compared to actual native event mechanisms. With this regard, DesktopX is serving as an event proxy.
  • You should compile using the Debug or Release MinSize configuration.
  • There is a custom implementation of DllRegisterServer all scriptable plugins should comply with. It is basically registering the plugin information using HKEY_CURRENT_USER rather than HKEY_LOCAL_MACHINE. This allows auto-setup when running a gadget under limited user privileges.

Previous Page     Next Page