Search notes:

VBScript MS-Office App Creator

This page describes a lightweight VBScript library that I routinely use to create Office applications (Access, Excel or Word documents with Visual Basic for Applications modules) from the command line (cmd.exe or PowerShell).
The source code is here (create-MS-Office-app.vbs) or on github.

Subs and Functions

compileApp(app) Compiles the project. The function returns true if successful and false otherwise. See this example for a possible usage.
createOfficeApp(prod, fileName) Creates an Office document (.docm, .xlsx, .xlsb or .accdb, depending on the value of prod and or extension of fileName). If the document alread exists, it is deleted.
openOfficeApp(prod, fileName) Opens an Office document. Unlike createOfficeApp(), no attempt is made to delete if it exists.
insertModule(app, moduleFilePath, moduleName, moduleType) Add a VBA module to the application. If module with same name already exists, it is replaced.
importVBAFile Imports a VBA file.
insertSheet(wb, sheetName, codeName) Adds a worksheet and assigns sheetName and codeName to the inserted sheet's name and code name, respectively. An exampe is here.
addReference(app, guid, major, minor) Add a reference to a type library aka typelib.
currentDir() Returns the path of the current directory with a trailing back slash (\)
replaceThisWorkbookModule(app, moduleFilePath) Set the content of an Excel's ThisWorkbook module
addFormWithModule Adds a form (msForms.userForm) and the corresponding VBA module, an example that demonstrates this functionality is here.

createOfficeApp

createOfficeApp(prod, fileName) creates an Office document or an Access database.
The first parameter (application) is a string that determines what is to be created. Its value must be one of word, excel or access.
The second parameter (fileName) controls the filename of the document/database that is created. If the file already exists, it is deleted.
set doc = createOfficeApp("word"  , "p:\ath\to\word\document.docm"   )
set xls = createOfficeApp("excel" , "p:\ath\to\excel\worksheet.xlsm" )
set acc = createOfficeApp("access", "p:\ath\to\access\database.accdb")

openOfficeApp

openOfficeApp(prod, fileName) opens an Office document for further manipulation.
The meaning of the parameters of openOfficeApp(prod, fileName) are the same as those of createOfficeApp(prod, filename).
Currently (version 0.03), only prod = excel is implemented.
set xls = openOfficeApp("excel"  , "p:\ath\to\excel\worksheet.xlsm")

insertModule

insertModule(app, moduleFilePath, moduleName, moduleType) inserts a VBA module.
app must be an application object
For Word and Excel documents, the required application object is obtained by using the .application property on the object that is returned by createOfficeApp(…).
moduleFilePath is a string that locates the file path of the module that is to be inserted.
moduleName is the name that will be assigned to the inserted module. If there is already a module with this name, it will be replaced.
moduleType is an integer that describes what type of module is to be inserted. The possible values are 1 and 2. They correspond to the constants vbext_ct_StdModule and vbext_ct_ClassModule.
An example that demonstrate how insertModule() replaces a module is here.

addReference

addReference(app, guid, major, minor) adds a reference to a type library aka typelib. (See for example adding a reference to a library programmatically).
app has the same meaning as in insertModule(…)
guid is the GUID the uniquely identifies the type library whose reference is to be added.
major and minor specify the type library's version.

currentDir

currentDir() returns the current directory with a trailing backslash (\) so that it can be easily concatenated with the relative path names of files, especially in conjunction with inserting modules (insertModule())

replaceThisWorkbookModule

replaceThisWorksheetModuleModule(app, moduleFilePath) is quite similar to insertModule() but can only be used for an Excel application in order to insert a ThisWorksheet module.
See this example that demonstrates this function.

Examples

The three simple examples for Access, Excel and Word are very similar: They create the respective Office document and insert a module, named functions.bas which has one sub, named main which calls msgBox() to demonstrate that the sub was called.
When done with the creation of the document, this main() sub is called.
Generally, these examples need to be executed on the command line with
P:\ath\to\example\dir> cscript create.wsf
This example demonstrates how an object can be passed to the VBA that is running in the application in order to receive messages.
For Excel, there are also these additional examples:

Source code

This is the source code of create-MS-Office-app.vbs:
'
' Provide the functionality to create Excel, Access or Word applications
' from the command line.
'
' The functions in this file should be called from a *.wsf file.
'
' Version 0.13
'
' See also https://renenyffenegger.ch/notes/Microsoft/Office/VBScript-App-Creator/
'

option explicit

dim fso
set fso = createObject("scripting.FileSystemObject")

