// This code was compiled with Microsoft Visual C++ version 2.2.
// The entry point is set to DllMain, and all of the standard
// DLL libraries were used. To create a make file, just choose
// Project... New from VC++ 2.2, select DLL from the list of options,
// and add the source files. Compile and rename the DLL output file
// with a WLL file extension. Copy the WLL to Word's Startup 
// directory, or load the WLL using the Templates and Add-ins dialog
// box.

#include <windows.h> 
#include <stdlib.h>
#include <mmsystem.h>  // Link with winmm.lib
#include "wdcapi.h"
#include "wdcmds.h"       
#include "wdfid.h"
#include "config.h"       

#include "capilib.h"   // This file comes from the Word SDK

#include "basedefs.h"  // File of macros for easier coding
#include "base1ui.h"   // Resource constants
#include "wdmenus.h"   // Custom file for the GetMenuName function

HANDLE hDLLInst;       // hInstance of this DLL (WLL)
int    err;            // Error code for wdCommandDispatch
int    gDocID;         // Document ID

// These are the string constants to be placed on menu items in Word
char   szSound           [] = "Play &Windows Sound";
char   szSwapWords       [] = "&Swap Words";
char   szFontScroll      [] = "Font Size Picker";
char   szStringArray     [] = "Strin&g Array Demo";
char   szMyTable         [] = "&Insert Table";

// Menu separator string (this string should be localized!) 
char   szSeparator       [] = "(Separator)";


// These are the string constants to be placed on the status line
// while the user is scrolling through the menu
char   szStatusSound       [] = "Plays the SOUND.WAV file.";
char   szStatusSwapWords   [] = "Swaps the Words at the insertion point";
char   szStatusFontScroll  [] = "Adjusts the font size of the current word.";
char   szStatusStringArray [] = "Displays the name of all running applications.";
char   szStatusMyTable     [] = "Inserts a table with the cells labeled.";

// These are string constants that match the exported function name.
// Word 95 will use GetProcAddress using these values
char   szFuncSound       [] = "Sound";
char   szFuncSwapWords   [] = "SwapWords";
char   szFuncFontScroll  [] = "FontScroll";      
char   szFuncStringArray [] = "StringArray";
char   szFuncMyTable     [] = "MyTable";

// These are other strings
char   szSoundText       [] = "Can Your Hear The Sound? ";
char   szSoundWave       [] = "SOUND.WAV";
char   szInt             [] = "%d";
char   szSoundPath[255];  // The fully qualified path for the sound wave

// Here are the function prototypes

DLGPROC _loadds ScrollPickerDlgProc ( DIALOG_PARAMS );
void RemoveMenuItem ( int DocID, ushort iMenuID, LPSTR szItemText );

/****************************************************************************
   FUNCTION: DllMain(HANDLE, DWORD, LPVOID)

   PURPOSE:  DllMain is called by Windows when
             the DLL is initialized, Thread Attached, and other times.
             Refer to SDK documentation, as to the different ways this
             may be called.
             
             The DllMain function should perform additional initialization
             tasks required by the DLL.  In this example, no initialization
             tasks are required.  LibMain should return a value of 1 if
             the initialization is successful.
           
*******************************************************************************/

#ifdef WIN32
INT  APIENTRY DllMain(HANDLE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
  hDLLInst = hinstDLL;

  return 1;
}

#else
// Standard LibMain for a Windows DLL
//   
short CALLBACK LibMain (HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine)
{
	if (wHeapSize >= 0)
		UnlockData (0) ; 

	hDLLInst = hInstance;
   
	return 1 ;
}
#endif
//*************************************************************************
//
// GetMenuName
//
// Gets the name of the Word popup menu. This code is
// language independant, since numerical constants are used
// to define the menus. The numerical constants are defined
// in WDMENUS.H
//
// An example of the usage of this call would be
//
//   GetMenuName (DOCOPEN_File, szMenu, sizeof(szMenu));
//
// to get the name of the "File" menu when a document is open.
// 
//*************************************************************************

