Late Binding Word Ribbon - vba

First post here, and also still learning VB so be gentle :).
I'm trying to understand how to late bind the Word Ribbon to a .dotm file.
I have a large number of users working in the Windows 10 (64 bit) platform using Office 2010 (32 bit), but we are transitioning to Office 365 (64 bit) in the next few months.
Both versions support ribbon customization using XML and VB. I'm trying to do the following:
Create a Custom Ribbon
Add various buttons, etc with custom images
When each button is clicked it runs a macro
The .dotm file will be distributed to each users machine to one of the trusted locations so that the ribbon loads each time a word session is opened and that the macros run when clicked.
I have a working prototype for Office 2010, but the same file in Office 365 does not work due to missing references to the Microsoft Office 15 Object library. From my understanding by "late binding" rather than "early binding", I can solve this problems since I wont be referencing the Microsoft Office 2015 object library directly and when Word loads it will look to whatever reference library the computer has and uses to initialize the Ribbon.
I see I need to change the code to reference the ribbon as an Object, but am not sure what the specific call to the Word Ribbon customization needs to be (and how to tie everything together). Looking through the various MSDN library links hasn't helped, its mainly just confused me further.
I've posted my generalized code below. Any help and advice would be appreciated.
The XML file looks like this
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Onload">
<ribbon>
<tabs>
<tab id="CustomTab1" label="Tab1">
<group id="Grp1" label="Group1" >
<button id="Grp1Btn1" size="large" label="Button1" image="Button1Image" onAction="Button1Clicked"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
Then within the Project\Microsoft Word Objects\ThisDocument I have the code that loads the Custom Ribbon, and then links the button click to the appropriate macro.
Option Explicit
Public myRibbon as IRibbonUI
'This is the link to the XML file so the custom ribbon loads at startup
Sub Onload(Ribbon as IRibbonUI)
Set myRibbon = Ribbon
End Sub
'Code for a single button which then runs a macro
Sub Button1Clicked(ByVal control as IRibbonControl)
Macro.Button1
End Sub
'Each button has its own Sub like this one, just with an different name and link to its macro
Then under Project\Modules\Macro, I have all the code associated with each macro, like this:
Sub Button1()
MsgBox Prompt:="At this point Macro 1 would run, but for this example, all you get is this silly dialog box", Title:="Button 1 Success"
End Sub

You do not need to use late binding to make the ribbon work under O365. Word will pick up the later object libraries automatically.
You will need to test all your code to ensure that it is 64 bit compatible, but that is a different issue.
For O365 the object libraries (.OLB) will be under C:\Program Files\Microsoft Office\root\Office16. The Office 16 library (MSO.dll) should be under C:\Program Files\Common Files\Microsoft Shared\OFFICE16. If they're not there you need to find out why.
There are some additional security options that system admins can set for O365 including disabling the use of macros. You need to check what options are being applied as part of the install and ensure that whoever is responsible for the rollout understands that you need to use macros.
You also need to ensure that the .dotm is stored in the folder designated as the Startup folder. You can check this in File | Options | Advanced | File Locations
This location must be the same as the one listed for Startup in the Trusted Locations. By default it is C:\Users\UserName\AppData\Roaming\Microsoft\Word\STARTUP. You should note that the Startup folder under C:\Program Files\Microsoft Office\root\Office16\ is not a trusted location anymore (under Office 2010 it was an implicitly trusted folder and any .dotm located there would work regardless of the trusted folder settings).

If this needs to be run on different machines, you can use code to find the Startup Folder on the particular machine. The user, though, will need to set that folder as a trusted location. Here is a macro that puts the path in a message.
Sub ShowStartUpPath()
' Macro written by Charles Kenyon 2014-02-25
' Shows setting for Startup Folder location in Microsoft Word in a message box.
'
MsgBox _
Prompt:="Your StartUp folder location is " & _
Application.StartupPath & _
"." & Chr(13) & Chr(10) & Chr(13) & Chr(10) & ".", _
Buttons:=vbOKOnly, _
Title:="Current Startup Folder Setting Information"
'
End Sub

