Basic Plug-In Code (How to Create a Plug-In) Changed in Version 2022.1

www.CAD6.com

The CAD6interface supports three types of plug-ins: External Commands, Import Filters and Export Filters. All three plug-in types use similar basic code that performs initialization and termination. For this task, each plug-in supplies 4 to 8 procedures that can be called by the serving application (sometimes referred to as "Callback Procedures"):

 

DllMain

This procedure is called at the time the plug-in is loaded into the serving's application memory, i.e. at the time the serving application itself is being loaded. Usually, this procedure's only task is to save the plug-in's instance handle for further use.

 

MKI_PlugInInit

This procedure is usually called immediately after the plug-in has been loaded and supplies the plug-in with some information about the serving application (like its interface version, its serial number etc.). In addition, it supplies the address of a MKI_PLUGIN_ID structure that the plug-in should fill with information about itself, like plug-in type (external command, import filter or export filter), plug-in name and available commands. Finally, this procedure is the right place to permanently allocate memory, to load settings, to initialize global data etc.

 

MKI_PlugInCommand

This procedure is called each time the user selects a command that is to be handled by this plug-in.

 

MKI_PlugInExit

This procedure is called at the time the plug-in is to be discarded from memory, i.e. at the time the serving application is terminated. This is the right time to free all memory allocated by the plug-in and to save settings (if required).

 

MKI_PlugInImport

This procedure must exist in any import filter. It is called each time the serving application wants to import a non-MKD drawing.

 

MKI_PlugInExport

This procedure must exist in any export filter. It is called each time the serving application wants to export a non-MKD drawing.

 

MKI_PlugInNotify Renamed in Version 2020.0

This optional procedure is called each time the serving application or another plug-in requires a plug-in-supplied extended object to be initialized or modified, e.g. by multiplying with a matrix. This procedure should only be provided if the plug-in is capable of handling plug-in-supplied extended objects and/or creating custom collection or pinboard items.

 

MKI_PlugInRequest New in Version 6.02

This optional procedure is called each time the serving application or another plug-in requires a textual request to be resolved, e.g. a database query. This procedure should only be provided if the plug-in is capable of handling at least one type of request.

 

In order to enable the serving application to find these procedure, their names must be exactly as stated above. In addition, they must be "exported". When developing plug-ins using a C++ compiler, this can be done by either declaring the procedures as MKI_INTERFACE_EXPORT (defined as extern "C" __declspec( dllexport )) or by stating them in the EXPORT section of a .DEF file supplied to the linker.

 

The following C++ code shows a possible implementation of the main four callback procedures for a plug-in of type External Command. Please note the comments included!

 

C++ Source Code

// Standard Windows include files

#include  "windows.h"

#include  "windowsx.h"

#include  "d2d1.h"

#include  "d2d1_1.h"

#include  "dwrite.h"

#include  "wincodec.h"

 

// CAD6 API include files

#include  "..\mki_lib6\mki_lib6.h"

#include  "..\mki_lib6\compiledate.h"

 

// Static variables used to store global handles.

HINSTANCE g_hInstDLL,

          g_hGlobalInst;

HWND      g_hGlobalWnd;

MKI_BITMAP_PTR g_pBitmap;

 

MKI_COMMAND_DATA  g_acCmds[MKI_PLUGIN_MENU_MAX];

MKI_BUTTON_DATA   g_acButtons[MKI_PLUGIN_BUTTON_MAX];

 

//----------------------------------------------------------------------

 

BOOL WINAPI

DllMain(

 HINSTANCE f_hInstance,

 DWORD f_dwReason,

 void* f_pDummy )

 

  // This procedure's only duty is to store the plug-in's

  // instance handle. All other initialization tasks should

  // be performed inside the MKI_PlugInInit procedure below.

  switch( Reason )

  {

    case DLL_PROCESS_ATTACH:

      g_hInstDLL = f_hInstance;

      break;

 

    case DLL_PROCESS_DETACH:

      g_hInstDLL = nullptr;

      break;

  }

  return( TRUE );

}

 

//----------------------------------------------------------------------

 

MKI_INTERFACE_EXPORT __int32

MKI_PlugInInit(

 const LPCSTR f_pszSerialNumber,

 HINSTANCE f_hMainInst,

 HWND f_hMainWnd,

 __int32 f_nInterfaceVersion,

 MKI_PLUGIN_ID_REF f_rID )