function createOfficeApp(prod, fileName) ' {
 '
 '  Note: when creating an Access database, this function
 '  returns an application object. When creating an excel
 '  Worksheet, it returns an Excel Worksheet.
 '

    if fso.fileExists(fileName) then ' {
       on error resume next
       fso.deleteFile(fileName)

       if err.number = 70 then ' Permission denied {
          wscript.echo fileName & " could not be deleted (probably because it is in use)"
          set createOfficeApp = nothing
          exit function
       end if ' }
       on error goto 0

       wscript.echo fileName & " was deleted because it already existed"
    end if ' }

    dim fileSuffix, fileFormat
    fileSuffix = right(fileName, 5)

    dim app

    if     prod = "access" then ' {

           set createOfficeApp = createObject("access.application")
           createOfficeApp.newCurrentDatabase fileName, 0 ' 0: acNewDatabaseFormatUserDefault

           set app = createOfficeApp
    ' }
    elseIf prod = "excel"  then ' {

           set app             = createObject("excel.application")
         '
         ' createOfficeApp becomes a worksheet here, really...
         '
           set createOfficeApp = app.workBooks.add

         '
         ' Determine file format value based on extension of filename
         '
           if     fileSuffix = ".xlsb" then ' {
                  fileFormat = 50    ' xlExcel12

           elseif fileSuffix = ".xlsm" then
                  fileFormat = 52    ' xlOpenXMLWorkbookMacroEnabled

           else
                  wscript.echo fileName & " has  suffix that is not (yet?) supported"
                  set createOfficeApp = nothing
                  exit function
           end if ' }

           createOfficeApp.saveAs fileName, fileFormat
    ' }
    elseIf prod = "word"   then ' {
           set app             = createObject("word.application")

           set createOfficeApp = app.documents.add

         '
         ' Determine file format value based on extension of filename
         '
           if     fileSuffix = ".docm" then ' {
'                 fileFormat = 20    ' wdFormatFlatXMLMacroEnabled
                  fileFormat = 13    ' wdFormatXMLDocumentMacroEnabled

           elseif fileSuffix = ".dotm" then
'                 fileFormat = 22    ' wdFormatFlatXMLTemplateMacroEnabled
                  fileFormat = 15    ' wdFormatXMLTemplateMacroEnabled
           else

                  wscript.echo fileName & " has  suffix that is not (yet?) supported"
                  set createOfficeApp = nothing
                  exit function
           end if ' }


        '
        '  Note: saveAs2, not saveAs.
        '
           createOfficeApp.saveAs2 fileName, fileFormat

    end if ' }

    app.visible     = true

    if prod <> "word" then ' {
  '
  ' Keep application opened after scripts terminates
  '   https://stackoverflow.com/q/36282024/180275
  '
  ' In Word, userControl is read only and set to true if
  ' the application was created with createObject(), getObject() or opened
  ' with open()
  '
      app.userControl = true
    end if ' }

  '
  ' Add (type lib) reference to "Microsoft Visual Basic for Applications Extensibility 5.3"
  '
  '      2020-07-13: TODO: is this reference always present in Word documents?
  '
    call addReference(app, "{0002E157-0000-0000-C000-000000000046}", 5, 3)

end function ' }

function openOfficeApp(prod, fileName) ' {

    dim app
    if prod = "excel" then ' {
       set app = createObject("excel.application")
       dim updateLinks : updateLinks = false
       set openOfficeApp = app.workBooks.open(fileName, updateLinks)
    else
       wscript.echo("Todo. implement " & prod & " for openOfficeApp")
       set openOfficeApp = nothing
       exit function
    end if ' }

end function ' }

function vb_components(app) ' {

    dim vb_editor ' as vbe
    dim vb_proj   ' as VBProject
    dim vb_comps  ' as VBComponents

    if app.name = "Microsoft Word" then
       set vb_proj   = app.activeDocument.vbProject
    else

       on error resume next
       set vb_editor = app.vbe

       if err.number <> 0 then ' {
          if err.number = 1004 then
             wscript.echo("Unable to get reference of app.vbe: probably because macros are disabled")
             wscript.quit(-1)
          end if
          wscript.echo("Unexpected error when trying to get app.vba: " & err.number & " - " & err.description)
          wscript.quit(-1)
       end if ' }

       on error goto 0

       set vb_proj   = vb_editor.activeVBProject
    end if

    set vb_components = vb_proj.vbComponents

end function ' }

sub insertModule(app, moduleFilePath, moduleName, moduleType) ' {
 '
 '  moduleType:
 '    1 = vbext_ct_StdModule
 '    2 = vbext_ct_ClassModule
 '
 '  See also https://renenyffenegger.ch/notes/development/languages/VBA/modules/Common/00_ModuleLoader
 '
    if not fso.fileExists(moduleFilePath) then ' {
       wscript.echo moduleFilePath & " does not exist!"
       wscript.quit
    end if ' }

    dim vb_comps  ' as VBComponents
    set vb_comps  = vb_components(app)

  '
  ' Check if a module by the given name already exists.
  ' If so, remove it.
  '
  ' If no module with the name moduleName exists, by default
  ' vb_comps(moduleName) throws a 'VBAProject: Subscript out of range'
  ' error.
  ' We're going to let such an error escape by embedding the
  ' statement between the following two 'on error …' statements:
  '
    on error resume next
    dim comp      ' as VBComponent
    set comp = vb_comps(moduleName)
    on error goto 0

    dim mdl       ' as codeModule

    if not isEmpty(comp) then

       set mdl = comp.codeModule
       dim nofLines
       nofLines = mdl.countOfLines
       mdl.deleteLines 1, nofLines

    else

       set comp = vb_comps.add(moduleType)
       set mdl  = comp.codeModule

    end if

 '
 '  2021-06-04  V0.7  An absolute path is required when
 '                    calling addFromFile()
 '
    mdl.addFromFile fso.getAbsolutePathName(moduleFilePath)
    on error resume next
    comp.name = moduleName
    if err.number <> 0 then ' {
       wscript.echo moduleName & " cannot be used as a module name!"
       wscript.quit -1
    end if ' }
    on error goto 0

    if app.name = "Microsoft Access" then
       app.doCmd.close 5, comp.name, 1 ' 5=acModule, 1=acSaveYes
    end if

