Use Python to Inject Macros into Spreadsheets

John Smith picture John Smith · Jun 19, 2013 · Viewed 13.5k times · Source

I've got a macro that I'd like a bunch of existing spreadsheets to use. The only problem is that there are so many spreadsheets that it would be too time consuming to do it by hand!

I've written a Python script to access the needed files using pyWin32, but I can't seem to figure out a way to use it to add the macro in.

A similar question here gave this answer (it's not Python, but it looks like it still uses COM), but my COM object doesn't seem to have a member called VBProject:

Set objExcel = CreateObject("Excel.Application") 
objExcel.Visible = True 
objExcel.DisplayAlerts = False 
Set  objWorkbook = objExcel.Workbooks.Open("C:\scripts\test.xls") 
   Set xlmodule = objworkbook.VBProject.VBComponents.Add(1)  
   strCode = _ 
   "sub test()" & vbCr & _ 
   "   msgbox ""Inside the macro"" " & vbCr & _ 
   "end sub" 
   xlmodule.CodeModule.AddFromString strCode 
objWorkbook.SaveAs "c:\scripts\test.xls" 
objExcel.Quit 

EDIT: Link to the similar question referenced: Inject and execute Excel VBA code into spreadsheet received from external source

I also forgot to mention that although this isn't Python, I was hoping that similar object members would be available to me via the COM objects.

Answer

dilbert picture dilbert · Jun 23, 2013

This is the code converted. You can use either the win32com or comtypes packages.

import os
import sys

# Import System libraries
import glob
import random
import re

sys.coinit_flags = 0 # comtypes.COINIT_MULTITHREADED

# USE COMTYPES OR WIN32COM
#import comtypes
#from comtypes.client import CreateObject

# USE COMTYPES OR WIN32COM
import win32com
from win32com.client import Dispatch

scripts_dir = "C:\\scripts"
conv_scripts_dir = "C:\\converted_scripts"
strcode = \
'''
sub test()
   msgbox "Inside the macro"
end sub
'''

#com_instance = CreateObject("Excel.Application", dynamic = True) # USING COMTYPES
com_instance = Dispatch("Excel.Application") # USING WIN32COM
com_instance.Visible = True 
com_instance.DisplayAlerts = False 

for script_file in glob.glob(os.path.join(scripts_dir, "*.xls")):
    print "Processing: %s" % script_file
    (file_path, file_name) = os.path.split(script_file)
    objworkbook = com_instance.Workbooks.Open(script_file)
    xlmodule = objworkbook.VBProject.VBComponents.Add(1)
    xlmodule.CodeModule.AddFromString(strcode.strip())
    objworkbook.SaveAs(os.path.join(conv_scripts_dir, file_name))

com_instance.Quit()