Related

Outlook / VBA Macro inside Modal-Email

We have a small macro (added to the ribbon) which on click is supposed to attach some files from a certain folder.
This works, when creating a "new" Mail from within Outlook. When you use (for example) Adobe Acrobats "send as email" Function, the email in Outlook is opened as modal dialog.
The button on the ribbon now has no effect. It simple does nothing. (Not even a MessageBox in the first line would be displayed)
Using Developer Tools -> Macros and selecting the macro from "there" works.
Why isn't the button on the ribbon, calling the very same macro NOT working with modal emails?
the macro - but as mentioned not even a MessageBox would appear.
Sub AddAttachments()
Dim Path As String
Path = "C:\test\"
Dim NewMail As MailItem, oInspector As Inspector
Set oInspector = Application.ActiveInspector
If oInspector Is Nothing Then
MsgBox "No active inspector"
Else
Set NewMail = oInspector.CurrentItem
If NewMail.Sent Then
MsgBox "This is not an editable email"
Else
With NewMail
d = Dir(Path & "*.*")
While d <> ""
.Attachments.Add Path & d
d = Dir
Wend
End With
End If
End If
End Sub
Update:
With another application offering more settings for the email-sending application, I was able to figure the following out:
When using "Outlook OLE" als Email-Sending Method, everything works as expected.
When using "MAPI" as Email-Sending Method, the effect mentioned above appears.
So, Adobe Acrobat seems to use (Simple) MAPI by default.
Edit: After knowing the actual cause, I found this: https://www.msoutlook.info/question/203 - Seems to be a known, not solvable limitation for Applications creating their emails through MAPI because Outlook is not loaded "fully" but just some basic stub.
Update:
When clicking on the "Send as mail Button" (1) the following Window appears. Clicking on the "Macro-Button" inside the ribbon (2) does nothing.
Switching to DeveloperTools, Selecting "Macros" and selecting the same Macro from there however works:
The Button on the other hand works for "New Emails" created via Outlook and "New Mails" created by using Outlook OLE rathen than MAPI.
Okay, to summarize the problem and the missing solution:
I was in touch with the support of various applications showing this problem and they confirmed, that this problem is caused by using MAPI as entrypoint.
For Example, "PDF24" allows you to specify wheter it should use MAPI or Outlook OLE: Choosing MAPI results in the same problem, while choosing Outlook OLE makes VBA-Macros work...
I also noted, that the Addin responsible for processing VBA-Scripts (Microsoft.VbaAddinForOutlook) is not loaded, when Outlook is triggered using MAPI:
The Eventlog (Application) shows that several Addins are loaded during startup:
Outlook loaded the following add-in(s):
Name: Microsoft Exchange Add-in
Description: Exchange support for Unified Messaging, e-mail permission rules, and calendar availability.
ProgID: UmOutlookAddin.FormRegionAddin
GUID: {F959DBBB-3867-41F2-8E5F-3B8BEFAA81B3}
Load Behavior: 3
HKLM: 1
Location: C:\Program Files (x86)\Microsoft Office\Root\Office16\ADDINS\UmOutlookAddin.dll
Boot Time (Milliseconds): 0
Name: Outlook Social Connector 2016
Description: Connects to social networking sites and provides people, activity, and status information.
ProgID: OscAddin.Connect
GUID: {2163EB1F-3FD9-4212-A41F-81D1F933597F}
Load Behavior: 3
HKLM: 1
Location: C:\Program Files (x86)\Microsoft Office\Root\Office16\SOCIALCONNECTOR.DLL
Boot Time (Milliseconds): 0
...
But the VBA-Addin is only loaded, when "clicking" on the macro-button for the first time, because it's LoadBehavior: 9 rather than LoadBehavior: 3:
(This event appears "onclick"):
Name: Microsoft VBA for Outlook Addin
Description:
ProgID: Microsoft.VbaAddinForOutlook.1
GUID: {799ED9EA-FB5E-11D1-B7D6-00C04FC2AAE2}
Load Behavior: 9
HKLM: 1
Location: C:\Program Files (x86)\Microsoft Office\Root\Office16\ADDINS\OUTLVBA.DLL
Boot Time (Milliseconds): 0
(The event is completly missing, when the Window is created through a MAPI-Call)
I managed to "fake" a Entry in the Registry in order to load the Addin right away:
[HCU\SOFTWARE\Microsoft\Office\Outlook\Addins\Microsoft.VbaAddinForOutlook.1]
"LoadBehavior"=dword:00000003
Which indeed made the Addin load during Outlook-Startup - but without the desired impact of having VB-Macros working in MAPI-generated Email-Windows.
Name: Microsoft VBA for Outlook Addin
Description:
ProgID: Microsoft.VbaAddinForOutlook.1
GUID: {799ED9EA-FB5E-11D1-B7D6-00C04FC2AAE2}
Load Behavior: 3
HKLM: 1
Location: C:\Program Files (x86)\Microsoft Office\Root\Office16\ADDINS\OUTLVBA.DLL
Boot Time (Milliseconds): 0
But maybe "this" might be a good starting point for somebody facing the same Problem and seeking for a solution.

