WordClose.sas

/****************************************************************************/
/*  Program Name  : WordClose                                               */
/*  Purpose       : Macro utility to close Word application with option to  */
/*                  save.                                                   */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*  Author        : Matt Trzcinski                                          */
/*  Last Update   : 2017/05/06                                              */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*  Input(s)      : None                                                    */
/*  Output(s)     : May save file as DOC, DOT, TXT, RTF, DOCX, or PDF.      */
/*  Usage         : Takes argument 'save' which must be either 'yes' or     */
/*                  'no'. Default path is the current working directory.    */
/*                  Path may be quoted or unquoted. Default name is         */
/*                  'untitled-document'.  Default format is .rtf. If path   */
/*                  already contains a document of the same name and type   */
/*                  name, the file will be over-written without warning.    */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*  Example(s)    : Close and save:                                         */
/*                                                                          */
/*                      %WordClose(                                         */
/*                          save  = yes                                     */
/*                        , path  = c:\temp                                 */
/*                        , name  = testname                                */
/*                        , format= docx                                    */
/*                      );                                                  */
/*                                                                          */
/*                  Close without saving:                                   */
/*                                                                          */
/*                      %WordClose(save=no);                                */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*  Note(s)       : May turn off numlock. Sometimes process will hang.      */
/*                  If this happens try selecting the Word application.     */
/*                                                                          */
/*  Developer     : When possible, preference is given to DDE. DDE requires */
/*                  use of WordBasic commands. Documentation for the        */
/*                  commands used in this program are found at the end of   */
/*                  this program.  To download the WordBasic help file,     */
/*                  goto                                                    */
/*                                                                          */
/*                    http://word.mvps.org/Downloads/Wrdbasic.exe           */
/*                                                                          */
/*                  WordBasic does not handle .docx or .pdf.  It also has a */
/*                  problem saving an edited file in the same format as the */
/*                  original. For instance, suppose SAS creates an rtf and  */
/*                  edits it. If WordBasic is used to save the changes as   */
/*                  an rtf, then the changes are not saved.  Because of     */
/*                  this, all saving is handled by VBScript.                */
/*                                                                          */
/*                  It is not possible with DDE or VBScript to call a Save  */
/*                  As dialog. For details, see                             */
/*                                                                          */
/*                    http://stackoverflow.com/questions/4386124/           */
/*                    how-can-i-use-the-common-save-as-dialog-from-vbscript */
/*                                                                          */
/*                  It is possible to write a C# script and compile it      */
/*                  using Powershell.  However, doing so requires use of    */
/*                  the 'iex' command.  Calls to Powershell 'iex' generate  */
/*                  a FireEye HX ALERT security notification from CIT the   */
/*                  following day.                                          */
/*                                                                          */
/* TODO           : Investigate why SAS sometimes hangs until Word is       */
/*                  selected.  It may have something to do with the call to */
/*                  GetObject function or .ActiveDocument method in the     */
/*                  VBScript.                                               */
/*                                                                          */
/****************************************************************************/

%macro IsEmpty(macroVariable);
  %sysevalf(%superq(&macroVariable)=, boolean)
%mend;

%macro SetSystemOptions(opt1, opt2, opt3);
  options &opt1. &opt2. &opt3.;
%mend;

%macro WordClose(save=, path=, name=, format=) / minoperator mindelimiter = ',';

  %let macroStart     = %sysfunc(datetime());
  %let originalNOTES  = %sysfunc(getoption(notes));
  %let originalXWAIT  = %sysfunc(getoption(xwait));

  options nonotes noxwait;

  filename pwd '.';
  %let originalPWD = %sysfunc(pathname(pwd));
  filename pwd clear;

********************************************************************

** Verify Word is open

********************************************************************;
  filename wordDDE dde 'winword|system';

  %let linkConnection = %sysfunc(fopen(wordDDE, S));

  %if (&linkConnection. <= 0) %then %do;
    %put ERROR: [&SYSMACRONAME] Word not open. Failed to close Word.;
    %SetSystemOptions(&originalNotes., &originalXWAIT.);
    %abort cancel;
    %end;

  %let closeReturnCode = %sysfunc(fclose(&linkConnection));

********************************************************************

** Verify arguments