{

  MKI_FILENAMEW szFileName;

 

  // Check whether the serving application supports the

  // required interface version. If not, return immediately

  // without calling any interface function!

  if( f_nInterfaceVersion > MKI_INTERFACE_VERSION )

  {

    return( MKI_INIT_OLD_PLUGIN );

  }

  else if( f_nInterfaceVersion < MKI_INTERFACE_VERSION )

  {

    return( MKI_INIT_OLD_APPLICATION );

  }

 

  // Save the instance handle and the main window's handle

  // of the serving application for further reference.

  // If the plug-in wants to open popup windows for user input

  // or data display, these windows should be created with

  // hMainWnd as their parent window!

  g_hGlobalInst = f_hMainInst;

  g_hGlobalWnd  = f_hMainWnd;

 

  // Load the bitmap file with icons (32 bit RGBA colors PNG file).

  // Size of menu icons is 48 x 48 pixels, command icons 80 x 80 pixels.

  MKI_FileApplicationPath( "SAMPLE.PNG", szFileName );

  g_pBitmap = MKI_BitmapLoadImage( szFileName );

 

  // Set the OwnerID and the plug-in identification. For a

  // further description, see MKI_PLUGIN_ID.

  f_rID.m_nOwnerID    = 9999;

  f_rID.m_nPlugInID   = 0;

  f_rID.m_nPlugInCTRL = MKI_PLUGINCTRL_ALL;

  f_rID.m_pContainer  = nullptr;

 

  // This plug-in supplies external commands that shall be

  // listed in the TRIM menu, so set PlugInType to

  // MKI_PLUGINMENU_TRIM.

  f_rID.m_cPlugInData.m_nType = MKI_PLUGINMENU_TRIM;

 

  // Initialize the plug-in's name to be displayed in the

  // serving application.

  f_rID.m_cPlugInData.m_cInputData.m_nCommandMode  = MKI_COMMAND_DIRECT;

  f_rID.m_cPlugInData.m_cMenuData.m_pszMenuEntry   = "Sample Plug-In";

  f_rID.m_cPlugInData.m_cMenuData.m_pszDescription = "Sample Plug-In >";

 

  f_rID.m_cPlugInData.m_cIconData.m_pIconBitmap  = g_pBitmap;

  f_rID.m_cPlugInData.m_cIconData.m_nIconXOffset = 0;

  f_rID.m_cPlugInData.m_cIconData.m_nIconYOffset = 0;

  f_rID.m_cPlugInData.m_cIconData.m_nIconXSize   = MKI_ICON_COMMAND_SIZE;

  f_rID.m_cPlugInData.m_cIconData.m_nIconYSize   = MKI_ICON_COMMAND_SIZE;

  f_rID.m_cPlugInData.m_cIconData.m_nIconMode    = 0;

 

  f_rID.m_cCommandData  = g_acCmds;

  f_rID.m_cButtonData   = g_acButtonData;

  f_rID.m_cExtendedData = nullptr;

 

  // Fill the CommandData arrays with the command names, menu entries

  // and additional information.

  g_acCmds[0].m_cInputData.m_nCommandMode  = MKI_COMMAND_DIRECT;

  g_acCmds[0].m_cMenuData.m_pszMenuEntry   = "&1  Command One";

  g_acCmds[0].m_cMenuData.m_pszDescription = "Sample Plug-In >Command One";

  g_acCmds[0].m_cIconData.m_pIconBitmap  = g_pBitmap;

  g_acCmds[0].m_cIconData.m_nIconXOffset = 0;

  g_acCmds[0].m_cIconData.m_nIconYOffset = 1 * MKI_ICON_COMMAND_OFFSET;

  g_acCmds[0].m_cIconData.m_nIconXSize   = MKI_ICON_COMMAND_SIZE;

  g_acCmds[0].m_cIconData.m_nIconYSize   = MKI_ICON_COMMAND_SIZE;

  g_acCmds[0].m_cIconData.m_nIconMode    = 0;

  g_acCmds[0].m_nType = MKI_MENUMODE_ENTRY;

 

  g_acCmds[1].m_cInputData.m_nCommandMode  = MKI_COMMAND_DIRECT;

  g_acCmds[1].m_cMenuData.m_pszMenuEntry   = "&2  Command Two";

  g_acCmds[1].m_cMenuData.m_pszDescription = "Sample Plug-In >Command Two";

  g_acCmds[1].m_cIconData.m_pIconBitmap  = g_pBitmap;

  g_acCmds[1].m_cIconData.m_nIconXOffset = 0;

  g_acCmds[1].m_cIconData.m_mIconYOffset = 2 * MKI_ICON_COMMAND_OFFSET;

  g_acCmds[1].m_cIconData.m_nIconXSize   = MKI_ICON_COMMAND_SIZE;

  g_acCmds[1].m_cIconData.m_nIconYSize   = MKI_ICON_COMMAND_SIZE;

  g_acCmds[1].m_cIconData.m_nIconMode    = 0;

  g_acCmds[1].m_nType = MKI_MENUMODE_ENTRY;

 

  g_acCmds[2].m_cInputData.m_nCommandMode  = MKI_COMMAND_DIRECT;

  g_acCmds[2].m_cMenuData.m_pszMenuEntry   = nullptr;

  g_acCmds[2].m_cMenuData.m_pszDescription = nullptr;

  g_acCmds[2].m_cIconData.m_nIconMode    = -1;

  g_acCmds[2].m_nType = MKI_MENUMODE_ENTRY | MKI_MENUMODE_SEPARATOR;

 

  g_acCmds[3].m_cInputData.m_nCommandMode  = MKI_COMMAND_DIRECT;

  g_acCmds[3].m_cMenuData.m_pszMenuEntry   = "&+  About";

  g_acCmds[3].m_cMenuData.m_pszDescription = "Sample Plug-In >About...";

  g_acCmds[3].m_cIconData.m_pIconBitmap  = g_pBitmap;

  g_acCmds[3].m_cIconData.m_nIconXOffset = 0;

  g_acCmds[3].m_cIconData.m_nIconYOffset = 3 * MKI_ICON_COMMAND_OFFSET;

  g_acCmds[3].m_cIconData.m_nIconXSize   = MKI_ICON_COMMAND_SIZE;

  g_acCmds[3].m_cIconData.m_nIconYSize   = MKI_ICON_COMMAND_SIZE;

  g_acCmds[3].m_cIconData.m_nIconMode    = 0;

  g_acCmds[3].m_nType = MKI_MENUMODE_ENTRY;

 

  g_acCmds[4].m_nType = MKI_MENUMODE_END;

 

  // Fill the ButtonData arrays with the button names

  // and additional information.

  g_acButtons[0].m_nType = MKI_MENUMODE_END;

 

  // Return the current interface version to indicate the

  // initialization has been finished successfully.

  return( MKI_INIT_OK );

}

 