void GetMenuName ( ushort iMenuCode, LPSTR szName, ushort cbSize )
{
  short  iCode, iType;  // Parameters for Word's wdMenuText$ function
  WCB    wcb;           // Word Call Block
  int    err;           // error code

  iCode = iMenuCode % 100;  // The code is the lower two digits
  iType = iMenuCode / 100;  // The type is the third digit (see WDMENUS.H)

  InitWCB  ( &wcb, TypeString, szName, cbSize );   // Init for string return type
  AddShortParam  ( &wcb, iType  );        // Add the iType param
  AddShortParam  ( &wcb, iCode  );        // Add the iCode param
  WORDFUNCTION   ( wdMenuText$   );       // Call Word's MenuText$ Function
}

//*************************************************************************
// 
// This main entry point is automatically called when the WLL is loaded.
// 
//*************************************************************************

short WINAPI _loadds wdAutoOpen( short DocID )
{
  char   szMenu[128]; // Buffer for GetMenuName
  int    i;

  gDocID = DocID;

  // Build the path for the wave file, i.e. "C:\OFFICE\WORD\STARTUP\SOUND.WAV"
  GetModuleFileName (hDLLInst, szSoundPath, sizeof(szSoundPath));
  i = lstrlen ( szSoundPath ) - 1;
  while ( i && (szSoundPath[i] != '\\')) i--;
  if (i) i++;
  szSoundPath[i] = 0;

  // Register the five demo functions with Word
  CAPIRegister   ( DocID, szFuncSound      , szStatusSound      );                        
  CAPIRegister   ( DocID, szFuncSwapWords  , szStatusSwapWords  );                         
  CAPIRegister   ( DocID, szFuncFontScroll , szStatusFontScroll );                         
  CAPIRegister   ( DocID, szFuncStringArray, szStatusStringArray);                         
  CAPIRegister   ( DocID, szFuncMyTable    , szStatusMyTable    );                         
  
  // Get the "File" Menu's text
  GetMenuName ( DOCOPEN_File,  szMenu, sizeof(szMenu));

  // Add a menu separator at the end of the file menu
  CAPIAddMenuItem( DocID, 
                   szMenu, 
                   szSeparator,
                   "",
                   -2,              // -2 means at end
                   0
                   );
 
  // Add the "SwapWords" function to the end of the File Menu
  if  ( (err = CAPIAddMenuItem( DocID, 
                                szMenu, 
                                szFuncSwapWords,
                                szSwapWords,
                                -2,              // -2 means at end
                                0
                              ))
       != 0)
    return 0;  // WLL Failed
 
  // Add the "Sound" function to the end of File Menu
  if  ( (err = CAPIAddMenuItem( DocID, 
                                szMenu, 
                                szFuncSound,
                                szSound,
                                -2,              // -2 means at end
                                0
                              ))
       != 0)
    return 0;  // WLL Failed

  // Add the "StringArray" function to the end of File Menu
  if  ( (err = CAPIAddMenuItem( DocID, 
                                szMenu, 
                                szFuncStringArray,
                                szStringArray,
                                -2,              // -2 means at end
                                0
                              ))
       != 0)
    return 0;  // WLL Failed


  // Add the "My Table" function to the end of File Menu
  if  ( (err = CAPIAddMenuItem( DocID, 
                                szMenu, 
                                szFuncMyTable,
                                szMyTable,
                                -2,              // -2 means at end
                                0
                              ))
       != 0)
    return 0;  // WLL Failed

  // Add the "FontScroll" function to the Text Shortcut menu (right
  // click on a word in STANDARD text to see this menu)
  GetMenuName ( SHORTCUT_Text, szMenu, sizeof(szMenu));
  if  ( (err = CAPIAddMenuItem( DocID, 
                                szMenu, 
                                szFuncFontScroll,
                                szFontScroll,
                                4,              // Fourth spot on menu
                                2
                              ))
       != 0)
    return 0;  // WLL Failed

  // Success!
   
  return 1;
}

//*************************************************************************
// 
// This function is called when the WLL unloads
// 
//*************************************************************************

