Autohotkey runs Excel VBA macro but changes are not applied - vba

I have an Excel file, say Plano.xlsx and I am trying to run a VBA macro script on it using Autohotkey following the instructions stated here.
I don't want the Excel to be visible during this process. The VBA code is supposed to enter the value 99 in the cell C1 at the first sheet.
After hours of trial and error, the Autohotkey script runs smoothly without errors i.e. it opens an Excel process in the background supposedly
editing the Excel file and then exits. The problem is that the Excel file does not change at all. The VBA code works fine if I paste it manually
in a new VBA module in Excel without using Autohotkey.
Here is the code:
#SingleInstance force
#Include Acc.ahk
VBcode=
(
Sub myFunction()
Worksheets(1).Select
Worksheets(1).Range("C1").Select
Selection.Value = 99
End Sub
)
Excel_Run("myFunction")
Excel_Run(sFunction){
FilePath = C:\Users\KostasK\Desktop\Plano.xlsx
oExcel := ComObjCreate("Excel.Application")
Excel_ImportCode(VBcode)
oWorkbook := oExcel.Workbooks.Open(FilePath)
Excel_Get().Run(sFunction)
oWorkbook.Save
oExcel.Quit
}
Excel_ImportCode(VBcode){
if fileexist(A_ScriptDir . "\tempvbcode.txt")
FileDelete, %A_ScriptDir%\tempvbcode.txt
FileAppend, %VBcode%, %A_ScriptDir%\tempvbcode.txt
Excel_Get().ActiveWorkbook.VBProject
.VBComponents.Import(A_ScriptDir . "\tempvbcode.txt")
}
Excel_Get(WinTitle="ahk_class XLMAIN") { ; by Sean and Jethrow, minor modification by Learning one
ControlGet, hwnd, hwnd, , Excel71, %WinTitle%
if !hwnd
return
Window := Acc_ObjectFromWindow(hwnd, -16)
Loop
try
Application := Window.Application
catch
ControlSend, Excel71, {esc}, %WinTitle%
Until !!Application
return Application
}
To get the Acc.ahk library that is included in the script please see here. My Autohotkey version is v.1.1.23.05 and I use Excel 2013. I did not
take a closer look on Excel_Get() function but I used it instead of ComObjActive("Excel.Application") because the latter produces errors. There
is some useful info about that here. Finally, please note that I have enabled the following options in Excel Trust Center:
Enable all macros (not recommended, potentially dangerous code can run) and Trust access to the VBA project object model. Also, in Add-ins section
in COM Add-ins nothing is checked (I don't know if that matters). Finally, I always run the script as administrator.

This can be accomplished simply without VBA macro, while file being closed.
FilePath = C:\Users\KostasK\Desktop\Plano.xlsx
oExcel := ComObjGet(FilePath)
oExcel.Worksheets("Sheet1").Range("C1").VALUE := "99" ; you can put actual sheet name instead "sheet1"
oExcel.Save
oExcel.Quit
oExcel :=

Related

Need code help on calling a macro from a new VTSO addin for Word

I have created a new addin with a ribbon in MVS. On click of button1 I want to run a macro that is stored in a .dotm file in the Startup folder in Word. The .dotm file is called MyMacros and the macro is titled "TableMacro".
The module name in Word is titled NewMacros and the top rows of the macro in Word are:
Sub TableMacro()
`
` TableMacro
I am sure the macro is started with the code below but even this is guess:
Private Sub Button1_Click_1(sender As Obeject, e As RibbonControlEventArgs) Handles Button1.Click
`code to call TableMacro'
End Sub
I know how to write macros but I have no idea the code needed to trigger the macro stored in the MyMacros.dotm file.
To search all global templates, including the Building Block template, from a VSTO add-in, you can use this:
Dim wApp = Globals.ThisAddIn.Application
Dim i As Integer, Tmplt As Word.Template = Nothing
For i = 1 To wApp.Templates.Count
If wApp.Templates(i).Name = "MyMacros.dotm" Then
Tmplt = wApp.Templates(i)
wApp.Run(Tmplt.Name & "!TableMacro")
End If
Next
The value of performing it this way is you now have an object variable set to a specific global template and you can then get at AutoText, Styles, etc. and of course macros that are stored in that specific global template.
Your VSTO code has a Microsoft.Office.Interop.Word.Application object. Say you're storing that reference in a variable named hostApp, you could do this:
hostApp.Run("TableMacro")
That requires the .dotm file to be the "active" document. If the document isn't active and you have a reference to it (say, theDocument), I think this might work (untested):
hostApp.Run(theDocument.Name & "!TableMacro")
The object VB.NET uses is the same one VBA uses, so if Application.Run "MyMacros!TableMacro" works in VBA, it will work in VB.NET. I'd try to fiddle in VBA first to get the syntax right - you get instant feedback, vs needing to build and launch the host, load the add-in and test the thing with VSTO.
The following Run syntax worked for me from within a VSTO Add-in to run VBA code in a Template loaded as an add-in. It uses the module name plus the macro name.
Keep in mind that Run can only work with public subs...
Globals.ThisAddIn.Application.Run("Module1.TestPublicVarx")

PowerPoint 2013 macro keeps file locked open after close command

I have a PowerPoint VBA function that opens presentations, copies slides into the active presentation, then closes the source presentation. It worked fine in 2010, but fails in 2013 (all on Windows 7) if it tries to open the same presentation more than once. It appears to me that after the presentation.close command is issued, the window is closed, but the file remains locked open until the VBA code exits. So if the code attempts to open that file again it returns the error:
"Method 'Open' of object 'Presentations' failed"
Here's a simplified form of the function I'm running that behaves the same way. I've had a colleague test this again in PowerPoint 2010 and it runs fine. I've also had a colleague test it under his 2013 to make sure it's not something with my particular installation.
Sub testopen()
Dim ppFile As Presentation
Dim i As Integer
Const fpath = "C:\test.pptx"
For i = 1 To 2
Set ppFile = Application.Presentations.Open(fpath)
ppFile.Close
Set ppFile = Nothing
Next i
End Sub
The file test.pptx is just a blank presentation. In debug mode I can see the file opens and closes on the first loop, then on the second loop the open command fails and I can see in Windows explorer that the hidden temporary file still exists, indicating the file is still open, until I exit the VBA code. I also verified that the file is held open by adding in a function to check the file open status.
I've spent probably an hour googling this and cannot find any other descriptions of this problem. I'm sure I can implement a workaround but it's driving me crazy that I can't find any other reports of seemingly such a simple issue. Any suggestions are greatly appreciated! Thanks.
The Best way that I have achieved this is to simply create a VBS file and in the VBS file I call out the desired VBA code. It's little more hassle than to write the VBA code, but it's the solution that worked for me.
For example in the VBS file:
Dim args, objPP
Set args = WScript.Arguments
Set objPP = CreateObject("Powerpoint.Application")
objPP.Open "C:\path\to\file.ppx"
objPP.Visible = True
objPP.Run "The_Macro"
objPP.Save
objPP.Close(0)
objPP.Quit
Or better yet, have the entire code within the VBS file and have it copy the desired slides.
Hope this helps you achieve your result.
Setting the file as Read Only resolved the issue. The open command is now:
Set ppFile = Application.Presentations.Open(fpath, msoTrue)
Also, saving the file before closing it resolved the issue. For that, add:
ppFile.Save
Interestingly, I had already tried setting the Saved property to True (ppFile.Saved = msoTrue), which does NOT work. Thanks to Michael for his suggestion on the VBS script. That does work and I had never run an external VBS script so I learned something new. In this case, I'd prefer to stick with a VBA solution.

Running a VBA macro from a Visual C++ Application

My goal is to use my application (written in visual c++ 2010) to open some files in excel and run a macro written in VBA
Here is the code I have right now
try
{
System::Object^ oMissing = System::Reflection::Missing::Value;
Excel::Application^ pExcel = gcnew Excel::Application();
pExcel->Visible = true;
Excel::Workbooks^ pBooks = pExcel->Workbooks;
Excel::_Workbook^ pBook = pBooks->Open(_XLSFile, ... );
Excel::_Workbook^ pBook2 = pBooks->Open(_XMLFile, ... );
Excel::_Workbook^ pBook3 = pBooks->Open(_MacroFile, ... );
pExcel->Run(macroName, ... );
// ....
}
catch(Exception^ e)
{
System::Windows::Forms::MessageBox::Show(e->Message);
}
I've omitted the 20ish "System::Reflection::Missing::Value" parameters required for Open() and Run().
The Run command throws an exception, gets caught, and the message box shows the error:
Cannot run the macro '<macroName>'. The macro may not be available in this workbook or all macros may be disabled
My problem is that this message appears for one macro file but not another, even though they are very similar and both do indeed have the macro defined in them.
I think it has to do with macros being enabled, which excel makes the user do manually. My confusion is why it works with one file and not the other.
Any suggestions on how to fix this are greatly appreciated!
EDIT: The solution to my particular problem (though I'm not sure why it worked) was to put my macro file into 'design mode' in the VBA IDE

vbScript opens up excel but doesn't load macro/modules?

I m in a very weird situation. I created a vbs script that would open my excel file. I had defined vba code in WorkBook_open method. I thought creating a vbs script to open up my excel would invoke my workBook_open method and execute the vba code inside it. But I was wrong. Below is my vbs code.
filePath = "E:\data_extracts\mydata.xlsm"
Set oExcel = CreateObject("Excel.Application")
oExcel.Workbooks.Open(filepath)
oExcel.Visible = True
oExcel.Run "RefreshDataFromIQY"
oExcel.ActiveWorkbook.Save
oExcel.ActiveWorkbook.Close
oExcel.Quit
Set oExcel = Nothing
On debugging, it fails at oExcel.Run "RefreshDataFromIQY" saying either macros are not available or disabled. Hence it is the code just opnes up excel application successfully and that's all it does. I have macro codes in module1, module2. How/where do I write to execute my macros in vbs script below. My macros/modules have to be executed in sequence and some of my macros are recorded macros. Any help is much appreciated. Thanks.
Thanks for your input Scott. Here's what I made changes to my code
Dim oExcelApp
Dim oExcelWkb
set oExcelApp = createobject("Excel.Application")
set oExcelWkb = oExcelApp.Workbooks.Open("\\myserver\data_extracts\TestTOPTMay307.xlsm")
oExcelWkb.Close True
oExcelApp.Quit
However on running it from command line, its giving me runtime error Object required: 'Close'. Any idea why? Why is it failing to Close? What am i doing wrong? Thanks.
I just tested your code against a dummy file I made. It worked when I placed the code inside a module and left it as public. However, when I put into a private module -> like worksheet level module, I got the error you got.
However, when I referenced the private object, the code ran through. So my answer to you is to replace
oExcel.Run "RefreshDataFromIQY"
With
oExcel.Run "[yourClassName].RefreshDataFromIQY"
Also, I placed a workbook_event in my file as well. The event triggered successfully on open, so if there is trouble with yours, it's most likely in the code inside the event.

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.