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

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")

Related

word add in cached and not able to remove it

there was originally an add-in installed for word, under the path
D:\User\UserName\AppData\Microsoft\Word\Startup\addin.dot
it is working fine, and it add to Word menu with a name addin_2017. Since it is already 2021, we figured its better to update the menu name, so we changed the name to addin_v2 instead. However, when we place the updated addin.dot into the folder, both name showed up in the Word add-in menu. addin_2017, and addin_v2. I am wondering if there are anything cached somewhere.
I have also tried to use VBA code to remove all the add-in, but when I copy the file back to the STARTUP folder, there are two items in the menu still.
Looking back at some (very old) Wd2003 projects the method that I used was to have two separate routines for deleting and creating menus. These were called when the add-in was loaded and unloaded, e.g.
Public Sub AutoExec()
DeleteMenu
CreateMenu
End Sub
Public Sub AutoExit()
DeleteMenu
End Sub
Public Sub DeleteMenu()
Dim cbc As Office.CommandBarControl
For Each cbc In Application.CommandBars("Menu Bar")
If cbc.Caption = "AddinV2" Or cbc.Caption = "Addin2017" Then cbc.Delete
Next cbc
End Sub
It is obvious from your investigations that the rogue menu exists in the addin. One option to remove it is to:
Create a new template
Export all the code modules from the existing addin
Import the modules into the new template.
This should be seen as only a (very) temporary measure until you have created a new add-in with a custom ribbon tab.

Can a Variable be stored in an Excel File that can not be accessed through Excel

I just discovered that in MS Word it is possible to store a Variable in a MS Word File that can not be accessed through the regular interface when running Microsoft Word.
Sub SetMyVariable()
Dim VARNAME As String
VARNAME = "HiddenVar"
ActiveDocument.Variables.Add VARNAME, "My special info"
End Sub
This gets saved in the XML Schema under word\settings.xml
I have tried using the ThisWorkbook Object in Excel, but it doesn't seem to have a Variable object that can be added like in word.
I want to know if there is something similar in Excel to store information/varialbes that get saved with the file.
PS: the closest thing I can think of (and use in codig) is a hidden named range.
You can try with the CustomXMLParts property of the Workbook which from the link seems a generic feature of Office products and available in Excel. Given you noted that a user would have to manually inspect the XML within the unzipped xlsx files then this seems to map to the Word Variables feature. The code sample just substitutes ThisWorkbook for ActiveDocument:
Option Explicit
Sub TextXMLPart()
Dim objXMLPart As CustomXMLPart
'add
Set objXMLPart = ThisWorkbook.CustomXMLParts.Add("<foo>bar</foo>")
'inspect
For Each objXMLPart In ThisWorkbook.CustomXMLParts
Debug.Print objXMLPart.XML
Next objXMLPart
End Sub
The accepted answer to this question (which focuses on Excel and vsto) states that:
Custom XML parts For an application-level add in, this is my preferred method of storing any application data that needs to be persisted in a saved xls file without ever being visible to the user.

Writing VBA procedure/function to an .xlam addin

I have created a VBA project in Excel and within said project is a module that writes code to another module. I eventually password protected the project so as to keep the code hidden (not realizing right away that this would pose some pretty obvious issues).
When the user interacts with the file, the module attempts to run the code but, encounters an error (a password protected project can't modify itself! Hey we all have our duh moments :p).
So, as a way around this, I figured 'why not make the portion that is edited unprotected', whilst keeping the remainder of the file locked. So I figured I would create an unprotected .xlam add-in, then write said code to this portion. I am having some difficulty figuring how to do this though however. Below is the code I have written that writes code to another module:
Public Sub errorWrite()
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim s As String
Dim d As String
Dim lineNumb As Long
With ThisWorkbook.VBProject.VBComponents.item("NewModule")
.CodeModule.InsertLines j, "Public sub newModule()"
j = j + 1
With ThisWorkbook.VBProject.VBComponents.item("NewModule")
.CodeModule.InsertLines j, "(my code etc..)"
j = j + 1
With ThisWorkbook.VBProject.VBComponents.item("NewModule")
.CodeModule.InsertLines j, "end sub"
end sub
While I have found some info on Stack and elsewhere regarding .xlam add-ins (Updating an xlam add-in using VBA) for example, I'd rather just keep it as simple as possible. Suggestions and hints are much appreciated. Thanks!
Using an unprotected Addin seems like a reasonable approach. But the code you have posted writes to another Module within the Protected addin. You need to reference the other AddIn as a VBProject
Change the name of your Unprotected AddIn to a unique Name (it defaults to VBAProject and does not have to be unique)
Then create a reference to it like this
Dim VBP As VBProject
Set VBP = ThisWorkbook.VBProject.VBE.VBProjects("NameOfYourUnprotectedAddin")
Then all your code uses this reference instead of ThisWorkbook.VBProject
Eg
With VBP.VBComponents.item("NewModule")
.CodeModule.InsertLines j, "Public sub newModule()"
' etc
End With
so I was able to find a way that seems to work, I wrote the code using FSO, then save the file as a .bas module. From there I used the below to have the module imported directly into the .xlam addin as a module:
Public Sub importBas()
Dim VBP As VBProject
Set VBP = Workbooks("example.xlam").VBProject
VBP.VBComponents.Import ("C:\Users\...example.bas")
VBP.VBComponents.Remove
End Sub
Then, in the original module, I deleted procedure within the xlam after execution was finished. Therefore no code can be seen (assuming I remember to use error-handling and disable break mode :p)
I am still curious how to write it 'directly', so I will play around with it more and see if I get it, although this way works
Thank you :)

