Finalizing the Main DLL

Here we will be adding the NotePadCmd function to note_plug.dll which will be called each time notepad recieves a WM_COMMAND message, as well as modifying the NotePlug function to properly enumerate and call a predefined function from each plugin DLL.

Tools Used

[~] Dev-C++ - For compiling the DLL files.
[~] Note_plug.zip - DLL and source code for note_plug.dll.

Introduction

Our primary DLL, note_plug.dll, contains two functions: NotePlug and NotePadCmd. These have each been briefly covered in previous sections, but here we will complete them by allowing them to enumerate all DLL files in the plugins directory, and call each DLL's pluginit and plugproc functions. Each plugin DLL must contain one of each of these functions in order to be a valid plugin. They take the following parameters:

DLLIMPORT int pluginit(HMENU menu,HMENU submenu,HWND handle,int msgno)

DLLIMPORT int plugproc(int Msg,HWND hwnd)

It is NotePlug's responsibility to call each plugin's pluginit function, while NotePadCmd will call each plugproc function.

NotePadCmd

As seen previously, NotePadCmd will recieve three arguments: notepad's window handle, the ID number of the WM_COMMAND message and an unknown integer value. The NotePadCmd function must then send the window handle and message ID number to the plugproc function in each plugin DLL; the plugins will then process the message and perform the appropriate actions.

To accomplish this, NotePadCmd will need to load each plugin DLL, then get the address of the plugin's plugproc function. This can be done via the LoadLibrary and GetProcAddress functions, but we don't want to call these for each and every plugin every time notepad recieves a WM_COMMAND message; we want to only load them once, the first time NotePadCmd is called. In creating the previous assembly code to call NotePadCmd, I noticed that when notepad is first loaded, it recieves a WM_COMMAND with an ID of 33554447; using this, when NotePlugCmd recieves an ID of 33554447, it will load all the DLLs and save their respective function's addresses in the procaddrs array:


********************Begin NotePadCmd Code********************
int retval;
WIN32_FIND_DATA data;
HINSTANCE plugprochandle;
HANDLE filehandle;
char dllname[260];
FARPROC procaddrs[1024]; //Limits you to 1024 plugins. Can be changed if you desire.
int a;

DLLIMPORT int NotePadCmd(HWND hwnd,int msg,int unknown)
{
//Set a to zero each time the function is called
a = 0;
//Notepad recieves a WM_COMMAND message with an ID of 33554447 when it initially loads.
//When this is called, we load the addresses of each plugin procedure for later use.
if(msg == 33554447){
//Find the first dll file in the plugins directory filehandle = FindFirstFile("plugins\\*.dll",&data);
if(filehandle == INVALID_HANDLE_VALUE){return 1;}
strcpy(dllname,"plugins\\");
strcat(dllname,data.cFileName);
//Load the plugin DLL
plugprochandle = LoadLibrary(dllname);
//Get the address of the plugin's plugproc function and save it in the procaddrs array
procaddrs[a] = GetProcAddress(plugprochandle,"plugproc");
if(procaddrs[a]){
a++;
}

//Find all subsequent dll files in the plugins directory
while(FindNextFile(filehandle,&data) && a < 1024){
strcpy(dllname,"plugins\\");

strcat(dllname,data.cFileName);
//Load the plugin DLL
plugprochandle = LoadLibrary(dllname);
//Get the address of the plugin's plugproc function and save it in the procaddrs array
procaddrs[a] = GetProcAddress(plugprochandle,"plugproc");
if(procaddrs[a]){
a++;
}

}
return 0;
}

//Pass the WM_COMMAND message ID and window handle to each plugproc function
while(procaddrs[a]){
retval = procaddrs[a](msg,hwnd);
//If the return value is 1, exit immediately
if(retval){return retval;}
a++;
}
return retval;
}

********************End NotePadCmd Code********************