//----------------------------------------------------------------------

 

MKI_INTERFACE_EXPORT __int32

MKI_PlugInCommand(

 __int32 f_nCommandID,

 __int32 f_nExecMode,

 void* f_pData )

{

  __int32 nResult = FALSE;

 

  // Check which type of action is requested.

  switch( f_nExecMode )

  {

    // Execute the command.

    case MKI_EXECMODE_USER:

    case MKI_EXECMODE_SYSTEM:

      switch( f_nCommandID )

      {

        // The user selected the command with the index 1

        // (i.e. the command titled "Command One".

        case 1:

 

          // Insert here code for executing "Command One"

 

          nResult = TRUE;

          break;

 

        // The user selected the command with the index 2

        // (i.e. the command titled "Command Two".

        case 2:

 

          // Insert here code for executing "Command Two"

 

          nResult = TRUE;

          break;

 

        // The user selected the command with the index 4

        // (i.e. the command titled "About...".

        case 4:

          MessageBox( hGlobalWnd, "Release 2022\n\nAuthor: Bugs Bunny",

                                  "Sample PlugIn", MB_OK );

          nResult = TRUE;

          break;

      }

      break;

 

    // Display the corresponding help topic.

    case MKI_EXECMODE_HELP:

      switch( f_nCommandID )

      {

        case 1:

        case 2:

        case 4:

          MKI_DialogHelpTopic( hGlobalWnd, "SAMPLE", f_nCommandID );

          break;

      }

      break;

 

    // Load plug-in-dependent settings from the current drawing.

    case MKI_EXECMODE_GET_PROFILE:

      // Nothing to do here...

      break;

 

    // Save plug-in-dependent settings to the current drawing.

    case MKI_EXECMODE_SET_PROFILE:

      // Nothing to do here...

      break;

 

    // Initialize menu entry states.

    case MKI_EXECMODE_MENU_INIT:

      // Nothing to do here...

      break;

 

    // All plug-in have been loaded and initialized.

    case MKI_EXECMODE_READY:

      // Nothing to do here...

      break;

 

    // The application is up and running

    case MKI_EXECMODE_RUNNING:

      // Nothing to do here...

      break;

  }

  return( nResult );

}

 

//----------------------------------------------------------------------

 

MKI_INTERFACE_EXPORT bool

MKI_PlugInExit( bool f_fSaveSettings )

{

  // Usually, this procedure's duty is to free memory and

  // to save settings. For this simple plug-in, none of these

  // tasks is required, so do only return true.

  return( true );

}

 

Plug-ins of type Import Filter and Export Filter use similar callback procedures with three differences. First, inside the MKI_PlugInInit procedure, the value PlugInID->PlugInData.Type is set to MKI_PLUGINMENU_IMPORT or MKI_PLUGINMENU_EXPORT. Second, import and export filters may offer only a single command, i.e. they do not own a submenu and thus set PlugInID->CommandName to nullptr. As a result, the MKI_PlugInCommand procedure will always be called with CommandID set to zero. Third, they must offer either a MKI_PlugInImport or a MKI_PlugInExport procedure, respectively. They must not offer both!

 

CAD6interface 2024.2 - Copyright 2024 Malz++Kassner® GmbH