How to access a Word public variable in Excel VBA

I'm trying to automate some report generation where Excel VBA is doing all the work. My employer has a standardized set of templates of which all documents are supposed to be generated from. I need to populate one of these templates from Excel VBA. The Word templates utilize VBA extensively.
This is (some of) my Excel VBA code:
Sub GenerateReport() ' (Tables, InputDataObj)
' code generating the WordApp object (works!)
WordApp.Documents.Add Template:="Brev.dot"
' Getting user information from Utilities.Userinfo macro in Document
Call WordApp.Run("Autoexec") ' generating a public variable
Call WordApp.Run("Utilities.UserInfo")
' more code
End sub
In the Word VBA Autoexec module, a public variable named user is defined and declared. The Userinfo sub from the Utilities module populates user. Both these routines are run without any complaints from VBA. I would then like to be able to access the user variable in my Excel VBA, but I get the following error
Compile Error: Variable not yet created in this context.
How can I access the Word VBA variable in Excel VBA? I thought it more or less was the same?
EDIT: the user variable is a user defined Type with only String attributes. Copying the Word VBA functions that populate the user variable is absolutely doable, just more work than I though was necessary...
In a Word module:
Public Function GetUserVariable() As String '// or whatever data type
GetUserVariable = user
End Function
In an Excel module:
myUser = WordApp.Run("GetUserVariable")
Alternatively, you could be able to replicate the variables value - as it's called user I suspect it is returning some information about a user, or author, of a document. In which case one of the following might be what you're after:
'// Username assigned to the application
MsgBox WordApp.UserName
'// Username defined by the system
MsgBox Environ$("USERNAME")
'// Name of the author of the file specified
MsgBox CreateObject("Shell.Application").Namespace("C:\Users\Documents").GetDetailsOf("MyDocument.doc", 9)
Another option - if you could only add a line of code to the Utilities.UserInfo sub (after setting your public variable):
ActiveDocument.Variables("var_user") = user
Then you could access it easily afterwards in Excel:
Sub GenerateReport() ' (Tables, InputDataObj)
' code generating the WordApp object (works!)
'I am assuming your WordApp object is public, as you don't declare it.
'Capture the new document object
Dim newdoc as Object
set newdoc = WordApp.Documents.Add(Template:="Brev.dot")
' Getting user information from Utilities.Userinfo macro in Document
Call WordApp.Run("Autoexec") ' generating a public variable
Call WordApp.Run("Utilities.UserInfo")
'Get and show the value of "user"
Dim user as String
user = newdoc.Variables("var_user")
msgbox, user
End Sub
This is assuming that useris a string.
EDIT: As it is a requirement to work only on the Excel VBA, I would definely try the approach suggested by Scott and MacroMan - replicating the same functionality of the Word macros in Excel - if possible.
I assume that you've already ruled out the possibility of using an edited copy of the original template, set in a public folder...
For the sake of completness, there is another possibility: actually it is possible to inject VBA code in a Word document without the VBProject Object Model, by "brute force". If you rename a Word document as a .zip file and open it, you will notice a \word\vbaProject.bin file in it. This file contains the VBA project for the document and, in principle, one could add or change VBA code by modifying or replacing it.
I did some tests transplanting code from one document to another by simply copying the vbaProject.bin file, and the concept works. If you are interested in learning more about this file, this topic could be of use.
Notice, however, that to do what you want with such a technique would be somewhat complex (it would involve, for starters, updating zip files from your Excel VBA), and would require a lot of experimentation to mitigate the risk of accidentally corrupting your files. Definetly not recommended if you are looking for an easy and simple solution - but it is possible.

dotm creates new document - how to remove the reference to the template?

I have a dotm with macros stored in it. When a new document is created, there is a reference to the dotm file (if you're looking at the project in VBA editor) which I need to eliminate.
Is it somehow possible to do this with VBA code?
You can sever the original reference by replacing it with a reference to another template using the Attachedtemplate property. For example:
Private Sub Document_New()
ActiveDocument.AttachedTemplate = "C:\Users\Joe\AppData\Roaming\Microsoft\Templates\Normal.dotm"
End Sub