********************************************************************;
  %if       %upcase(&save.) = NO and not %IsEmpty(path) %then
    %put WARNING: [&SYSMACRONAME] SAVE argument is 'NO' but PATH argument is defined.;
  %else %if %upcase(&save.) = NO and not %IsEmpty(name) %then
    %put WARNING: [&SYSMACRONAME] SAVE argument is 'NO' but NAME argument is defined.;
  %else %if %upcase(&save.) = NO and not %IsEmpty(format) %then
    %put WARNING: [&SYSMACRONAME] SAVE argument is 'NO' but FORMAT argument is defined.;

  %if %IsEmpty(save) %then %do;
    %let save = NO;
    %put WARNING: [&SYSMACRONAME] SAVE is blank. Defaulting to SAVE=NO.;
    %end;

  %if not (%upcase(&save.) in (YES, NO)) %then %do;
    %put ERROR: [&SYSMACRONAME] Invalid argument SAVE.  Must be yes/no.;
    %SetSystemOptions(&originalNotes., &originalXWAIT.);
    %abort cancel;
    %end;

  %if %upcase(&save.) = YES %then %do;

    %if not %IsEmpty(path) %then %do;
      %let dirExists = %sysfunc(fileexist(&path));
      %if (&dirExists. = 0) %then %do;
        %put ERROR: [&SYSMACRONAME] Invalid path.;
        %SetSystemOptions(&originalNotes., &originalXWAIT.);
        %abort cancel;
        %end;
      %let path = %sysfunc(dequote(&path));
      %end;
    %else %if %IsEmpty(path) %then %do;
      %let path = &originalPWD;
      %SetSystemOptions(notes);
      %put NOTE: [&SYSMACRONAME] No PATH given. PATH set to "&path".;
      %SetSystemOptions(nonotes);
      %end;

    %if %IsEmpty(name) %then %do;
      %let name = untitled-document;
      %put WARNING: [&SYSMACRONAME] No NAME given. NAME set to "&name".;
      %end;

    %if %IsEmpty(format) %then %do;
      %let format = rtf;
      %put WARNING: [&SYSMACRONAME] No FORMAT given. FORMAT set to "&format".;
      %end;

    %if not(%upcase(&format.) in (DOC, DOCX, DOT, TXT, PDF, RTF)) %then %do;
      %put ERROR: [&SYSMACRONAME] Invalid format. Must be 'doc', 'docx', 'dot', 'txt', 'pdf', or 'rtf'.;
      %SetSystemOptions(&originalNotes., &originalXWAIT.);
      %abort cancel;
      %end;
    %end;

  %SetSystemOptions(notes);
  %put NOTE: [&SYSMACRONAME] Executing: WordClose(save=&save, path=&path, name=&name, format=&format);
  %SetSystemOptions(nonotes);

********************************************************************

** Close Word

