Excel-2010 - CustomUI - Backstage : Errors when various files are opened in the same instance - vba

Firstly, i'm very happy to join the community. I hope we could often exchange advice. I'm french so excuse me for the mistakes in the sentences.
I try to explain my problem :
I had the "good" idea to use the Backstage of my Excel file to create a small dashboard. It works very well.
The problem arises when this file is opened at the same time as another file, in the same instance of Excel. The second file is trying to access functions "Backstage_OnShow" and "Backstage_OnHide" of my workbook so I have a message "Impossible to run the macro 'Backstage_OnShow' (or 'Backstage_OnHide'). It is possible that the macro is not available in this workbook ..." <- This is a translation to the french error message.
How can I do to not have this message or rather to ensure that the backstage is specific to my file and not the instance of Excel?
I show with my code snippets. It will be more clear.
In my XML, I have this :
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad ="Ribbon_Load">
<ribbon startFromScratch="false"/>
<backstage onShow="Backstage_onShow" onHide="Backstage_onHide">
...
</backstage>
</customUI>
In my Excel file, i have this :
Public Sub Ribbon_Load(ribbon As IRibbonUI)
Set Ruban = ribbon
End Sub
Public Sub Backstage_onShow(ByVal contextObject As Object)
'Rafraichissement du ruban
Ruban.Invalidate
End Sub
Public Sub Backstage_onHide(ByVal contextObject As Object)
End Sub
All this is contained in an Excel file, which is normal.
In Excel 2010, Excel files open by default in the same instance, which does not bother me, but, when a "normal" file is opened in the same instance as my customized backstage file, the normal file tries, I do not know by what miracle, to access the function Backstage_onShow Backstage_onHide and as soon as I display its backstage. However, this file should not even knows these functions exist because they are not reported for him.
Thank you in advance.
Sincerely,
Patrice.
PS : this is a link if you want to show my file. It's a safe code snippets of course !

#David, i have a solution !!!!
Thanks you to have take your time to answer me !!!
I try to explain you.
So, if we think a few moment, we note the problem is we need a stated place to store our code snippets. This place is in the XLSTART !!! If we use the PERSONAL.XLSB to store that :
Public Sub Ribbon_Load(ribbon As IRibbonUI)
Set Ruban = ribbon
End Sub
Public Sub Backstage_onShow(ByVal contextObject As Object)
'Rafraichissement du ruban
Ruban.Invalidate
End Sub
Public Sub Backstage_onHide(ByVal contextObject As Object)
'ErreurSaisieTaux = 0
End Sub
After, we can modify the customui.xml like that :
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad ="PERSONAL.XLSB!Ribbon_Load">
<ribbon startFromScratch="false"/>
<backstage onShow="PERSONAL.XLSB!Backstage_onShow" onHide="PERSONAL.XLSB!Backstage_onHide">
...
</backstage>
</customUI>
I have tried and it works.
What do you think about this idea ? Do you see drawbacks with this method ?
Patrice.