void WINAPI _loadds wdAutoRemove( void )
{
  // Clean up the menus before exiting
  // NOTE: This is not really necessary becasue the menu items were added 
  // using the DocID as the context. In this case Word removes the menu
  // items automatically when the WLL is unloaded. 
  RemoveMenuItem ( gDocID, DOCOPEN_File, szSwapWords    );
  RemoveMenuItem ( gDocID, DOCOPEN_File, szSound        );
  RemoveMenuItem ( gDocID, DOCOPEN_File, szFontScroll   );
  RemoveMenuItem ( gDocID, DOCOPEN_File, szStringArray  );  
  RemoveMenuItem ( gDocID, DOCOPEN_File, szMyTable      );

}      
 
//*************************************************************************
// 
// If this was a WordBasic macro, it would look like this:
// 
// Sub MAIN
//   WordLeft 1, 1  `Move the caret 1 word left, and select it
//   EditCut        `Cut the text
//   WordRight 1    `Move the caret 1 word right
//   EditPaste      `Paste the text
// End Sub
//
//*************************************************************************

void WINAPI _loadds SwapWords ( void )
{
  WCB    wcb;           // Word Call Block

  InitWCB (&wcb, TypeShort, NULL, 0 );  // WordLeft 1, 1
  AddShortParam ( &wcb, 1 );
  AddShortParam ( &wcb, 1 );
  WORDCALL ( wdWordLeft );

  InitWCB (&wcb, TypeShort, NULL, 0 );  // Edit cut
  WORDCALL ( wdEditCut );

  InitWCB (&wcb, TypeShort, NULL, 0 );  // WordRight 1,1
  AddShortParam ( &wcb, 1 );
  WORDCALL ( wdWordRight );

  InitWCB (&wcb, TypeShort, NULL, 0 );  // Edit Paste
  WORDCALL ( wdEditPaste );
}

//*************************************************************************
// 
// This Sound function first types "Can You Hear This? " and then
// plays the wave file.
// 
//***********************************************************************

void WINAPI _loadds Sound ( void )
{
  WCB    wcb;              // Word Call Block
  char   szWaveFile[255];  // Full path of wave to play

  InitWCB (&wcb, TypeShort, NULL, 0 );      // Insert "Can you hear this?"
  AddStringParam ( &wcb, szSoundText );     //
  WORDCALL ( wdInsert );                    // 

  // Build the filename

  lstrcpy (szWaveFile, szSoundPath);
  lstrcat (szWaveFile, szSoundWave);

  sndPlaySound ( szWaveFile, SND_ASYNC );  // SDK Multimedia function
}

//*************************************************************************
// 
// This function will bring up a little popup dialog box that the user
// can change the font size with via a scrollbar.
// 
//*************************************************************************

void WINAPI _loadds FontScroll ( void )
{
  WCB      wcb;            // Word Call Block
  int    iSize;          // Font size in points
  HWND     hWndWord;       // Window handle of MS Word

  // Get the hWnd of Word using the wdGetHwnd Word Function

  InitWCB ( &wcb, TypeShort, NULL, 0 );
  WORDFUNCTION ( wdGetHwnd );

  hWndWord = (HWND)(wcb.wdoprReturn.Long);
  
  // Get the size of the current font calling
  // FontSize() as a function
  InitWCB ( &wcb, TypeDouble, NULL, 0 );
  WORDFUNCTION ( wdFontSize );
  iSize = (int)wcb.wdoprReturn.Double;

  // Bring up a dialog box with the fontsize and scrollbar.
  // Pass a pointer to iSize as the lParam in WM_INITDIALOG

  if (DialogBoxParam ( hDLLInst, 
                       MAKEINTRESOURCE(SCROLLFONT), 
                       hWndWord, 
                       (FARPROC)ScrollPickerDlgProc, 
                       (LONG)(LPINT)&iSize 
                     )
     )
    {
    InitWCB ( &wcb, TypeVoid, NULL, 0 );  // Call Word's Function:
    AddDoubleParam ( &wcb, (double)iSize );  //
    WORDCALL ( wdFontSize );               // FontSize ( iSize )
    }

}