end sub ' }

sub importVBAFile(app, filename) ' {
    vb_components(app).import filename
end sub ' }

sub addFormWithModule(app, formName, modulePath) ' {
 '
 ' 2021-08-23: is this sub still used.
 ' wb is an unreferenced variable in here.
 '

   dim frm ' as VBIDE.vbComponent
   set frm = wb.vbProject.VBComponents.add(3) ' 3 = vbext_ct_msForm

   frm.properties.item("name").value = formName

 ' In spite of the 1, this is actually a 'form module'
   insertModule app, modulePath, formName, 1

end sub ' }

sub addReference(app, guid, major, minor) ' {
  '
  ' guid identfies a type lib. Thus, the guid should be found in the
  ' Registry under HKEY_CLASSES_ROOT\TypeLib\
  '
  ' Note: guid probably needs the opening and closing curly paranthesis.
  '
    dim ref
    for each ref in app.VBE.activeVbProject.references
        if ref.guid = guid then
           wscript.echo "guid " & guid & " (" & ref.description & ") was already added"
           exit sub
        end if
    next

    call app.VBE.activeVbProject.references.addFromGuid (guid, major, minor)
end sub ' }

function currentDir() ' {
     dim wshShell
     set wshShell = createObject("WScript.Shell")

     currentDir = wshShell.CurrentDirectory & "\"

end function ' }

sub replaceThisWorkbookModule(app, moduleFilePath) ' {
 '
 '  Set the content of an Excel's ThisWorksheet module
 '
    insertModule app, moduleFilePath, "thisWorkbook", 1

'
'   Old Code as of version 0.6:
'
'     if not fso.fileExists(moduleFilePath) then ' {
'        wscript.echo moduleFilePath & " does not exist!"
'        wscript.quit
'     end if ' }
'
'     dim mdl
'     set mdl = app.vbe.activeVBProject.vbComponents.item(1).codeModule
'     call mdl.addFromFile (moduleFilePath)

end sub ' }

function insertSheet(wb, name, codeName) ' {

    set insertSheet  = wb.sheets.add
    insertSheet.name = name
    wb.vbProject.vbComponents(insertSheet.codeName).name = codeName

end function ' }

function compileApp(app) ' {

   dim cmdBar
   set cmdBar = app.VBE.commandBars
   dim compile
   set compile = cmdBar.findControl(1, 578 ) ' 1 = msoControlButton. 578: the ID of the control

'  on error resume next
   if compile.enabled then
      compile.execute
   end if

   if compile.enabled then
    '
    ' If compilation was successful, the respective
    ' button/control is greyed out (or disabled).
    ' The fact that the button is still enabled tells us
    ' that the compilation had an error.
    ' In this case, a message box is open in the
    ' VB Project window with the error
    ' description.
    '
      compileApp = false
   else
      compileApp = true
   end if

end function ' }
Github repository VBS-MS-Office-App-Creator, path: /create-MS-Office-app.vbs

History / Versions

0.1 Initial version
0.2 Excel: Determine file format value based on extension of filename (which allows to save .xlsm and .xlsb files). This change was needed to create a Personal.xlsb Workbook (2020-06-19).
0.3 Add openOfficeApp() (2020-06-27)
0.4 Improve comments (2020-07-13)
0.5 Add parameter update = false when opening Excel workbooks.
0.6 Don't remove existing module, delete their contents and reload from file. This was needed to write into Sheet… VBA modules
0.7 Turn relative into absolute paths in insertModule (2021-06-04)
0.8 Rename replaceThisWorksheetModule to replaceThisWorkbookModule (2021-06-14)
0.9 Add function compileApp (2021-06-16). See this example.
0.10 Add function insertSheet (2021-06-25). See this example.
0.11 Cosmetic change (dim comp before the variable is used).
0.12 Function addFormWithModule, an example that demonstrates this functionality is here.
0.13 Add function importVBAFile

See also

Creating an MS-Access database with ADOX and VBScript
https://github.com/ReneNyffenegger/runVBAFilesInOffice

Index