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.
|