The Backstage is part of the Application, and you have specified that certain macros should be called for the onShow and onHide events.
Excel will always assume that an unqualified macro, e.g., Backstage_onShow should be found within scope of the ActiveWorkbook. When this macro does not exist in the ActiveWorkbook, the error raises.
To resolve this, revise the XML so that the macro call qualifies a specific workbook:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad ="Ribbon_Load">
<ribbon startFromScratch="false"/>
<backstage onShow="Exemple.xltm!Backstage_onShow" onHide="Exemple.xltm!Backstage_onHide">
...
</backstage>
</customUI>
Regarding Whether This Method Can Work on an XLTM File
Follow-up from comments:
When you use the above method on an XLSM file, the file which contains the Backstage_OnShow macro is open, and so calls to Exemple.xlsm!Backstage_OnShow work, because Exemple.xlsm is an open workbook file.
When you use this method on an XLTM file, to create a new file from template, the explicit XML with onShow ="Exemple.xltm!Backstage_OnShow will fail because the macro cannot be found. The macro cannot be found because Exemple.xltm is not a valid member of the Workbooks collection.
If, as you previously attempt, the XML does not fully qualify the macro (e.g., onShow="Backstage_OnShow), you will get the same error if you navigate to any open workbook which was not created by the XLTM, for the same reason: the macros cannot be found. In this case, the macro cannot be found in the scope of ActiveWorkbook.
It seems this simply may not work with an XLTM file (at least not without considerable effort)
You might be able to make it work if, instead of hijacking the Backstage you create a custom tab.
Alternatively, I think you would have to modify the Ribbon XML for each new instance of the file, and while it is possible to modify the Ribbon XML, I think it is not possible to do while the file is open.
Best solution I can think of would be to create an add-in or macro that does:
1) Prompt user for new filename
2) Create new file from template, save & close it
3) Modify the contents of the new file's Ribbon XML
4) Open the new file
You could see this for information about unpacking and modifying the XML:
http://www.jkp-ads.com/articles/Excel2007FileFormat02.asp)

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.

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...

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

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.

VBA - force excel to save in custom format

For a project at work i would like to allow the user to create an export of data to be saved for a single project, since it will not be possible to save my main workbook for each project.
I have created a function that exports the data to another workbook, but now i want to make sure that the user won't change the exported data directly (By setting another file format, i think the users would think more about opening the workbook, and therefore not do it by "accident")
So my question is:
Can i force the user to save the file as a .clg file?
Edit:
I solved the problem by saving the file with .SaveAs and specifying the format as "51" (The same as .xlsx"). I used GetSaveAsFilename to allow the user to decide the location (i.e. the project folder), and only browse for .clg files.
Dim sFullName As String
sFullName = exportFile.Application.GetSaveAsFilename(thisFile.Sheets("Project information").Cells(1, 2).Value, "Export files (*.clg),*.clg", , "Choose export location", "Export")
If Not Len(sFullName) = 0 Or sFullName = "False" Then
exportFile.SaveAs Filename:=sFullName, FileFormat:=51
End If
exportFile.Close
If someone wants to use Mr. Monshaw's approach, one way to go is to create a class module, which has a variable withevents - which means that any event that fires in the variable, fires in your module.
Public WithEvents wbk As Workbook
Just remember to keep the instance of the class in memory, for example by adding it to a collection.
Another way to use the BeforeSave is to dynamically write the code, see http://www.ozgrid.com/forum/showthread.php?t=163903
Is 'clg' a format you use? or just a random not xls/x extension?
If the file does need to be viewed and viewed in excel (guessing your not exporting windows catalogue files?) then changing the extension might not be the best move.
If you create your export using VBA, then you can automate some things to make it more apparent to the users that they should not be editing the data.
The Workbook.SaveAs method has the option for "ReadOnlyRecommended" which will inform the user that the workbook should be opened read-only when opening it. (its optional though, so they could ignore it)
Assuming the data is always read-only you could also lock the workbook so that no changes can be made.
Update
If you want to save an Excel Workbook with a custom extension (and not as a custom format) then using the Workbook.SaveAs method will work. ex: ThisWorkbook.SaveAs "Report.clg"
You'll either need to have your export code do this for you, or if its a more manual process you can just create a macro with a button to invoke the save-as for you instead of using the standard save-as dialogue.
If you want to save as a custom format, and it is not one of the ones supported by Workbook.SaveAs (supported formats) you will have to do as #Mr.Monshaw suggested and watch for the onbeforesave and generate the file manually. (unlikely someone has done an excel to windows catalogue converter)
im not sure what the constant for clg would be but the fileformat property of SaveAs in vba would allow you to decide what format to save in
i saw a clever solution to something similar to this if you can find the fileformat, just add a macro to your book with a workbook event like this:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Call SaveAsCLG(SaveAsUI) 'set save type
Cancel = True 'cancel user save
End Sub
that will override a user save-as request