Applications Development

Paper 21-25

Using SAS® Software and for Applications to Automate Tasks in Word: An Alternative to Dynamic Data Exchange Mark Stetz, Amgen, Inc., Thousand Oaks, CA

ABSTRACT DDE AND THE HARRIS DESIGN Using Dynamic Data Exchange (DDE) to drive Microsoft While DDE implementation is simple, the mixture of SAS Office applications such as Word and Excel has long and WordBasic syntax can result in confusing, difficult to been a technique utilized by SAS programmers. While maintain code. The following shows a simple DATA step alternatives exist (e.g., generating RTF code, OLE for populating a Word document using DDE and Automation), the use of DDE in conjunction with the native WordBasic (assuming Word is currently running): scripting languages of these applications has been appealing since implementation is simple and the macro filename cmds dde 'winword|system' ; recording capabilities of Office applications make script generation nearly automatic. Harris (SUGI 24 data _null_ ; Proceedings, 1999) described a particularly elegant file cmds ; design for using the SAS System and DDE to populate Microsoft Word documents. put '[Insert "Hello World"]' ; put '[FileSaveAs ' Now that the more sophisticated Visual Basic for '.Name = "c:\My Documents\Hello", ' Applications (VBA) is the common development '.Format = 0, ' environment among all Office applications, however, DDE '.AddToMru=0]' ; only works with Office application legacy macro put '[FileClose]' ; languages. Employing Harris’ design, this paper run ; describes a technique to simulate the ease of use of the DDE methodology while taking full advantage of VBA to Harris suggested a design to simplify the coding of such automate Microsoft Word. DATA steps by wrapping WordBasic commands in intuitively named SAS macros. For example, the INTRODUCTION WordBasic Insert command would be encapsulated as DDE is a feature of that allows two follows: programs to share data or send commands directly to each other. SAS programmers have long taken %macro puttext(text) ; advantage of DDE with Microsoft Office applications to put "[Insert ""&text""]"; %mend ; perform such tasks as generating Word documents or Excel spreadsheets from within their SAS programs. Assuming macros exist that wrap other WordBasic commands similarly, the above DATA step could be re- Among the advantages of using DDE is its straightforward coded as follows: implementation. SAS programs simply use PUT statements within a DATA step to send scripting filename cmds dde 'winword|system' ; commands to an Office application. In addition, the macro recording capabilities of Office applications make data _null_ ; generating script effortless. file cmds ;

