» Home

  » Plugins

      » Mime

            » Developers Guide

               » Create the plugin

Brass Plugins: Mime Developers Guide

 

This page shows you how to create a plugin criteria DLL with win32, C++ and MS Visual C++ 6. If you want to code in another language then the principles still apply but you'll be on your own to implement them.

 

Creating the plugin - Win32 & C++

Download the VC6 workspace & code for this sample plugin

Now that we have our application ready to receive gesture messages, we need to code the criteria DLL to interface with Mime. I like to keep the DLL's nice and simple so we'll code this with straight win32 and C++. You can do yours with MFC if you really want, but we won't cover that here.

The purpose of the criteria DLL is twofold. First it tells Mime what the criteria are for any given action. Second, it provides code for each action. Mime registers a gesture from the user and checks your plugin criteria for a match. It then calls the appropriate function that matches the gesture action.

We start by creating a standard win32 DLL using the "Win32 Dynamic Link Library" appwizard. I've named the project "TestPlugin" - the DLL name is relatively important, it should be unique and descriptive. When prompted what kind of DLL I wanted to create, I chose a "simple DLL". This tells the Appwizard to make an empty .cpp file ready for us.

Open "TestPlugin.cpp" and all we have is an empty DLLMain as in the screenshot above. Great! First thing to do is to delete DLLMain - we don't need it. So now we're left with an empty file containing a single #include "stdafx.h". Time to get coding.

 

The Standard Elements of a Plugin

Mime retrieves criteria data from plugins via a standard struct, so we need to add the struct definition to the start of the TestPlugin.cpp file:

 

You must never change this struct in any way. If you're looking to copy and paste this, here it is in text form:

typedef struct _tagWindowCriteria
{
    char m_szWindowName[MAX_PATH];
    char m_szWindowClassName[MAX_PATH];
    char m_szParentWindowName[MAX_PATH];
    char m_szParentWindowClassName[MAX_PATH];
    char m_szOverallWindowName[MAX_PATH];
    char m_szOverallWindowClassName[MAX_PATH];
} WINDOWCRITERIA;

 

Next we need to add the version struct and export function:

 

The ACTIONVERSION struct and the GetPluginMimeInterfaceVersion function are essential for version control. As Mime is updated extra features will be added. If there wasn't a method of tracking versions, all the action plugins from a previous version of Mime wouldn't work in the new version. You must always put the current version of the interface into this struct.

How do you know what the current version is? Simple - visit the Quick Reference Page. The version numbers you need to use for the current featureset are listed there.

Mime uses the versions returned by the function to know what information to pass the plugin DLL. If you're not sure how or why this works, don't worry. All you need to do is to visit the Quick Reference Page. Again if you're looking to copy any paste the code, here it is:

typedef struct _tagActionVersion
{
    int m_iMajorVersion;
    int m_iMinorVersion;
    int m_iBuildVersion;
} ACTIONVERSION;

// The version export function
extern "C" __declspec(dllexport) void GetPluginMimeInterfaceVersion(ACTIONVERSION* pVersion)
{
    pVersion->m_iMajorVersion = 0;
    pVersion->m_iMinorVersion = 1;
    pVersion->m_iBuildVersion = 1;
}

I'll repeat this for the final time. You must never, ever change the structure of the criteria struct, the version struct or the GetPluginMimeInterfaceVersion function.

 

Coding the Implementation

Now we're ready to code our implementation. In the first part of this guide, whether you followed the MFC or win32 version, we added a message handler for our MIME_GESTURE_SHOWABOUTBOX message. We need to add the definition of this message to our TestPlugin.cpp file too:

 

Time to code the implementation. The entire purpose of this exercise is to load the about box with a gesture, so lets add a ShowAboutBox() function. The name of the function is important, as this is what the user sees in Mime as the "action". The prototype of all action functions is:

extern "C" __declspec(dllexport) void Function(HWND hWnd, const char* szParam)

 

Note that we (of course) need to export the function from the DLL.

 

Simple enough. When the user makes the gesture they have associated with ShowAboutBox, this function posts our custom message back to the application. Mime provides the function with the correct HWND to send the message to, and the message handler we wrote in our test application will show the About box. All we're doing here is standard message sending within Windows.

 

Dealing With User Parameters

The szParam parameter is passed to your action function when the user supplies a parameter for the action. For example, if the action function was "LoadAWebPage", the szParam might be "http://www.google.com". Parameters are completely and entirely handled within Mime for you - you'll see how to tell Mime you want the user to supply a parameter in the next section, when we create the descriptor file. All you do is tell Mime you want a user-supplied parameter - it magically arrives in your action function for you with no extra work.

If you told Mime you didn't need a parameter for this action function, szParam will be either "0" (that's a string literal with the zero character in it) or NULL (a zero length string). You should test for this as follows:

extern "C" __declspec(dllexport) void SampleFunc(HWND hWnd, const char* szParam)
{
    if(strlen(szParam) == 0 || strcmp("0", szParam)
    {
        // No parameter was supplied by the user, handle this
    }

    // Normal implementation
}

 

Remember that Mime cannot know exactly what you want to do with this parameter, so the user is free to type whatever they want (including total rubbish). Your action function should sanitize szParam and make sure it's suitable for whatever you want to do with it.

 

Setting the Criteria

That's all the code we need for the action. Now we need to configure the criteria.

From a developer point of view, every single action function has an associated criteria function. Even though the user only sees one level of criteria in the Mime interface, you as the developer can specify individual criteria for every single function. An example of when you would want to use this is MDI applications. You may only want to allow certain gestures to have actions in certain windows/views, rather than in every single part of the application.

To configure the criteria for an action, we create a second function named exactly the same as the action function but with "Criteria" appended. In other words if the action function is "PrintPage", the criteria function is "PrintPageCriteria". In this case, our function is called ShowAboutBoxCriteria().

The prototype for all criteria functions is

extern "C" __declspec(dllexport) void FunctionNameCriteria(WINDOWCRITERIA* pCriteria)

 

As expected it must be exported from the DLL.

 

 

In the screenshot there are 2 sets of strcpy() commands - if you tried this exercise with the win32 app you must delete the MFC lines, if you tried it with the MFC app you must delete the win32 line.

If you don't delete the non-applicable lines, this tutorial won't function. Criteria are cumulative.

The purpose of the criteria function is to fill out the pCriteria struct parameter with the window criteria that must be met for the action to be taken. The criteria specifies the window in which the gesture must take place. Here's the members of the WINDOWCRITERIA struct again:

m_szWindowName                - The name/window title of the window the gesture must be made in
m_szWindowClassName           -
The class name of the window the gesture must be made in
m_szParentWindowName          -
The name of the parent window of the window the gesture must be made in
m_szParentWindowClassNamex    -
The class of the parent window of the window the gesture must be made in
m_szOverallWindowName         -
The name of the overall window of the window the gesture must be made in
m_szOverallWindowClassName    -
The class of the overall window of the window the gesture must be made in

 

That might be a little confusing at first glance, so here's a screenshot of PaintShop Pro, with the output of Spy++ added into it:

 

The red lines link the Spy++ window entries to the actual windows in the screenshot. Here you can visually see the image canvas window has a class of "AfxFrameOrView42", and is a child of the window titled "Image66 (1:1) (Background)". That window is in turn a child of "MDIClient", which finally is a child window of "PaintShop Pro - Image 66".

If the user made a gesture on the image canvas window, the windows would be as follows:

Window Name: <blank>
Window Class: AfxFrameOrView42

Parent Window Name: Image66 (1:1) (Background)
Parent Window Class: Afx:40000000:b:10011:6:b6066f

Overall Window Name: "PaintShop Pro - Image 66"
Overall Window Class: Afx:40000000:8:10011:0:3063d

 

We skip any windows between the parent window and overall window as these usually are of little help.

Now you (hopefully) understand the meaning of the fields in the WINDOWCRITERIA struct. It is only necessary to fill out the struct members that are needed to identify the window. So in the case of PaintShop Pro you would only need to set the following:

m_szWindowClassName = AfxFrameOrView42
m_szOverallWindowName = "Paint Shop Pro"

Leave the rest of the fields set to NULL and Mime will be able to perfectly match a gesture on the canvas window to this criteria. Filling out more fields than necessary actually impacts performance, because Mime ignores any null fields but does match fields with data in them.

Criteria are cumulative, so all non-NULL fields must positively match for a criteria to be met.

The next part of this puzzle is the window name field. Note how I only specify "Paint Shop Pro" in the variable above, not the full "Pain Shop Pro - Image 66".

A lot of applications change their window titles to reflect what's being worked on at the current time. To handle this, Mime will pattern match window names (window, parent window and overall window) against criteria. So specifying "Paint Shop Pro" for the overall window name criteria will match against PaintShop Pro, no matter what image is being edited.

Mime does not pattern match on class names - only exact matches are allowed.

Where possible you should match on class names rather than window titles - explicit checking classnames is faster than pattern matching against window titles.

 

Going back to our test application, if you used the MFC version of the app then the criteria will be:

m_szWindowClassName = "#32770"
m_szWindowName = "TestAppMFC"

Note that we need to specify both the class name and the window name/title. MFC gives a standard class name to dialogs, so if we didn't include the windowname our criteria would match on any MFC dialog app!

If you used the win32 version of the app, the criteria is simply:

m_szWindowClassName = "TESTAPP"

Note that we use the class name in preference to a window title - explicit checking classnames is faster than pattern matching window titles. Yes, I repeated that twice for emphasis.

If you're wondering how I got the window titles and classnames (and what they are) for all these windows, you may want to brush up on your Windows programming theory. There are plenty of good window spy applications available but I just use good old Spy++. You can also use the secret Mime gesture, which is discussed at the end of the Exclusions page.

The exclusions doc page for Mime works on the same window matching principle as described here. You may want to review it.

 

Okay, we've created a test app and a DLL containing our action function and criteria function. Time to create the descriptor file.