//**********************************************************************
//
// Your Basic Boring dialog box procedure with a scrollbar!
//
//**********************************************************************

DLGPROC _loadds ScrollPickerDlgProc ( DIALOG_PARAMS )
{
  CRACKER_VARS
  
  char   szTemp[64];
  int    iWZIQ = 0;

  static LPINT lpiSize;
         int   iSize;

  switch ( msg )
    {
    case WM_INITDIALOG: // lParam contains a pointer to iSize

      lpiSize = (LPINT)lParam; // Tuck away pointer in static var

      // Set the scrollbar's range for 4 to 127 points

      SetScrollRange ( GetDlgItem ( hDlg, IDD_SCROLLBAR ),
                       SB_CTL,
                       4, 127,
                       TRUE 
                     );

      // Set the position on the scroll bar

      iSize  = *lpiSize;
      SetScrollPos ( GetDlgItem ( hDlg, IDD_SCROLLBAR ), 
                     SB_CTL, iSize, 
                     TRUE
                   );

      // Update the static text control

      wsprintf ( szTemp, szInt, iSize ); 
      SetDlgItemText ( hDlg, IDD_CURSIZE, szTemp );

      break;
      
    case WM_HSCROLL:

      // Slice and dice your WM_HSCROLL message....
       
      iSize = GetScrollPos ( GetDlgItem ( hDlg, IDD_SCROLLBAR ), SB_CTL );

      switch ( wParam )
        {
        case SB_TOP:           iSize = 4;                                    break;
        case SB_BOTTOM:        iSize = 127;                                  break;
        case SB_LINEDOWN:      if (iSize < 127) iSize++;                     break;
        case SB_LINEUP:        if (iSize >   4) iSize--;                     break;
        case SB_PAGEDOWN:      if (iSize < 117) iSize+=10; else iSize = 127; break;
        case SB_PAGEUP:        if (iSize >  14) iSize-=10; else iSize =   4; break;
        case SB_THUMBPOSITION: 
        case SB_THUMBTRACK:    iSize = LOWORD(lParam);
        }

      SetScrollPos ( GetDlgItem ( hDlg, IDD_SCROLLBAR ), SB_CTL, iSize, TRUE );

      // Update the statix text

      wsprintf ( szTemp, szInt, iSize );
      SetDlgItemText ( hDlg, IDD_CURSIZE, szTemp );
      
      break;

    case WM_COMMAND:

      CRACK_MESSAGEsc
      switch ( CRACKER_wID )
        {
        case IDOK:

          /// If user presses OK, then update the iSize passed in

          *lpiSize = GetScrollPos ( GetDlgItem ( hDlg, IDD_SCROLLBAR ), SB_CTL );

          // FALL-THROUGH

        case IDCANCEL:
          
          EndDialog ( hDlg, IDOK == wParam );
          break;
        }
      break;

    default:

      return FALSE;
    }

  return TRUE;
}

//*************************************************************************
//
//  RemoveMenuItem: Removes a menu item from Word.
//
//*************************************************************************

