HOWTO: Build a Microsoft Word Add-in (WLL) Using Visual C++

ID: Q183758


The information in this article applies to:
  • Microsoft Office 2000 Developer
  • Microsoft Visual C++, 32-bit Editions, version 5.0
  • Microsoft Word 97 for Windows


SUMMARY

A WLL is an add-in for Microsoft Word that you can build with any compiler that supports building DLLs (dynamic link libraries). This article is designed to get you started building WLLs with Microsoft Visual C++ 5.0. To follow the steps outlined in this article, you should have some experience building DLLs and you should have the Microsoft Word Developer's Kit, which contains the necessary files to build a WLL.

For information about how to obtain the Word Developer's Kit, see the Microsoft Press Web site at the following address:

http://mspress.microsoft.com/


MORE INFORMATION

Create a WLL

  1. Create a new MFC AppWizard (.dll) project called ANewWLL.


  2. Copy capilib.c, capilib.h, wdcapi.h, wdcmds.h, wderror.h, wdfid.h, and config.h from the Microsoft Word Developer's kit to your project directory.


  3. Rename capilib.c to capilib.cpp.


  4. Add capilib.c, capilib.h, wdcapi.h, wdcmds.h, wderror.h, wdfid.h, and config.h to your project.


  5. Add the following as the very first line of capilib.cpp to avoid compiler errors about pre-compiled headers:


  6. 
    #include "stdafx.h" 
  7. In the CallCapi() function in capilib.cpp, change the following line:


  8. 
          InitWCB( lpwcb, retType, lpBuffer, cBufferSize ); 
    to the following:
    
          InitWCB( lpwcb, retType, (UCHAR *)lpBuffer, cBufferSize ); 
    Change the following lines:
    
       case 's':
       AddStringParam( lpwcb, va_arg(marker, LPSTR)); 
    to the following:
    
       case 's':
       AddStringParam( lpwcb, (UCHAR *)va_arg(marker, LPSTR)); 
  9. At the end of the capilib.cpp file, change the following lines:


  10. 
    static short (WINAPI *pfn_wdCommandDispatch) ();
    
    short WINAPI wdCommandDispatch(short CommandID, // fci
       short DlgOptions, // grfDlg
       short cArgs,
       LPWDOPR lpwdoprArgs,
       LPWDOPR lpwdoprReturn)
    {
       if (pfn_wdCommandDispatch == NULL)
          FARPROC) pfn_wdCommandDispatch = GetProcAddress(
             GetModuleHandle(NULL), "wdCommandDispatch"
          );
       return ((*pfn_wdCommandDispatch)(
          CommandID, DlgOptions, cArgs, lpwdoprArgs, lpwdoprReturn)
       );
    } 
    to the following:
    
    typedef unsigned short (CALLBACK *URET) (
       short, short, short, LPWDOPR, LPWDOPR
    );
    URET pfn_wdCommandDispatch;
    
    short WINAPI wdCommandDispatch(short CommandID, // fci
       short DlgOptions, // grfDlg
       short cArgs,
       LPWDOPR lpwdoprArgs,
       LPWDOPR lpwdoprReturn)
    {
       if (pfn_wdCommandDispatch == NULL)
          pfn_wdCommandDispatch = (URET)GetProcAddress(
             GetModuleHandle(NULL), "wdCommandDispatch"
          );
       return ((*pfn_wdCommandDispatch)(
          CommandID, DlgOptions, cArgs, lpwdoprArgs, lpwdoprReturn)
       );
    } 
  11. In the wdcapi.h, change the following lines:


  12. 
    typedef struct
    {
        short cArrayDimensions;
        short ArrayDimensions[];
    } ARRAY_DEF; 
    to the following:
    
    typedef struct
    {
        short cArrayDimensions;
        short *ArrayDimensions;
    } ARRAY_DEF; 
  13. Add the following lines after #include "ANewWLL.h" in ANewWll.cpp:


  14. 
    #include "capilib.h"
    #include "wdcmds.h"
    #include "wdfid.h" 
  15. Add the following code to the end of ANewWll.cpp:


  16. 
    // DocId of WLL session...
    short g_docId;
    
    // General-purpose variables...
    static char buf[8192];
    int err;
    
    // Routine to report errors encountered...
    void ReportError(char *msg) {
       static char errBuf[1024];
       sprintf(errBuf, "%s, Error = %d", msg, err);
       ::MessageBox(NULL, errBuf, "WLL Error", MB_SETFOREGROUND);
    }
    
    // Table of functions that we will register...
    struct {
       char *name; // Function name...
       char *desc; // Function description...
    } g_funcTbl[] =
    {
       {"MyMotd",     "Message of the day"},
       {"InsertTime", "Insert Time"},
       {0, 0} // List is null-terminated
    };
    
    // Menu name
    UCHAR *g_menuName = (UCHAR *)"&Custom Menu";
    
    // wdAutoOpen() - entry point for WLLs...
    short __stdcall wdAutoOpen(short DocID) {
       // Some diagnostic output, remove from your final code ...
       ::MessageBox(NULL, "In wdAutoOpen()!", "Msg", MB_SETFOREGROUND);
    
       // Store the docId for later use...
       g_docId = DocID;
    
       // Register our functions...
       int i;
       for(i=0; g_funcTbl[i].name; i++) {
          err = CAPIRegister(
             DocID, (UCHAR *)g_funcTbl[i].name,
            (UCHAR*)g_funcTbl[i].desc
          );
          if(err) {
             sprintf(buf, "CAPIRegister(%s, %s)",
                g_funcTbl[i].name, g_funcTbl[i].desc);
             ReportError(buf);
          }
       }
    
       // Add our menu...
       err = CAPIAddMenu(DocID, g_menuName, 1, 0);
       if(err) ReportError("CAPIAddMenu()");
    
       // Add our menu items...
       for(i=0; g_funcTbl[i].name; i++) {
          err = CAPIAddMenuItem(
             DocID, g_menuName, (UCHAR *)g_funcTbl[i].name,
             (UCHAR *)g_funcTbl[i].desc, -2, 0
          );
          if(err) {
             sprintf(buf, "CAPIAddMenuItem() for %s",
                g_funcTbl[i].name);
             ReportError(buf);
          }
       }
    
       // Initialize crt pseudo-random number generator...
       srand(time(0));
    
       return TRUE;
    }
    
    // Displays a message of the day (MOTD)...
    short __stdcall MyMotd(void) {
       char *name[] = {
          "Rebekah",
          "Brent",
          "Michael",
          "Joseph",
          "Bob",
          0
       };
       char *quote[] = {
          "An apple a day, keeps the doctor away!",
          "Carpe Diem: Seize the Day!",
          "What you dare to dream, dare to do!",
          "I think, therefore I am.",
          "A place for everything, and everything in its place.",
          "Home is where the heart is.",
          0
       };
       int nNames, nQuotes;
    
       for(nNames=0; name[nNames]; nNames++);
       for(nQuotes=0; quote[nQuotes]; nQuotes++);
    
       sprintf(buf, "%s says '%s'",
          name[rand()%nNames], quote[rand()%nQuotes]
       );
       ::MessageBox(NULL, buf, "XLL MOTD", MB_SETFOREGROUND );
    
       return 0;
    }
    
    // Inserts time at current document location...
    void __stdcall InsertTime(void) {
       WCB wcb;
    
       InitWCB(&wcb, TypeShort, NULL, 0);
    
       _strtime(buf);
       AddStringParam(&wcb, (UCHAR *)buf);
    
       err = wdCommandDispatch(
          wdInsert, CommandAction, wcb.cArgs, wcb.wdoprArgs, lpwdoprNil
       );
       if(err) {
          ReportError("wdInsert");
       }
    
    } 
  17. You will see a ANewWLL.DEF file in your project. Open that and add the names of the functions your .wll will export, so that the file looks like the following:


  18. 
    ; ANewWLL.def : Declares the module parameters for the DLL.
    
    LIBRARY      "ANewWLL"
    DESCRIPTION  "ANewWLL Windows Dynamic Link Library
    
    EXPORTS
         ; Explicit exports can go here
             wdAutoOpen
             MyMotd
             InsertTime 
  19. Compile and rename your ANewWLL.dll to ANewWLL.wll.


Use the Add-in with Microsoft Word 97

  1. Start Microsoft Word 97.


  2. On the Tools menu, click Templates and Add-ins. Add ANewWLL.wll and click OK. Notice that when you click OK, the wdAutoOpen() function runs, and your new menu is created.


  3. On the Custom Menu, click Message of the day. When your menu item is clicked, the MyMotd() function runs and displays a MessageBox with a quote such as "Rebekah says 'An Apple a day, keeps the doctor away!'."


  4. On the Custom Menu, click Insert Time. When you select this menu item, the InsertTime() function runs and inserts the current time into your document.



REFERENCES

Microsoft Word Developer's Kit (ISBN: 1-55615-880-7)

© Microsoft Corporation 1999, All Rights Reserved.
Contributions by Joe Crump, Microsoft Corporation

Additional query words: wll vc wdcommanddispatch capilib wdopr capi wcb

Keywords : kbVC500 kbSDKWord kbWord kbGrpDSO kbOffice2000
Version : :; WINDOWS:97; winnt:5.0
Platform : WINDOWS winnt
Issue type : kbhowto


Last Reviewed: June 18, 1999
© 2000 Microsoft Corporation. All rights reserved. Terms of Use.