I have created a simplistic menu in word that opens a number of word files with various macros built in. Some of the macros function as normal when opened via the menu (e.g. forms that are showed on Private Sub Document_Open), but others (code to ignore warnings about saving in a macro free environment) are ignored.
Other issues, such as a debug errors about Word being 'unable to fire event', also seem to be occurring, but only when the files are opened via the menu. All the menu does is use Documents.Open and open the file.
When users open the file without the menu, no issues occur (no warnings about saving in a macro free environment, no errors about firing events).
Any help would be appreciated! I would prefer to not have to scrap the menu entirely for usability, as there are quite a few files that the menu serves as an index for.
Code below:
If ComboBox1.Text = "- Full title page" Then
Documents.Open ("A:/Workgroup Documents/Folder 1/templates/name of file.docm")
End If
Code from document that is opened (ThisDocument module):
Private WithEvents App As Word.Application
Option Explicit
Dim sDocName As String
Dim kcsval As String
Private Sub Document_Open()
frmFooters.Show
Set App = Word.Application
CATform.Show
On Error Resume Next
Documents("J:\Workgroup Documents\Edited Transcript\templates\menu.docm").Close SaveChanges:=wdDoNotSaveChanges
FixSaveError
End Sub
Then some forms, and a module and class module that deal with the save error. Module:
Option Explicit
Dim MyClass As New Class1
Sub FixSaveError()
Set MyClass.App = Word.Application
End Sub
Class module (Class1):
Public WithEvents App As Word.Application
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, _
SaveAsUI As Boolean, Cancel As Boolean)
Application.DisplayAlerts = wdAlertsNone
ActiveDocument.Save
Application.DisplayAlerts = wdAlertsAll
End Sub
The module and class module function purely to remove the warning message about saving in a macro-free environment when saving as docx from docm.
The forms that open up on Document_Open function fine, if it gets that far (the 'error firing event' error sometimes stops it getting there, but only sometimes and only on some machines - and only when using the menu to open the files).
Related
I am writing a VBA project containing 1 module (m1) and 1 userform (uf).
In "ThisDocument" I am calling a public sub from "m1" that initializes a collection I then refer to in the userform. This works perfectly fine until I deploy this project to other files.
I save the file as a .dotm in the %Appdata%/Microsoft/word/startup folder so I can use the project in all of my word files. But as soon as I try to use my project in other files the userform opens itself as designed but the collection is empty.
What could be the problem here?
Manually calling the initialization method from the userform works fine.
'----------------------------------------------ThisDocument
Private Sub Document_Open()
initBetaCollection
End Sub
'----------------------------------------------m1
Option Explicit
Public beta As Collection
Sub initBetaCollection()
Set beta = New Collection
beta.Add Array("0041", "A"), Key:="0041"
'...
End Sub
'----------------------------------------------uf
Option Explicit
Private Sub txtSearch_Change()
Dim arr As Variant
Dim search As String
'Defining the textinput as "search"
search = txtSearch.Value
For Each arr In beta
If search <> "" Then 'No empty search field
If arr(1) Like "*" & search & "*" Then 'Match found
lbResults.AddItem arr(0)
End If
End If
Next
End Sub
I get the: Run Time Error '424' object required
The problem with using Document_Open in the ThisDocument class or a macro named AutoOpen in a regular module is that both execute specifically for this document (or documents created from the template), only.
In order to have an application-level event, that fires for all documents opened, it's necessary to work with application-level events.
For this, first a class module is required. In the class module, the following code is needed:
'Class module named: Class1
Public WithEvents app As Word.Application
Private Sub app_DocumentOpen(ByVal Doc As Document)
'MsgBox Doc.FullName & ": on Open"
'Code here that should fire when a document is opened
'If something needs to be done with this document, use
'the Doc parameter passed to the event, don't try to use ActiveDocument
End Sub
Then, in a regular module, an AutoExec macro can be used to initialize the class with event handling. (AutoExec: fires when Word starts and loads a template with a macro of this name.)
Option Explicit
Dim theApp As New Class1
Sub AutoExec()
'MsgBox "AutoExec"
Set theApp.app = Word.Application
End Sub
'Sub AutoOpen()
' MsgBox "Open in AutoOpen" - fires only for this document
'End Sub
I have problems with sheets.activate contained into Workbook_Open() when the file is in protected mode (because downloaded from internet). As soon as you "Enable Editing" the Workbook_Open() is executed and the error comes out.
If I use this code:
Private Sub Workbook_Open()
Sheets(2).Activate
End Sub
I have this error:
Runtime Error 1004 Activate method of Worksheet class failed
Reading in some other discussion i tried using the Workbook_Activate() method which works if I have only one simple activation in all my project. The previous example could be fixed if I use:
Private Sub Workbook_Open()
a = True
End Sub
Private Sub Workbook_Activate()
If a Then
Sheets(2).Activate
a = False
End If
End Sub
but it partially fix the problem, this code could work but next time i have another sheets.activate in my project the error comes out again, (i.e. if i click a button into a panel or if i run other routines).
This error comes out only the first time you open the file, if you stop the debugger, close the file without saving and reopen the file the error doesn't come out again but i would avoid it to come out the first time
Thanks in advance
This looks like it is a known issue:
Object Model calls may fail from WorkbookOpen event when exiting Protected View
It says ...
Resolution
You can workaround the issue by either :-
If the location from where the workbooks are being open is trusted, add that location to the Excel's Trusted Locations.
Defer Object Model calls to outside of the WorkbookOpen event to WorkbookActivate event.
Code example to defer object model calls
Option Explicit
Public WithEvents oApp As Excel.Application
Private bDeferredOpen As Boolean
Private Sub oApp_WorkbookActivate(ByVal Wb As Workbook)
If bDeferredOpen Then
bDeferredOpen = False
Call WorkbookOpenHandler(Wb)
End If
End Sub
Private Sub oApp_WorkbookOpen(ByVal Wb As Workbook)
Dim oProtectedViewWindow As ProtectedViewWindow
On Error Resume Next
'The below line will throw error (Subscript out of range) if the workbook is not opened in protected view.
Set oProtectedViewWindow = oApp.ProtectedViewWindows.Item(Wb.Name)
On Error GoTo 0 'Reset error handling
If oProtectedViewWindow Is Nothing Then
bDeferredOpen = False
Call WorkbookOpenHandler(Wb)
Else
'Delay open actions till the workbook gets activated.
bDeferredOpen = True
End If
End Sub
Private Sub WorkbookOpenHandler(ByVal Wb As Workbook)
'The actual workbook open event handler code goes here...
End Sub
Check the file properties before you open it. If it comes from the internet, there is usually a "Unprotect" checkbox on the properties page you can check to unprotect it, then it should open like normal.
There is also a "Read-only" checkbox that might help you too. Make sure the file is not read only.
Just do this before you open it.
I've ended up cleaning up someone's mess on a Word document that's used throughout my company. It is a macro-heavy document that needs to be saved as a .docm exclusively. I'm worried about it getting "save as"-d as something other than a .docm, but I can't seem to find a way to limit the save as file picker or swap out the extension on save as while still using VBA.
How can I achieve this?
Edit: Some of the things I've tried, to no avail:
This has the right idea, but doesn't actually limit the filetypes down on save https://answers.microsoft.com/en-us/msoffice/forum/msoffice_word-mso_other/how-to-set-path-for-wddialogfilesaveas-dialog/535b7f9c-9972-425c-8483-35387a97d61d
Towards the bottom, Microsoft says that SaveAs isn't compatible with filter.clear and filter.add https://msdn.microsoft.com/en-us/library/office/aa219834(v=office.11).aspx
In order to hijack the native "SaveAs" dialog in Word, you need to lever the Application-level event for DocumentBeforeSave, and then call the FileDialog manually, in order to validate the extension.
1. Create a standard code module and name it modEventHandler. Put the following code in it.
Option Explicit
Public TrapFlag As Boolean
Public cWordObject As New cEventClass
'You may not need these in a DOCM, but I needed to implement this in an ADD-IN
Sub TrapEvents()
If TrapFlag Then
Exit Sub
End If
Set cWordObject.DOCEvent = Application
TrapFlag = True
End Sub
Sub ReleaseTrap()
If TrapFlag Then
Set cWordObject.DOCEvent = Nothing
Set cWordObject = Nothing
TrapFlag = False
End If
End Sub
2. Create a Class Module called cEventClass and put this code in the module:
Option Explicit
Public WithEvents DOCEvent As Application
Private Sub DOCEvent_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
' Do not prevent SAVEAS for *other* documents
If ObjPtr(Doc) <> ObjPtr(ThisDocument) Then
Exit Sub
End If
If SaveAsUI Then
' The user has invoked SAVE AS command , so we will hijack this and use our own FileDialog
Call CustomSaveAs(Doc)
' Prevent duplicate appearance of the SAVEAS FileDialog
Cancel = True
End If
End Sub
Private Sub CustomSaveAs(ByRef Doc As Document)
Dim fd As FileDialog
Dim filename$
Set fd = Application.FileDialog(msoFileDialogSaveAs)
fd.Show
If fd.SelectedItems.Count = 0 Then Exit Sub
filename = fd.SelectedItems(1)
If Not Right(filename, 4) = "docm" Then
' ### DO NOT EXECUTE this dialog unless it matches our .DOCM file extension
MsgBox "This document should only be saved as a DOCM / Macro-Enabled Document", vbCritical, "NOT SAVED!"
Else
fd.Execute
End If
End Sub
3. In ThisDocument module, do the following code:
Private Sub Document_Close()
Call modEventHandler.ReleaseTrap
End Sub
Private Sub Document_Open()
Call modEventHandler.TrapEvents
End Sub
How Does This Work?
ThisDocument raises the Document_Open event which calls on the TrapEvents procedure.
TrapEvents procedure creates a WithEvents instance of Word.Application class, exposing additional events to automation. One of these is DocumentBeforeSave.
We use the DocumentBeforeSave event to trap the SaveAs operation. If the User has requested a SaveAs, then we force the dialog with logic as created in CustomSaveAs procedure. This uses simple logic to test the file extension provided, and prevents the document from being saved if it is not a DOCM extension. If the FileDialog does receive a valid DOCM extension, then we Execute the dialog which saves the file as the new name.
How to make Microsoft Word to run a VBA macro every time before any document is saved? Could it be done without adding macros into the document itself?
You can subscribe to application events in Document_Open by using WithEvents variable and conventional method names (VariableName_EventName). Works in templates as well.
You can put this code into ThisDocument object, or make a separate class module as described here.
Private WithEvents App As Word.Application
Private Sub Document_Open()
Set App = Word.Application
End Sub
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
MsgBox("BeforeSave")
End Sub
List of all application events.
You must add the code bits at the correct place.
This must be at the top of your code page amongst your public variables or constant declerations
Private WithEvents App As Word.Application
Then add this as an open document event.
Private Sub Document_Open()
Set App = Word.Application
End Sub
This is the event that fires on save command Ctrl+s or save icon. I added my own save format and print as I saw It most useful in the case of people filling out forms and you don't want them to overwrite the initial template.
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
''save file with the saveas2 command.
ActiveDocument.SaveAs2 FileName:="YourDocumentNameORVariable" + _
" Date_" + Format(Now(), "yy-mm-dd"), _
FileFormat:=wdFormatDocumentDefault, _
SaveFormsData:=True
''addition to print file upon save
ActiveDocument.PrintOut Background:=True, Range:=wdPrintAllDocument, Copies:=1, Collate:=True
End Sub
Read more about Printout methods: Microsoft VBA - PrintOut
Read more about SaveAs2: Microsoft VBA - SaveAs2
Read more about FileFormat for Saving: Microsoft VBA - FileFormat for Saving
try saving your file in .xlsm, then close, open and save it again. it should work fine.
I have a VBA addin which I want to run every time someone opens a document. It's working fine for opening existing documents (AutoOpen) and creating new documents from the File > New menu (AutoNew) but when I just open Word up for the first time, neither of these events are firing. The only event I can seem to hook into is the AutoExec event and this is not great as the document doesn't exist yet so ActiveWindow is null.
Can anyone help?
Sub AutoNew
MsgBox "New"
End Sub
Sub AutoOpen
MsgBox "Open"
End Sub
Sub AutoExec
MsgBox "Exec"
End Sub
I would start with DocumentOpen and NewDocument. An additional level of complexity exists if you need to support ProtectedView documents; Word triggers a different event. I've found that if I try to check for that event (and it doesn't occur) it raises an error. I didn't had much luck and eventually it wasn't worth the time I was spending. I've posted an example below of some code that opens the Style Pane when a document is opened or a new one created (assuming the add-in is loading) and expands the style margin in draft view if not already expanded.
In my UI module:
Dim X As New clsAppEvent 'This is in the declarations
Public Sub OnRibbonLoad(objRibbon As IRibbonUI)
Do While Documents.Count = 0
DoEvents
Loop ' I find this useful as sometimes it seems my ribbon loads before the document.
Call Register_Event_Handler
' Other stuff
End Sub
Private Sub Register_Event_Handler()
Set X.App = Word.Application
End Sub
Then, in a class module I call clsAppEvent:
Option Explicit
Public WithEvents App As Word.Application
Private Sub App_DocumentOpen(ByVal Doc As Document)
App.TaskPanes(wdTaskPaneFormatting).visible = True
End Sub
Private Sub App_NewDocument(ByVal Doc As Document)
App.TaskPanes(wdTaskPaneFormatting).visible = True
End Sub
Private Sub App_WindowActivate(ByVal Doc As Document, ByVal Wn As Window)
If Wn.StyleAreaWidth <= 0 Then
Wn.StyleAreaWidth = 60
End If
End Sub
Other than the caveats I've noted above, the one problem I've had is if a user has Auto code in their Normal template as well. This has only come up once so I haven't investigated it.
I wish I could find the site where I learned about this (and from which the Register_Event_Handler was derived. If I find it I'll add a comment.