Need skeleton code to call Excel VBA from PythonWin - vba

I need to invoke a VBA macro within an Excel workbook from a python script. Someone else has provided the Excel workbook with the macro. The macro grabs updated values from an external database, and performs some fairly complex massaging of the data. I need the results from this massaging, and I don't really want to duplicate this in my Python script, if I can avoid it. So, it would be great if I could just invoke the macro from my script, and grab the massaged results.
Everything I know about COM I learned from "Python Programming on Win32". Good book, but not enough for my task at hand. I searched, but haven't found any good examples on how to do this. Does anyone have any good examples, or perhaps some skeleton code of how to address/invoke the VBA macro? A general reference (book, web link, etc) on the Excel COM interfaces would also help here. Thanks.

OK, I got it! Thanks for the help on the Application.Run method. This info, plus the "Microsoft Excel Visual Basic Reference": http://msdn.microsoft.com/en-us/library/aa209782(office.10).aspx--as recommended by Hammond & Robinson in "Python Programming on Win32"--was what was needed.
Here's the skeleton code:
import win32com.client
xl=win32com.client.Dispatch("Excel.Application")
xl.Workbooks.Open(Filename="<your Excel File>",ReadOnly=1)
xl.Application.Run("<your macro name>")
#...access spreadsheet data...
xl.Workbooks(1).Close(SaveChanges=0)
xl.Application.Quit()
xl=0

I am sorry, I dont know python enough.
However, the following should help.
Excel's Application object has a Run method - which takes the name of the macro alongwith arguments to it.
Lets assume that the workbook has a macro named test.
Sub test(ByVal i As Integer)
MsgBox "hello world " & i
End Sub
You can call this using Application.Run "test", 1234
This will call the macro and show the messagebox with "hello world 1234".

Related

Import source from txt or bas file

I want to store source code of VBA macro in .bas or .txt file and run it when user runs macro. I have macro that is used by multiple people, and I would like to store file on server to prevent them to use older versions of same macro. I found following line in stackoverflow and placed it in module code that should import code
'Library should be turned on Microsoft Visual Basic for Applications Extensibility 5.3
Option Explicit
Sub main()
Dim VBPrj As VBIDE.VBProject
Dim VBCom As VBIDE.VBComponent
Set VBPrj = Application.VBE.ActiveVBProject
Set VBCom = VBPrj.VBComponents("Module1")
VBCom.CodeModule.AddFromFile ("C:\Users\lietu\OneDrive\Documents\tests\Module1.txt")
End Sub
then I created txt file with following code in right location
Attribute VB_Name = "Module1"
Sub main()
MsgBox "Hello World"
End Sub
What I'm doing wrong?
It is not possible to execute code in a text file as a VBA "macro". The programming language/environment/interface simply does not work that way - no ifs, ands or buts - no workarounds.
The content of a text or bas file must be imported into a VBA project, using code similar to what's in the question.
In order to be able to use the VB Extensibility libraries that this code depends on, a specific security setting in the host Office application must be disabled, making this kind of approach unreliable, at best. The setting cannot be disabled using code, for understandable reasons.
Indeed, the approach proposed in the question would be a massive security risk...

Running PowerPoint macro from PowerShell