void RemoveMenuItem ( int DocID, ushort iMenuID, LPSTR szItemText )
{
  int       i, iItemCount;
  char      szMenu[64];
  char      szName[64];
  char      szMenuItem[64];
  WCB       wcb;            // Word Call Block

  // Step 1: Get the text name of the popup menu

  GetMenuName ( iMenuID, szMenu, (ushort)sizeof(szMenu));

  // Step 2: Count the menu items using Word's 
  //         CountMenuItems ( szMenu ) built-in
  //         function.
  //
  //         The type is 0, 1, or 2, see the GetMenuName
  //         function to see how it ise used there as well.

  InitWCB  ( &wcb, TypeShort, NULL, 0    );  // Init struct
  AddStringParam  ( &wcb, szMenu         );  // Add menu string
  AddShortParam   ( &wcb, (ushort)(iMenuID / 100)  );  // Type ( 0, 1, or 2 )
  WORDFUNCTION    ( wdCountMenuItems     );
  iItemCount = wcb.wdoprReturn.Short;

  // Step 3: Loop through each spot on the menu, and locate
  //         the position where Word has this menu item.
  //         This loop runs backwards, in case you want to
  //         enhance it to delete multiple items, and we
  //         all know you delete items from menus in reverse
  //         order to avoid messing up the loop index.

  for ( i = iItemCount; i > 0; i-- )
    {
    // Step 3a: Get the text of the menu item into szMenuItem

    InitWCB  ( &wcb, TypeString, szMenuItem, sizeof(szMenuItem) );
    AddStringParam  ( &wcb, szMenu                      );  // Menu name
    AddShortParam   ( &wcb, (ushort)(iMenuID / 100)     );  // Type
    AddShortParam   ( &wcb, (ushort)i                   );  // Position
    WORDFUNCTION    ( wdMenuItemText$                   );
  
    if (!lstrcmpi(szMenuItem, szItemText))  // Found item to delete
      {
      // Step 3b: Get the name of the macro/function associated
      //          with this menu item into szName

      InitWCB  ( &wcb, TypeString, szName, sizeof(szName) );
      AddStringParam  ( &wcb, szMenu                      ); // Menu name
      AddShortParam   ( &wcb, (ushort)(iMenuID / 100)     ); // Type
      AddShortParam   ( &wcb, (ushort)i                   ); // Position
      WORDFUNCTION    ( wdMenuItemMacro$                  );

      // Step 3c: Delete the menu using the Tools...Customize->Menus
      //          Word dialog.
 
      InitWCB( &wcb, TypeShort, NULL, 0 );

      // Add Type
      AddShortDlgField ( &wcb, (ushort)(iMenuID / 100), fidMenuType, INPUT    );
      // Set the "Remove" option
      AddShortDlgField ( &wcb, 1,             fidRemove,   INPUT    );
      // Set the DocID as the context
      AddShortDlgField ( &wcb, (ushort)DocID, fidContext,  INPUT    ); 
      // Set the macro name
      AddStringDlgField( &wcb, szName,        fidName,     INPUT, 0 );
      // Set the menu ID
      AddStringDlgField( &wcb, szMenu,        fidMenu,     INPUT, 0 );
      // Call the dialog
      wdCommandDispatch( wdToolsCustomizeMenus, CommandAction,
                         wcb.cArgs, wcb.wdoprArgs, lpwdoprNil );

      break;  // Force the loop to stop
      }
    }
}

//*************************************************************************
//
//  MyTable: Inserts a table by presenting the user with a Word dialog
//           for the table insertion. After the table has been inserted,
//           values of the two fields in the dialog have been copied
//           to szCols and szRows. Our program can then convert those
//           strings to integers and use that information to put interesting
//           text in each table cell.
//
//*************************************************************************