Cannot fully intercept or trap SaveAs Dialog

With the help of the web, I have been able to write some VBA to check content controls on a Word 2013 template document and use the content to create a filename when Save or Save As are chosen, using custom subroutines with the appropriate names:
Sub FileSave()
If Left(ActiveDocument.Name, 4) = "PDR " Then
ActiveDocument.Save
Else
Call FileSaveAs
End If
End Sub
Sub FileSaveAs()
' too much to list, but in essence it
' checks contents of content controls
' and if properly populated build a string to use as a filename
' with the standard ActiveDocuments.SaveAs2 command
End Sub
The code works when using the keyboard methods of [Ctrl]+S/ pressing [F12], when using the standard Save button in the ribbon, and when using Save from the File menu.
However, if the user opts for the menu option and chooses File \ Save As... (i.e. the one that looks like this -
then it seems that the code in my FileSaveAs() routine gets bypassed and we get a normal Save As dialog box with the first couple of words from the document being suggested as the filename (i.e. standard Word behaviour).
How can I capture the menu method?
Alternatively, I did consider disabling "Save As..." from the File menu within a autonew() subroutine utilising CommandBars, but I can't get this to work. The sites I researched suggested that this ceased to be possible from Word 2010 onward.
I found that after Office 2007 you need to use a Custom UI editor to manipulate the XML of the file.
I then found that there was a change in some of the key words from Office 2013 onwards (when the "Office" button in the top left corner was replaced by the "File" menu). Not only does this link allow you to download the Custom UI Editor, but it gives the subtle details in the commands between Office 2010, 2013 and 2016: http://www.rondebruin.nl/win/s2/win005.htm
Whilst the page above refers to Excel, it appears to be common across Office as a whole. I used it to alter a Word 2013 file to successfully hide "Save As" from the File menu. Below is the relevant XML I employed to solve my particular issue:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<backstage>
<tab idMso="TabSave" visible="false" />
</backstage>
</customUI>

macros on document open. Bug in ms office 2013?

I have a macros that starts running when a document opens. It looks like this:
Private Sub Document_Open()
....
Dim strInput As String
strInput = ActiveDocument.Content
....
In office 2010 this macros works nicely, however, in office 2013 I get this error message (translation to English):
This command is not available, because there are no documents opened
And when I hit on Debug button, I see that this line of code is highligthed:
strInput = ActiveDocument.Content
I think it's a bug of office 2013, because for some insane reason it invokes this macros before the document is opened, even though I clearly force it to do right after opening - Private Sub Document_Open(). So, what is wrong with that and how can I fix that?
If you're seeing the yellow message bar then this isn't a bug. It's because your document isn't "trusted" - it's "sandboxed". You need to check whether it's a ProtectedViewWindow - introduced in Office 2010 - and take appropriate action.
The Protected View is explained in this article: https://support.office.com/en-us/article/What-is-Protected-View-d6f09ac7-e6b9-4495-8e43-2bbcdbcb6653. Basically, it prevents code from running in any document that comes from an origin / author that isn't trusted. The user needs to make an explicit decision to trust the document in order to go into Edit mode.
Obviously, this is a major impediment to VBA code, so you have the ProtectedViewWindow object in the object model. There are application-level events that can be used to manage this state in the entire Word environment, such as ProtectedViewWindowOpen that triggers when a document is opened "sandboxed". After determining whether the document is trustworthy, the code can make the document editable using the ProtectedViewWindow.Edit method.
For example, to trust all documents that come from a specific file path:
Private Sub app_ProtectedViewWindowOpen(ByVal PvWindow _
As ProtectedViewWindow)
If PvWindow.SourcePath = "C:\Test" Then
PvWindow.Edit
End If
End Sub
The above needs to be in an application-level class module and events for this class need to be active on the application. If you're not familiar with using application-level events, see https://msdn.microsoft.com/en-us/library/office/ff821218.aspx

Auto displaying form on opening a template file, dotm from explorer

I have written a form based document generation macro (in VBA) for distribution to a sales team.
For their ease of use, I want to provide a self-contained file which will display the form as soon as the document is opened.
Using AutoOpen I can get the form to display as intended if word is already open and the dotm file is opened within. However, if I double click the file from within explorer, nothing happens and I have to launch the macro manually. I thought AutoExec might allow this but no luck there. I've spent considerable time trying to get this to work through googling etc. but I'm not getting anywhere.
How can I make the form display even when the file is opened with a double click? Is it possible to do this without having to change normal.dotm for each user?
For further background, I am using Word 2013 with macros fully enabled during testing. The dotm file is stored in a trusted location.
I am using a macro to launch the form like this...
Public Sub AutoOpen()
StartPage.Show
End Sub
I have tried using AutoExec as well to no avail.
In the "generator.dotm" file got to Visual Basic and go in to the "ThisDocument" Microsoft Word Object.
At the top of the Visual Basic Editor select "Document" in the left hand side and then click on "New" on the right hand side. Private Sub Document_New() method will appear for you to be able to edit. Then you can call your userform in there. Similar to:
Private Sub Document_New()
Dim myForm As UserForm1
Set myForm = New UserForm1
myForm.Show
End Sub
Save your Generator.dotm and double click it through Windows explorer and you should get the results that you would like.

How do you take a vba addin and make an installer?

I have written a vba module that installs an addin button to excel. I would like to deploy it on many computers as easy as possible. Currently, these are my steps.
Save the excel as *.xlsm
Open the doc on the computer that I want to install
Save the document as an extension (*.xlam) which places it in the addin folder
Go to options>Add-Ins>Manage /Go>
Click the check box for my add in
Is there a way to automate this process? Write something in vb?
You can build an exe or msi installer with a tool such as Advanced Installer (the free edition). One thing you can do is copy files to a specific location and if you put it in the XLSTART folder then that add-in will automatically load for the user when starting Excel. There's a complimentary folder within the Excel program files directory, usually something like this
C:\Program Files (x86)\Microsoft Office\Office15\XLSTART
which if you drop xlam files into that folder they'll load by default too. The user specific option is
C:\Users[UserName]\AppData\Roaming\Microsoft\Excel\XLSTART
there are PROS/CONS to both models. One is will all users have access or just the user that installs. The benefit of the user specific option is it requires limited rights for installation (no admin privileges)
As others have said, you can easily copy the files to the needed folder in each users Apps directory. They will then need to check the box in the Add-Ins menu, but at least the file will be there. I use this at work with some success by putting the Add-ins and .bat file on a shared drive that everybody has access to. You can then just provide a link to the .bat file which will copy the file from the shared drive to the person's computer in their Apps directory. Here's an example .bat that I use:
xcopy "\\server\share\folder\Addins\CRWScleanup.xlam" "%APPDATA%\Microsoft\AddIns\" /y
Initially I had the .bat files setup to detect and handle Win7 and WinXP because we have a mix at work, but then I realized that the folder location after %APPDATA% (which leads to a different place for XP vs Win7) is the same for each version of Windows. I.e. \Microsoft\AddIns\ %APPDATA% is a global Windows variable and its value will vary for each user (that is good).
It works brilliantly once the Add-In is installed because to update it all I have to do is put the new version on the shared drive and have the user click the link to the .bat while Excel is closed. The new version gets copied over and the user doesn't have to do anything.
With VBA you have to rely on Office files (Excel ones, in this case) and cannot move to executables/installation packages. You might create a program (or a macro) performing the steps you want in an automated way. But if what you want is relying on a standard installation package, which the user might execute (as usual, when installing a program), you would have to move to VB.NET.
VB.NET and VBA are not too different (well... actually, VB.NET includes many more things, but "understands" most of the VBA code) and VB.NET is quite programmer friendly; so a conversion from VBA to VB.NET wouldn't take you too long. In VB.NET you have different ways to interact with Excel; from your question, I understand that you want the Add-in alternative: it generates a custom "installation package" which, once clicked, will install the give Excel Add-in in the target computer. Relying on this option is easy: in your Visual Studio (you need a VS to work with VB.NET), open "New Project" and, within the Visual Basic Templates, select Office (, your version) and Excel Add-in.
NOTE: useful link provided my Mehow: it refers to an old VS (2008) but things haven't changed too much since then.
NOTE 2: the aforementioned suggestion is available in any fee-based VS version since the 2008 one. Not sure about the support in free versions (Express ones).
The easiest way would be to add some install code to the xlsm file on open.
This code could then do the following:
Turn off the install code by changing a variable on sheet (this will
stop the on open code running next time the file opens);
Save a copy of the file as an xlam to the add-in folder;
Activate the add-in;
Then close the xlsm file.
All you need to do is then email the xlsm file out and ask people to open it.
The code is quite simple, but I can show if you need.
I researched a lot of the methods described above and on other sites, but have managed to create my own install and uninstall using Excel add-ins (*.xlam) themselves. My add-ins now install themselves the first time and I have an uninstallation script as well. Works like a charm.
I used my own variation and derivations of Ivan's Solutions': https://grishagin.com/vba/2017/01/11/automatic-excel-addin-installation.html
Sub SaveAsAddIn()
'Alt+F8 SaveAsAddIn Run
Dim sName As String
Dim sFilename As String
Dim o As Object
On Error GoTo Finally
Try:
With Application.ThisWorkbook
sName = Split(.Name, ".")(0) 'name of ThisWorkbook without extension
sFilename = Application.UserLibraryPath & sName & ".xlam"
.Save
.Worksheets.Add After:=.Worksheets(.Worksheets.Count) 'add a blank sheet at the end
On Error Resume Next
Application.AddIns(sName).Installed = False 'uninstall the previous version of the AddIn
SetAttr sFilename, vbNormal
Kill sFilename
Application.DisplayAlerts = False
For Each o In .Sheets 'delete all sheets except the last one
o.Delete
Next
.SaveAs Filename:=sFilename, FileFormat:=xlOpenXMLAddIn 'save ThisWorkbook as AddIn
Application.AddIns(sName).Installed = True 'install ThisWorkbook as AddIn
.Close
Application.DisplayAlerts = True
End With
Finally:
End Sub