********************************************************************;
  /*Shell command 'taskkill /F /IM WINWORD.EXE' may cause

    a 'not closed properly' type response upon Word reopen.*/
  %if %upcase(&save.) = NO %then %do;
    data _null_;
      file wordDDE;
      put '[FileExit 2]';
    run;
  %end;
  %else %do;

    %if       %upcase(&format.) = DOC  %then %let formatNumber = 0;
    %else %if %upcase(&format.) = DOCX %then %let formatNumber = 16;
    %else %if %upcase(&format.) = DOT  %then %let formatNumber = 1;
    %else %if %upcase(&format.) = PDF  %then %let formatNumber = 17;
    %else %if %upcase(&format.) = RTF  %then %let formatNumber = 6;
    %else %if %upcase(&format.) = TXT  %then %let formatNumber = 2;

    %let scriptFile = SaveDocumentAs%upcase(&format);
    %let dirTemp    = %sysfunc(getoption(WORK));

    data _null_;
      file "&dirTemp.\&scriptFile..vbs";
      put 'Set fso = CreateObject ("Scripting.FileSystemObject")';
      put 'Set stdout = fso.GetStandardStream (1)';
      put "stdout.WriteLine ""Saving document as &path.\&name..&format....""";
      put 'Dim oWord';
      put 'Dim oDoc';
      put 'Set oWord = GetObject(, "Word.Application")';
      put 'Set oDoc = oWord.ActiveDocument';
      put "oDoc.SaveAs ""&path.\&name..&format."", &formatNumber.";
      put 'oDoc.Close(False)';
      put 'oWord.Application.Quit(False)';
    run;

    /*Set pwd to dirTemp to avoid UNC errors*/
    %let cmd  = cd "&dirTemp";
    %syscall system(cmd);

    %sysexec(start /wait cscript "&dirTemp.\&scriptFile..vbs" //nologo);

    %let cmd  = cd "&originalPWD";
    %syscall system(cmd);
    %end;

********************************************************************

** Housekeeping

********************************************************************;
  filename wordDDE clear;
  options notes;

  %if %upcase(&save.) = YES %then %do;
    %let savedAs = &path.\&name..&format.;
    %let successMessage = Successfully closed Word. File saved as &savedAs..;
    %end;
  %else %let successMessage = Successfully closed Word. File not saved.;

  %put NOTE: [&SYSMACRONAME] &successMessage;
  %put NOTE: [&SYSMACRONAME] WORDCLOSE macro used (Total process time):;

  %let duration = %sysfunc(putn(%sysevalf(%sysfunc(datetime())-&macroStart.), time12.3));
  %if %sysfunc(minute("&duration."t)) > 0 %then %do;
    %put NO%str(TE-)         real time            %substr(&duration., 3, 8);
    %end;
  %else %do;
    %put NO%str(TE-)         real time            %substr(&duration., 6, 5) seconds;
    %end;

  %SetSystemOptions(&originalNotes., &originalXWAIT.);

%mend;

/********************************************************************

FileExit [Save]



Quits Word. FileExit (Windows) is the same as FileQuit (Macintosh).



Argument  Explanation

Save      Determines whether Word saves each document before closing

          it if it is "dirty" --- that is, if changes have been made

          since the last time the file was saved:

            0 (zero) or omitted    Prompts the user to save each

            changed document.

            1 Saves all edited documents before quitting.

            2 Quits without saving changed documents.

          The Save argument also controls whether a prompt appears if

          a document has a routing slip or if Word is printing in the

          background. Prompts appear if Save is 0 (zero) or omitted;

          otherwise, Word quits without routing documents or finishing

          print jobs.

********************************************************************/

/********************************************************************

https://msdn.microsoft.com/en-us/library/office/ff839952.aspx

Name                             Value  Description

wdFormatDocument                     0  Microsoft Office Word 97 - 2003 binary file format.

wdFormatDOSText                      4  Microsoft DOS text format.

wdFormatDOSTextLineBreaks            5  Microsoft DOS text with line breaks preserved.

wdFormatEncodedText                  7  Encoded text format.

wdFormatFilteredHTML                10  Filtered HTML format.

wdFormatFlatXML                     19  Open XML file format saved as a single XML file.

wdFormatFlatXML                     20  Open XML file format with macros enabled saved as a single XML file.

wdFormatFlatXMLTemplate             21  Open XML template format saved as a XML single file.

wdFormatFlatXMLTemplateMacroEnabled 22  Open XML template format with macros enabled saved as a single XML file.

wdFormatOpenDocumentText            23  OpenDocument Text format.

wdFormatHTML                         8  Standard HTML format.

wdFormatRTF                          6  Rich text format (RTF).

wdFormatStrictOpenXMLDocument       24  Strict Open XML document format.

wdFormatTemplate                     1  Word template format.

wdFormatText                         2  Microsoft Windows text format.

wdFormatTextLineBreaks               3  Windows text format with line breaks preserved.

wdFormatUnicodeText                  7  Unicode text format.

wdFormatWebArchive                   9  Web archive format.

wdFormatXML                         11  Extensible Markup Language (XML) format.

wdFormatDocument97                   0  Microsoft Word 97 document format.

wdFormatDocumentDefault             16  Word default document file format. For Word, this is the DOCX format.

wdFormatPDF                         17  PDF format.

wdFormatTemplate97                   1  Word 97 template format.

wdFormatXMLDocument                 12  XML document format.

wdFormatXMLDocumentMacroEnabled     13  XML document format with macros enabled.

wdFormatXMLTemplate                 14  XML template format.

wdFormatXMLTemplateMacroEnabled     15  XML template format with macros enabled.

wdFormatXPS                         18  XPS format.

********************************************************************/

Powered by peut-publier

©2020 Matt Trzcinski