void WINAPI _loadds MyTable( void )
{
    WCB     wcb;
    short   err; 
    short   cCol, cRow;
    short   nCols, nRows;
    static  uchar szCols[4], szRows[4];
    uchar   szBuffer[80]; 
     
    // Insert a table with the wdInsertTable command. This is done via
	// a word dialog. szCols and szRows will have the values of the
	// fields in this dialog
    InitWCB( &wcb, TypeShort, NULL, 0 );

	// By using OUTPUT, we are indicating that we want the
	// user to type in a value, and then we get those values
	// copied into these strings. If we had used INPUT, that
	// would mean that whatever was already in szCols and
	// szCols would be placed in the fields.
    AddStringDlgField( &wcb, szCols, fidNumColumns, OUTPUT, 4 );
    AddStringDlgField( &wcb, szRows, fidNumRows, OUTPUT, 4 );

	// Instantiate the dialog
	WORDDIALOG (wdTableInsertTable);
             
    // HourGlass Cursor and Turn screen updating OFF. The
	// CallCapi function is in CAPILIB.C; it wraps up the 
	// wdCommandDispatch ( CommandAction ) function call
	// nicely. The second to the last parameter indicates
	// the type of final paramter. For example, these calls
	// to CallCapi are adding an integer parameter, and then
	// calling the wdWairCursor or wdScreenUpdating function.
	// It's just yet another timesaver.
	CallCapi( &wcb, wdWaitCursor, TypeVoid, NULL, 0, "i", 1 );
	CallCapi( &wcb, wdScreenUpdating, TypeVoid, NULL, 0, "i", 0 );

    // Insert column and row information in each cell. Convert the
	// string field values into integers
    nCols = atoi( szCols );
    nRows = atoi( szRows );
    
    // Loop through the rows and colums, inserting text in each column
    for (cRow = 1; cRow <= nRows; cRow++)
      {
      for (cCol = 1; cCol <= nCols; cCol++)
        {
        // Insert Col and row number information in current cell, using
		// a string parameter.
        wsprintf( szBuffer, "Row %d, Col %d", cRow, cCol ); 
        err = CallCapi( &wcb, wdInsert, TypeVoid, NULL, 0, "s", szBuffer );
        
        // move IP to next cell (except at last cell)
        if ( cRow < nRows || cCol < nCols )
          {
          InitWCB( &wcb, TypeShort, NULL, 0 );
          err = wdCommandDispatch( wdNextCell, 0, wcb.cArgs, wcb.wdoprArgs, lpwdoprNil );   
          }
        }
      }
            
    // Move Insertion Point back to the start of the document
    InitWCB( &wcb, TypeShort, NULL, 0 );
    err = wdCommandDispatch( wdStartOfDocument, 0, wcb.cArgs, wcb.wdoprArgs, lpwdoprNil );

    // Turn screen updating back ON and restore cursor
	CallCapi( &wcb, wdWaitCursor, TypeVoid, NULL, 0, "i", 0 );
	CallCapi( &wcb, wdScreenUpdating, TypeVoid, NULL, 0, "i", 1 );
}
     

//*************************************************************************
//
//  StringArray: This function demonstrates using a string array in
// 	             Word. We will ask word for a list of all the running
//               apps, and we will cycle through a boatload of
//               messageboxes to show the poor user all the running apps.
//
//*************************************************************************

#define STRINGARRAYSIZE 15  // Max number of apps to show

void WINAPI _loadds StringArray( void )  
{
    WCB             wcb;
    short           err;
    HANDLE          hArrayDef;
    ARRAY_DEF far * ArrayDef;
    LPUCHAR         lpStrArray[STRINGARRAYSIZE];     //array of LPSTR to be passed in the call
    uchar           strArray[STRINGARRAYSIZE][64];   //buffers pointed by the array
    short           i;

    // Define the array to be STRINGARRAYSIZE elements. Word does the allocations,
	// and places the handle to the allocated array in hArrayDef. ArrayDef get the
	// array identifier that is used down below in GetAppNames.
    if(( ArrayDef = SetArrayDef( (HANDLE far *)&hArrayDef, 1, STRINGARRAYSIZE )) == NULL)
      {
      MessageBox( NULL, "", "Couldn't allocate Array_Def", MB_OK );
      return;
      }
        
    // Set up an array of LPSTR's to point to an array of strings (see above local
	// varaibles)
    for( i = 0; i < STRINGARRAYSIZE; i++ )
      {
      lpStrArray[i] = strArray[i];
      }
        
    // Call GetAppNames, using the array identifier, and pass in the array of
	// LPSTR's for the data.

    InitWCB( &wcb, TypeShort, NULL, 0 );
    AddStringArray( &wcb, ArrayDef, lpStrArray, 64 );

	WORDFUNCTION    ( wdAppGetNames );
    
    // Display the results
    for (i = 0; i < wcb.wdoprReturn.Short; i++)
      MessageBox( NULL, wcb.wdoprArgs[0].StringArray[i], "App Names", MB_OK );

    // Free memory allocated by Word

    GlobalUnlock( hArrayDef );
    GlobalFree( hArrayDef );   
}        
     
 