Prior to Office 97, Office applications such as Word and %puttext(Hello World) Excel each utilized their own individual scripting %saveas(c:\My Documents\Hello) languages to automate tasks within their respective %fileclos programs. Beginning with Office 97, however, the run ; programming environment was standardized such that all Office applications use the VBA development Harris provides examples of several such macros which environment. can be grouped into libraries and customized according to a user’s specific requirements. Among the advantages of Unlike the previous scripting languages which simply send incorporating this design are code re-use and complexity keystrokes or command strings to an application, VBA hiding (users of the libraries aren't required to know works by directly controlling Office applications, hooking WordBasic). In addition, both the libraries and programs into objects within the applications. This increased level utilizing the libraries are significantly easier to develop and of sophistication makes VBA incompatible with DDE. maintain.

This paper presents a technique for using VBA to CONVERSION TO VBA automate tasks in Microsoft Word. The technique is The conversion of WordBasic commands to VBA in many similar enough to the DDE methodology to rival its ease of cases is a relatively minor issue (assuming some use, yet provides additional capabilities not possible with knowledge of VBA). Word Help provides a mapping of DDE. Implementing the design outlined by Harris further WordBasic commands to VBA and macro recording simplifies and optimizes this approach. provides another resource. For example, the VBA Applications Development

equivalent for the macro PUTTEXT from above is: Parameter Default Description VISIBLE= Y N= Run Word without a %macro puttext(text) ; Graphical User Interface put 'Selection.TypeText ' (GUI) (this can speed up "Text:=""&text""" ; execution) %mend puttext ; [Note: There will be NO visible indication (e.g., a The important distinction is that the above VBA statement, button in the Toolbar) that unlike its WordBasic counterpart, is not executable on its Word is active or when own. It is only valid in the context of a VBA procedure. the macro has completed. This distinction precludes the use of DDE to send VBA To have the GUI restored statements to Word. when the macro has completed, set THE RUN_VBA DELIVERY MECHANISM NOTIFY=Y and Because VBA statements cannot be delivered to Word EXITWORD=N.] using DDE, an alternative delivery mechanism is required. MINIMIZE= N Y=Minimize Word while The new delivery mechanism employs a SAS macro and the macro executes (this a Word macro specifically designed to work together to can speed up execution) read in and execute a given Word macro stored in an NOTIFY= N Y=Have Word display a external (text) file. notification dialog box THE RUN_VBA SAS MACRO when the macro has completed running The RUN_VBA SAS macro (shown in Appendix 1) serves EXITWORD= N Y=Exit (close) Word upon as the SAS component of the delivery mechanism for any completion of the macro program using VBA. The macro provides an interface to execution (otherwise Word through the use of a Word command line startup Word remains open) switch and Windows environment variables. DETAILS The Word command line startup switch /mmacro-name THE RUN_VBA WORD MACRO instructs Word to execute the specified macro-name The Run_VBA Word macro (shown in Appendix 2) is the when Word is started. In the case of the delivery Word component of the delivery mechanism. The macro mechanism the macro-name is the Run_VBA Word is permanently stored in the Normal template and macro described later. designed to execute at Word startup as described previously. The Run_VBA Word macro uses the Because Word macros cannot have input arguments, the environment variables created by the RUN_VBA SAS delivery mechanism makes use of Windows environment macro to read in, execute, and delete a Word VBA macro variables to pass information to the Run_VBA Word generated in a SAS DATA step. macro. The RUN_VBA SAS macro allows the user to specify the values of these variables through its Other than the initial installation and set-up (described in parameters. the next section), the user has no direct interaction with the Run_VBA Word macro (although indirect interaction The RUN_VBA SAS macro simply generates and occurs through the RUN_VBA SAS macro). executes a Windows batch file. The batch file consists of commands to create and set the values of several MICROSOFT WORD SET-UP Windows environment variables (based on the values of The following steps describe how to create the Run_VBA the macro parameters), invoke Word (and thereby invoke macro in Microsoft Word and perform some additional the Run_VBA Word macro), and finally, delete itself. set-up: 1. Under the Tools menu select Macro and then RUN_VBA SAS MACRO PARAMETERS Visual Basic Editor (VBE) The RUN_VBA SAS macro has the following parameters: 2. Make sure the Project Explorer and Properties windows are visible (these are usually located Parameter Default Description along the left side of the VBE). If not, under the WORDPATH= None The MS-DOS directory View menu select Project Explorer and then path of the Word Properties Window. executable 3. In the Project Explorer window (usually located VBA_PATH= None The Windows directory on the upper left side of the VBE), make sure path of the file containing Normal is currently selected. The Project the VBA macro Explorer window title bar should display “Project VBA_PGM= None The name of the file (sans – Normal”. extension) containing the 4. Under the Insert menu select Module (Module1 VBA macro (Note: A .bas should have been added to the tree in the Project extension is assumed.) window) 5. Enter the code shown in Appendix 2 6. In the Properties window (usually located on the Applications Development

lower left side of the VBE, below the Project The DATA step presented earlier is modified as follows: Explorer window), click in the text field labeled “(Name)” and change “Module1” to “Run_VBA” %vba_lib (do not include the quotes). Press the Enter key so that the new value is reflected in the Project %let wordpath= Explorer window. c:\progra~1\micros~1\office ; 7. Under the Tools menu select References… %let path=c:\My Documents ; 8. In the list box, check the box next to “Microsoft %let program=Hello ; Visual Basic for Applications Extensibility” data _null_ ; (you will need to a scroll down to find that file "&path\&program..bas" ; selection) 9. Click the OK button to close the References put "Sub &program()" ; dialog box %puttext(Hello World) 10. Under the File menu, select Save Normal to %saveas(&path\&program) save the changes %fileclos 11. Exit the VBE put 'End Sub' ; run ; The Run_VBA Word macro creates a temporary module in the Normal template that is automatically deleted under %run_vba(wordpath=&wordpath, normal circumstances. If, however, an error occurs, the vba_path=&path, temporary module may not be deleted and will cause vba_pgm=&program, further errors until it has been deleted. Therefore, it is visible=Y, strongly recommended that Word be set-up to prompt for minimize=Y, saving changes to the Normal template. Having Word notify=Y, prompt to save changes (and selecting No) will prevent exitword=Y) ; the temporary module from being unintentionally saved in the Normal template. (Note: If you elect to follow this As the example demonstrates, the main differences from recommendation Word will always prompt to save the DDE methodology are: changes to the Normal template whether your program • The FILE statement now specifies an external text runs correctly or not since some change occurs. In either file (Note: A .bas extension is required.) case you should always select No when prompted.) • VBA Sub and End Sub statements are required to define a Word macro (Note: The Word macro To set-up Word to prompt for saving changes to the name must be the same as the filename.) Normal template do the following: • The RUN_VBA SAS macro must be invoked to 1. Under the Tools menu select Options… execute the Word macro created in the DATA step 2. Select the Save tab (Note: Macro variables are used to make the code more 3. Select the checkbox labeled “Prompt to save maintainable.) Normal template” HOW IT WORKS The following outlines the key events when the example is EXAMPLE executed: The following shows a simple VBA macro library based on • The DATA step generates a Word VBA macro the example DATA step shown previously: written to a text file • The RUN_VBA SAS macro generates and %macro vba_lib ; executes a Windows batch file that: Ø sets Windows environment variables based on %macro puttext(text) ; the macro parameter values specified put 'Selection.TypeText ' Ø invokes Word using the /mRun_VBA startup "Text:=""&text""" ; switch %mend puttext ; • The Run_VBA Word macro: %macro saveas(fname, Ø reads the values of the environment variables set format=wdFormatDocument) ; in the batch file put 'ActiveDocument.SaveAs ' Ø minimizes Word (minimize=Y) "FileName:=""&fname"", " Ø creates a new module in the Normal template "FileFormat:=&format" ; Ø reads in the text file containing the VBA macro %mend saveas ; generated in the SAS DATA step Ø executes the VBA macro %macro fileclos ; Ø deletes the new module from the Normal put 'ActiveDocument.Close ' template 'SaveChanges:=wdDoNotSaveChanges' ; Ø %mend fileclos ; deletes the text file containing the VBA macro Ø notifies the user via a dialog box that the macro %mend vba_lib ; has completed (notify=Y) Ø closes Word after the user acknowledges the dialog box (exitword=Y) Applications Development

ADVANTAGES OF THE VBA TECHNIQUE Finally, the VBA technique is more easily extendible to The VBA technique described in this paper offers several other Microsoft Office applications as well as to many important advantages over DDE and WordBasic. other non-Office applications that support VBA. Having Foremost, VBA is the scripting/programming language for each of the applications "speak the same language" Office applications. While backward compatibility with considerably simplifies the task of the developer in WordBasic exists, it comes at the expense of efficiency building and maintaining the libraries described by Harris. since Word must convert each WordBasic command to run under VBA. Using DDE also incurs additional CONCLUSION overhead since each statement must be interpreted The use of DDE, particularly in conjunction with the design individually as opposed to the VBA technique which described by Harris, provides a simple, powerful, and executes a single compiled macro. maintainable method of taking advantage of the scripting capabilities of Microsoft Office applications. The The VBA technique, while providing the complexity hiding emergence of VBA as the programming environment for inherent in the Harris design, also allows the use of the Office, however, provides new opportunities in extending entire programming language including control statements and enhancing these capabilities. The technique such as For…Next, Do, and If…Then…ElseIf. Therefore, described in this paper attempts to maintain the simplicity experienced VBA programmers can take advantage of of using DDE while opening the new doors provided by this capability to build even more efficient Word macros. VBA. Consider the following DDE example: TRADEMARKS filename cmds dde 'winword|system' ; SAS and all other SAS Institute, Inc. product or service names are registered trademarks or trademarks of SAS data _null_ ; Institute Inc. in the USA and other countries. ® indicates file cmds ; USA registration. do i=1 to 1000 ; %puttext(Hello World) Other brand and product names are registered end ; trademarks or trademarks of their respective companies. %saveas(c:\My Documents\Hello) %fileclos REFERENCES run ; Harris, Michael (1999), "Using the SAS System and Dynamic Data Exchange to Populate Microsoft Word While a similar VBA version is possible, the following Documents with Text, Tables, and Graphs", Proceedings takes advantage of a VBA looping construct to produce a of the Twenty-Fourth Annual SAS Users Group significantly more efficient Word macro—and SAS International Conference, 24, 156-160. program (Note: This example assumes the existence of the macro variables as defined in the previous VBA CONTACT INFORMATION example.): Comments and questions are welcomed. Contact the author at: data _null_ ; file "&path\&program..bas" ; Mark Stetz put "Sub &program()" ; Amgen, Inc. put 'Dim i as Integer' ; One Amgen Center Drive, MS 17-2-B put'Fori=1To1000' ; Thousand Oaks, CA 91320-1789 %puttext(Hello World) put 'Next i' ; [email protected] %saveas(&path\&program) %fileclos The source code used in this paper can be obtained at: put 'End Sub' ; run ; http://home.earthlink.net/~equizole/ Another advantage of the VBA technique is that each application (the SAS System and Word) execute their functions independently. The SAS System generates the VBA macro and invokes the RUN_VBA macro (which in turn generates and executes the Windows batch file). Word is started as an independent process (by the batch file) and executes the macro generated by the SAS System. While Word executes the macro the SAS System is free to begin the sequence again in which case a new instance of Word is invoked (regardless of whether or not the previous instance of Word is still running). This allows for great flexibility in batch processing and takes advantage of multi-tasking not possible using DDE. Applications Development

APPENDIX 1: RUN_VBA.SAS

%macro run_vba(wordpath=, vba_path=, vba_pgm=, visible=Y, minimize=N, notify=N, exitword=N) ;

options noxwait ;

/*------* | Generate a batch file to set environment variables and invoke Word using | | the /m option to run the Run_VBA Word macro. The Run_VBA Word macro uses | | the environment variables' values to read in and execute the VBA macro | | source code file(s) generated by the calling SAS program. | *------*/ data _null_ ; file "&vba_path\&vba_pgm..bat" ;

put "set VBA_PATH=&vba_path" ; put "set VBA_PGM=&vba_pgm" ; put "set VISIBLE=&visible" ; put "set MINIMIZE=&minimize" ; put "set NOTIFY=¬ify" ; put "set EXITWORD=&exitword" ;

put "start &wordpath\winword.exe /mRun_VBA" ;

put "del ""&vba_path\&vba_pgm..bat"" " ; run ;

/*------* | Run the batch file created in the previous DATA step | *------*/ data _null_ ; call system("""&vba_path\&vba_pgm..bat""") ; run ;

%mend run_vba ; Applications Development

APPENDIX 2: RUN_VBA.BAS

Sub Run_VBA()

'*** Declare local variables *** Dim vbcs As VBComponents Dim vbc As VBComponent Dim vba_num As Long Dim vba_filename As String

Dim vba_path As String Dim vba_pgm As String Dim minimize As String * 1 Dim visible As String * 1 Dim notify As String * 1 Dim exitword As String * 1

'*** Read environment variables (created in RUN_VBA.SAS) *** vba_path = Environ("VBA_PATH") vba_pgm = Environ("VBA_PGM") minimize = UCase(Environ("MINIMIZE")) visible = UCase(Environ("VISIBLE")) notify = UCase(Environ("NOTIFY")) exitword = UCase(Environ("EXITWORD"))

'*** Set the Word application window state *** If visible <> "Y" Then Application.visible = False If notify <> "Y" Then exitword = "Y" ElseIf minimize = "Y" Then Application.WindowState = wdWindowStateMinimize End If

'*** Create a new Word document *** Documents.Add Documents(1).Activate

'*** Get a reference to the Normal template component collection *** Set vbcs = VBE.VBProjects("Normal").VBComponents

'*** Construct the filename of the SAS generated VBA macro *** vba_filename = vba_path & "\" & vba_pgm & ".bas"

'*** Add a new code module to the Normal template *** Set vbc = vbcs.Add(vbext_ct_StdModule)

'*** Read in the SAS generated VBA macro *** vbc.CodeModule.AddFromFile FileName:=vba_filename

'*** Run the SAS generated VBA macro *** Application.Run MacroName:=vba_pgm

'*** Delete the code module from the Normal template *** vbcs.Remove VBComponent:=vbc

'*** Delete the file containing the SAS generated VBA macro *** Kill vba_filename

'*** Notify the user that program has completed *** If notify = "Y" Then Application.visible = True MsgBox vba_pgm & " completed." End If

'*** Exit Word *** If exitword = "Y" Then Application.Quit SaveChanges:=wdDoNotSaveChanges End If

End Sub