The first thing that happens is NotePlugCmd checks to see if the message ID is 33554447; if so, that indicates that this is the first time the function has been called, and it needs to load all the plugin DLLs and get the address of each of their plugproc functions. Each plugin must contain a plugproc function that takes two parameters: the message ID number and the main window handle. NotePlugCmd enumerates all the dll files in the plugins directory using FindFirstFile and FindNextFile. It loads each DLL via LoadLibrary, then uses GetProcAddress to retrieve the address of the DLL's plugproc function, which is stored in the procaddrs array. If the message ID number is not 33554447, then NotePadCmd loops through all the function addresses in the procaddrs array and calls them with the message ID number and window handle parameters. If one of the functions returns a non-zero value, indicating that the WM_COMMAND message should not be furthur processed, then NotePadCmd returns immediately without calling any remaining plugin functions.

NotePlug

Our previous NotePlug function worked, but it added menu options itself and did not allow plugin modules to do so. Our modified NotePlug function will enumerate all the plugin DLL files, and use GetProcAddress to retrieve the address of each pluginit function. Each plugin must contain a pluginit function, which is called once, and whose job it is to add menu options to notepad's menu as well as perform any additional computations as neccessary for the plugin:

********************Begin NotePlug Code********************
FARPROC libproc;
HINSTANCE libhandle;

DLLIMPORT int NotePlug(HWND handle)
{ 
    a = 0;
    /***************Create Plugins popup menu*********************/
    //First ID number to be assigned is 2001      
    int msgno=2001;
    //Get notepad's menu handle
    HMENU menu=GetMenu(handle);
    //Create a popup menu
    HMENU submenu=CreatePopupMenu();
    //Insert the popup menu between 'View' and 'Help'
    InsertMenu(menu,4,MF_BYPOSITION | MF_POPUP,(UINT)submenu,"&Plugins");
    //Apply changes to the menu
    SetMenu(handle,menu);
    /**************End create plugins popup menu*********************/
    
    /***************Enumerate all DLL files in the plugins directory*******************/
    //Find the first dll file in the plugins directory     
    filehandle = FindFirstFile("plugins\\*.dll",&data);
    if(filehandle == INVALID_HANDLE_VALUE){return 1;}
    strcpy(dllname,"plugins\\");
    strcat(dllname,data.cFileName);
    //Load the plugin DLL; NotePadCmd should have already loaded all DLLs, so 
    //LoadLibrary should just need to return the module handle
    libhandle = LoadLibrary(dllname);
    //Get the address of the plugin's pluginit function
    libproc = GetProcAddress(libhandle,"pluginit");
    //Call the first plugin's pluginit function
    if(libproc) {
    msgno=libproc(menu,submenu,handle,msgno);
    //Increment msgno by one
    msgno++;
    }

    //Loop to enumerate the remaining dll files
    while(FindNextFile(filehandle,&data)){
    strcpy(dllname,"plugins\\");
    strcat(dllname,data.cFileName);
    //Load the plugin DLL
    libhandle = LoadLibrary(dllname);
    //Get the address of the plugin's pluginit function
    libproc = GetProcAddress(libhandle,"pluginit");
    //Call the plugin's pluginit function and  increment msgno by one
    if(libproc) {
    msgno=libproc(menu,submenu,handle,msgno);
    msgno++;
    }
    }
    /***************End DLL enumeration in the plugins directory*******************/
    return 0;
}
********************End NotePlug Code********************

As before, NotePlug creates the 'Plugins' popup menu, but this time it also enumerates all the DLL files in the plugins directory and calls the pluginit function from each one. It is the individual plugins responsibility to add its own menu options for the user to interact with. Also note the msgno integer passed to each pluginit function; this is the ID value to be assigned to the plugin's menu option. Should the plugin create multiple menu options, it must increment msgno (and subsequently, the next menu option's ID number) by one for each menu option. The msgno value is returned to NotePlug, which increments it by one before calling the next pluginit function; this way, each plugin can have as many menu options as it wants without worrying about multiple plugins using the same ID number.

Conclusion

Now that note_plug.dll is fully functional, all that is left is to create a skeleton plugin structure for other coders to follow when creating their own plugins.

Copyright ©2006 craigheffner.com