I have been experiencing troubles when running a PowerPoint Macro from Powershell (running Excel macros already works). As there is not much info on the web regarding this topic, I'm willing that you will be able to help me.
I'm trying to automate reporting in my work area and the flow is to query data from MS SQL Server, import data into Excel file, refresh charts and then build the final report with PowerPoint slides.
PowerPoint file's charts' are linked with the Excel (via File -> Edit Links to Files) and after selecting the specific chart, Chart Tools menu appears where on the Design tab I just click "Refresh Data" and the chart refreshes. I already have a macro which refreshes data automatically for me. Now I want to call that macro from Powershell script. Please see my code below:
$ppt = New-Object -ComObject Powerpoint.Application
$FilePath = '\\c03d03\public\Data\uvall\IBNB\OnePager.pptm'
$presentation = $ppt.presentations.Open($FilePath)
$ppt.Visible = $true
$app = $ppt.Application
$tst = #('test')
$presentation.Application.Run("RefreshData",[ref]$tst) / 1st way
$app.Run("RefreshData", [ref]#()) / 2nd way
$app.Run("RefreshData", #()) / 3rd way
$app.Run("RefreshData") / 4th way
$presentation.save()
$presentation.close()
$ppt.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ppt)
And none of these 4 ways of calling a macro works. Sometimes it just returns an error that the "Sub or function not defined", even if it's truly defined, sometimes it returns "You cannot call a method on a null-valued expression" and other errors.
Related:
Run PowerPoint Macro from PowerShell
Automate Powerpoint Macro
Please also be aware that I'm using Office 2013
Maybe someone can carefully look into this and help me out!

VBA Dynamic Save As, Microsoft Project to Excel

I am trying to create a macro that will export a Microsoft Project file into an excel file. Through the use of macro recording I have got a line of code that accomplishes this using the export wizard, but I want the file path and file name to be dynamic so I can use this macro on different projects. I have been searching many other threads and the Microsoft website with no luck. Is this possible?
Here is what I have:
sub formatAndSave ()
FileSaveAs Name:="C:\Users\XXXXXX\SharePoint\Projects\ProjectType\HxH\myProject.xlsx",_
FormatID:="MSProject.ACE", map:="myMap"
end sub
One idea I tried was:
Active.Workbook.SaveAs FileName:=Title
Any help would be very much appreciated!
For the sake of simplicity, let's assume for all answers below your project is located at c:\projects\myProj.mpp
I think you're after the string replace function. Something like:
Dim excelFilePath As String
excelFilePath = Replace(ActiveProject.FullName, ".mpp", ".xlsx")
Debug.Print excelFilePath
'the output would be c:\projects\myProj.xlsx
If you're unfamiliar with string manipulation in VB/VBA, just search the web for "VBA string manipulation". Microsoft has a decent article here: https://msdn.microsoft.com/en-us/library/aa903372(v=vs.71).aspx
A few other things that may be handy for you are these variables:
ActiveProject.FullName 'shows full path & name, so you'd get "c:\projects\myProj.mpp"
ActiveProject.Path 'shows just the path, so you'd get "c:\projects\"
ActiveProject.Name 'shows just the file name, so you'd get "myProj.mpp"
Finally, one caveat I've seen is that the ActiveProject.FullName and ActiveProject.Name variables may or may not provide the file extension depending on your local windows environment settings. I've observed that if Windows Explorer is configured to hide file extensions, then these variables also withhold the extension; if Explorer is configured to show them, then they are provided in the variables. Make sure your code is robust to both cases, or make sure you have control over the environment where you code will run.

Calling an External VBA from VBScript

I am using a program called mathtype to pull some equation objects out of a word document. I've written code in VBA that works perfectly using their API, but I have to translate it to a VBScript file. I have looked all over google, but have not found any solution on how (If it is even possible) to call a VBA library from VBScript.
VBScript can't see the MathTypeSDK Objects/Functions.
If not possible, how would I encase the macro I need to run in a globally available word file and call it from the VBScript?
Edit: Got it! Unfortunately the approaches below, while helpful, did not work for my situation. I found something closer: Embedding the macro in a global file and calling it through the Word Objects Run command.
objWord.Run "Normal.NewMacros.RunMain"
Here is an approach which might work for you. I tested this simple example.
Class "clsTest" in file "Tester.docm":
Public Sub Hello()
MsgBox "Hello"
End Sub
Class "Instancing" is marked "PublicNotCreatable".
Module in "Tester.docm":
Public Function GetClass() As clsTest
Set GetClass = New clsTest
End Function
In your vbscript:
Dim fPath, fName
fPath = "C:\Documents and Settings\twilliams\Desktop\"
fName = "Tester.docm"
Dim wdApp, o
Set wdApp = CreateObject("word.application")
wdApp.visible=true
wdapp.documents.open fPath & fName
Set o = wdApp.Run("GetClass")
o.Hello
Set o=nothing
Again - I only tested this simple example: you'll have to adapt it to your situation and try it out.
Word-VBA was not made to create reusable libraries, I suppose (for usage in external programs).
One way to reuse existing Word-VBA code is, however, run Word via WScript.Shell.Run using the /m<macroname> command line switch (see http://support.microsoft.com/kb/210565/en-us for details). This, has the restriction that evertime you need to call a specific macro, a Word process is started again, running that macro, and ends afterwards. Means, if you need just one call to your Word.VBA for a specfific task, this may be ok, but if you need a lot of interprocess communication between your VBScript and your VBA macro, you should look for a different solution.

Combine several Excel files into one using VB

I have a folder with several excel files that have a date field, i.e. 08-24-2010-123320564.xls. I want to be able to have some VB scripting that will simply take the files that start with todays date and merge them into one file.
08-24-2010-123320564.xls
08-24-2010-123440735.xls
08-24-2010-131450342.xls
into
08-24-2010.xls
Can someone please help?
Thanks
GabrielVA
assuming you just want to append rows in simple spreadsheets, follow this logic:
Psuedocode
use an excel macro
(you could just as well automate excel from vb but why vbscript alone since you need excel anyway?)
have it to a dir listing (dir function)
dim a date_start variable init to "new"
dim a merged_spreadsheet as new doc default to nothing
loop thru result of dir
if date_start <> start of filename
if merged_spreadsheet is not nothing
save it
set it to nothing
store start of date (left mid function) in date_start
if merged_spreadsheet is nothing
make a new one
open the file from the dir command's loop
select all the data
copy it
go to first empty row in merged_spreadsheet
paste it
loop files
if merged_spreadsheet is not nothing
save it
If you're not happy with all those 'nothings', you can set a separate flag to keep track of whether you have a merged_spreadsheet or not. Think about what happens for just one file in a date, no files at all, etc.
Of course you will tear out your hair finding out how to automate those excel functions. The secret to turning 'hard' into 'pretty darn easy' is this:
macro recorder will reveal the automation commands
They are not intuitive. So record a macro. Then do things that you'll need to do in your code. Stop recording and look at the result.
For example:
* load/save files
* select only entered fields
* Select all
* copy / switch files / paste
* create new sheet
* click in various single cells and type (how to examine/set a cell's contents)
In summary-
(1) Know exactly the steps to what you're doing
(2) Use macro recorder to give away the secrets of the excel object model. Steal its secrets.
Really this won't be all that hard if you marry these two concepts cleverly. Since the macro will be vbscript (at least if you use office 97 ;-) you can probably run it from vbs or vb6 if you want. A hop to vb.net shouldn't be that hard either.
Aspose makes it pretty easy to work with excel-files in .NET http://www.aspose.com