Using Events with the Application Object in MS Word - vba

Ms Word does not want to load my add-in. I want to call a userform on print event. Here is my code:
in module 1
Option Explicit
Private Sub App_DocumentBeforePrint(ByVal Doc As Document, Cancel As Boolean)
'Debug.Print Now & " " & "App_DocumentBeforePrint: " & Wb.FullName
Userform1.Show
End Sub
Sub InitializeApp()
Dim X As New EventClassModule
Set X.App = Word.Application
End Sub
in Document module
Private Sub Document_Open()
Call InitializeApp
End Sub
in EventClassModule
Public WithEvents App As Word.Application
in Userform1 Mode
Option Explicit
Private Sub UserForm1_Initialize()
End Sub
I used this 2 links to help me write this code
1) https://msdn.microsoft.com/en-us/library/bb221264%28v=office.12%29.aspx
2) https://msdn.microsoft.com/en-us/library/gg597509%28v=office.14%29.aspx
Can anyone tell why my code does not work?

As explained in the first link you show, the procedure App_DocumentBeforePrint needs to be in the CLASS module (EventClassModule, in your explanation), not in Module 1.
Other than that, it's not clear what you mean by "my add-in". Usually, I'd think of a template (or COM add-in) when this term is used that's being loaded as an add-in. I'm concerned whether Document_Open is actually being triggered to initialize your events. This event, in the ThisDocument module (in reality, it's a class) will only fire when the document containing this code is opened...

For anybody else who comes across this thread like I did, this is what worked for me:
in module 1
(your module shouldn't contain the event-based sub; also, X needs to be declared as a global variable rather than within the 'InitializeApp' sub)
Option Explicit
Dim X As New EventClassModule
Sub InitializeApp()
Set X.App = Word.Application
End Sub
in Document module
Private Sub Document_Open()
Call InitializeApp
End Sub
in EventClassModule
(your Class Module should contain the event-based sub)
Public WithEvents App As Word.Application
Private Sub App_DocumentBeforePrint(ByVal Doc As Document, Cancel As Boolean)
'''Your procedure here
End Sub

Related

Is there a VBA event triggered when a Word document is closed?

I'm trying to use Normal.dotm as a macro storage object analogous to the Personal.xlsb object within Excel.
The built-in Document_Close() event seems like it cannot be triggered unless it's included within a specific document's ThisDocument object.
I've also tried to use this Application_Quit() event like so but to no avail:
Private Sub Application_Quit()
Msgbox "closing word"
End Sub
Is it possible to listen for closing of the word application like it is in excel with Auto_Close(), etc?
Attempt for #BigBen based on this documentation
Class Module "Event Class Module"
Public WithEvents App As Word.Application
Normal Module "Module 1"
Dim X As New Class1
Sub Register_Event_Handler()
Set X.App = Word.Application
End Sub
Private Sub App_Quit()
MsgBox "closing word"
End Sub
There are two basic ways to capture when any Word document closes:
Use a macro named AutoClose in any module of Normal.dotm (or any template loaded as an add-in). This is the "old-fashioned" way that comes from the WordBasic (Word 2.0 and 6.0) days.
Note that if any other document (or template) has AutoClose that this will over-ride the macro running a "more general" level. In other words, the document- (or template-) specific code takes priority.
Sub AutoClose()
MsgBox "The document " & ActiveDocument.FullName & " is closing."
End Sub
Work with an application-level event. This requires both a class module and a "plain" module. The event code is in the class module; the code to initialize the class must be in a "plain" module.
This event's name is DocumentBeforeClose and can be cancelled, both by the user and by code (see the Cancel parameter in the event signature).
In contrast to AutoClose, if more than one document or template has the event running all will fire - there is no priority or "override".
Class module named clsEvents
Public WithEvents app As Word.Application
Private Sub app_DocumentBeforeClose(ByVal Doc As Document, Cancel As Boolean)
MsgBox "The document " & Doc.FullName & " is being closed."
End Sub
Module
Public myEvents As New clsEvents
Public Sub StartEvents()
Set myEvents.app = Word.Application
End Sub
Public Sub EndEvents()
Set myEvents.app = Nothing
End Sub
Note that if both types of event are present the AutoClose fires after DocumentBeforeClose.
Note also that there is a document-specific event that will fire only for that document: Document_Close. This event must be in the ThisDocument class module of that document.
Private Sub Document_Close()
End Sub
Here are some codes I used to raise an event when the protected Word document is being closed. The purpose is to send a message box BEFORE the preview document is CLOSED. User is able to abort the closing event, modify the document and close the document again. The simpler Word document Document_Close() event handler does not support CANCEL = TRUE.
Step 1. Add following code into a CLASS (codes must be in class module). I named the class as 'EventClassModule', I made a public declaration 'App', these names are referenced in the module.
Public WithEvents App As Word.Application
Private Sub App_DocumentBeforeClose(ByVal Doc As Document, Cancel As Boolean)
If vbYes = MsgBox("Do you need to modify the certificate?", vbYesNo) Then
If ActiveDocument.ProtectionType <> wdNoProtection Then
ActiveDocument.Unprotect
End If
Cancel = True
End If
End Sub
Step 2: Add following code into a module. The class name 'EventClassModule' and the public declaration 'App' are referenced here. The code 'Set X.App = Word.Application' will point to the Word application object, and the event procedures in the class module will run when the events occur.
Dim X As New EventClassModule
Sub abc()
Set X.App = Word.Application
Dim Doc As Word.Document
Set Doc = ActiveDocument
If Doc.ProtectionType <> wdNoProtection Then
Doc.Unprotect
End If
ActiveDocument.Range.Text = Time
Doc.Protect wdAllowOnlyReading
End Sub

word 2003 vba bug? DocumentBeforePrint execute twice?

Is this a Word 2003 VBA bug? DocumentBeforePrint executes multiple times?
I reference
http://msdn.microsoft.com/en-us/library/office/aa211873(v=office.11).aspx
http://msdn.microsoft.com/en-us/library/office/aa211915(v=office.11).aspx
DocumentBeforeClose syntax
I make a test.dot with macro DocumentBeforePrint.
'ref http://msdn.microsoft.com/en-us/library/office/aa211915(v=office.11).aspx
Dim X As New EventClassModule
Sub Register_Event_Handler()
Set X.appWord = Word.Application
End Sub
Private Sub Document_New()
Call Register_Event_Handler
End Sub
Private Sub Document_Open()
Call Register_Event_Handler
End Sub
'ref http://msdn.microsoft.com/en-us/library/office/aa211873(v=office.11).aspx
Public WithEvents appWord As Word.Application
Private Sub appWord_DocumentBeforePrint _
(ByVal Doc As Document, _
Cancel As Boolean)
MsgBox "WORKING!"
End Sub
When I press Ctrl+P, the macro executes.
There is a bug. When I double click test.dot to generate two word files (a.doc/b.doc).
Press Ctrl+P,the DocumentBeforePrint will execute twice.
If I generate 3 word files, Press Ctrl+P, the Macro will execute three times.
What's wrong? I just want to execude once.
It appears you have put all the code except the printing code in the ThisDocument code module. This is incorrect. Because you put ^Dim X as NewEventClassModuleinThisDocument`, which is, itself, a class module representing each document, multiple instances were being created. Declaring it in a "plain module" doesn't cause this problem.
You need three modules:
"Plain" module:
Dim X As New EventClassModule
Sub Register_Event_Handler()
Set X.appWord = Word.Application
End Sub
"ThisDocument" module:
Private Sub Document_New()
Register_Event_Handler
End Sub
Private Sub Document_Open()
Register_Event_Handler
End Sub
"EventClassModule"
Public WithEvents appWord As Word.Application
Private Sub appWord_DocumentBeforePrint _
(ByVal Doc As Document, _
Cancel As Boolean)
MsgBox "WORKING!"
End Sub

Outlook events not firing

I'm trying to send an email based on a calendar reminder going off.
I'm having trouble getting VBA macros to recognize that an Outlook event has occurred.
I put this code in a Class Module:
Public WithEvents myOlApp As Outlook.Application
Sub Initialize_handler()
Set myOlApp = Outlook.Application 'also tried with double quotes around "Outlook.Application"
End Sub
Private Sub myOlApp_Reminder(ByVal Item As Object)
MsgBox ("test")
End Sub
Private Sub myOlApp_NewMail()
MsgBox ("test")
End Sub
When I get a new email or a reminder goes off, nothing happens.
I've tested with this macro in a normal module and it works:
Sub MsgBoxTest()
MsgBox ("test")
End Sub
I have macro settings on "Enable all macros" in the Trust Center.
I've searched google, stackoverflow, a bunch of other websites, and read the documentation on Microsoft.com.
I'm on Outlook 2016 on a PC running Windows 10 Enterprise.
A class module is just a blueprint for an object. A class module doesn't exist all by itself, at runtime a class module is just a type that an object variable can be declared as.
Your code is fine (leaked public field aside).
You're just missing an instance of that class. Keep the class and make ThisOutlookSession create an instance of it:
'[ThisOutlookSession]
Option Explicit
Private AppEvents As AppEventsHandler
Private Sub Application_Startup()
Set AppEvents = New AppEventsHandler
End Sub
Private Sub Application_Quit()
Set AppEvents = Nothing
End Sub
VBA classes fire an Initialize event on creation, and a Terminate event on destruction. Handle them to set your Private WithEvents field:
'[AppEventsHandler] (class module)
Option Explicit
Private WithEvents app As Outlook.Application
Private Sub Class_Initialize()
Set app = Outlook.Application
End Sub
Private Sub Class_Terminate()
Set app = Nothing
End Sub
Private Sub app_NewMail()
'TODO handle app event
End Sub
Private Sub app_Reminder(ByVal Item As Object)
'TODO handle app event
End Sub
'...more handlers...
That's it - now you're handling Outlook.Application events in a dedicated class, without polluting ThisOutlookSession with the nitty-gritty details of every event handler out there.
For this method, often used in documentation, run Initialize_handler manually or run it at startup in the special class module ThisOutlookSession.
Private Sub Application_Startup()
Initialize_handler
End Sub
In order to handle Reminder events, you need to enclose your code in a Sub named "Application_Reminder"
Try this:
Option Explicit
Private Sub Application_Reminder(ByVal Item As Object)
MsgBox "Test"
End Sub

How to schedule an async macro from WORD

I'm facing an annoying issue where I simply want to schedule an asynchronous macro from another instance of Word, which happens to be the same .doc file.
Meaning, in the ThisDocument namespace I have the following code snippet:
Public Sub Document_Open()
Set Obj = New Word.Application
Obj.OnTime Now + TimeValue("00:00:01"), "Module1.Test"
End Sub
I've declared a new object of Word due to the following reasons:
My macro may block user's I\O
The user may close the document before the macro finishes its task
And declared a module named Module1 with a simple MsgBox
Public Sub Test()
MsgBox "hhh"
End Sub
Needless to say, nothing happened, and I'm unable to check what OnTime function returns.
I've also tried the following combinations:
"!Module1.Test"
"c:\\....\\file.doc!Module1.Test"
What am I missing here?
Your newly created Word application, represented by a Word.Application object, doesn't have the document open. Thus there's no "Module1" as far as he's aware. The fact that you want to run code from the same document is immaterial. It's a different instance of Word.
Something like this works:
'''''''' ThisDocument
Option Explicit
Private Sub Document_Open()
Dim res As VbMsgBoxResult
Dim Obj As Word.Application
If Application.Visible Then
res = MsgBox("Hello", vbOKCancel, "Hi!")
Else
Exit Sub
End If
If res = vbOK Then
Set Obj = New Word.Application
Obj.Documents.Open "C:\Users\conio\Desktop\Hello.docm", , True
Obj.OnTime Now + TimeValue("00:00:01"), "Module1.Foo"
End If
End Sub
'''''''' Module1
Public Sub Foo()
If Not Application.Visible Then
MsgBox "Foo"
Application.Quit
End If
End Sub
Maybe you'd want to use a different check to differentiate between the interactive run and the unattended run.

VBA: Global Variable Cleared after 'Workbook_Open' sub

I have set up an application level event class to monitor when new workbooks are created/ opened by following CPearson's guide. This works fine in isolation. However, it is intended as part of add-in I'm writing where several other subs are also called in the 'Workbook_Open' sub, see below code:
Private XLApp As baseCXlEvents
Private Sub Workbook_Open()
Set XLApp = New baseCXlEvents
test
AddLocalReferences
AddModules
AddClassModules
Debug.Print "testing"
End Sub
So the XLApp variable is called in the module scope as a baseCXlEvents class. I have added a Class_Terminate event to this class and this is triggered after the Debug.print "testing" is run, i.e. XLApp is terminated after the Workbook_Open sub has run. This does not happen when I quote out the subs AddLocalReferences, AddModules, and AddClassModules, which do exactly as their names would imply. The sub test only prints a messages in debug to test whether calling additional subs caused XLApp to be terminated.
My current 'hunch' is that adding references, modules, or class modules counts as "editing", causing it to be terminated, as explained in this MS Support document. But, if so, why doesn't XLApp get terminated until the end of the sub? As opposed to as soon as AddLocalReferences is run.
Any suggestions why the class is terminated? I need it to 'stay alive' and also need to load additional modules and references for the addin upon workbook_open. If needed more details of this code can be provided.
I've decided to add my baseCXlEvents class module's code:
Option Explicit
Private WithEvents App As Application
Private Sub App_NewWorkbook(ByVal Wb As Workbook)
MsgBox "New Workbook: " & Wb.Name
End Sub
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
MsgBox "Workbook opened: " & Wb.Name
End Sub
Private Sub Class_Initialize()
Debug.Print "Initializing baseCXlEvents instance.."
Set App = Application
End Sub
Private Sub Class_Terminate()
Debug.Print "Terminating baseCXlEvents instance.."
End Sub
Well then try to separate the Workbook_Open event handler stuff (where you add references etc.) from the creation of the instance of the class baseCXlEvents using Auto_Open.
Workbook_Open runs first, Auto_Open runs then.
Note: baseCXlEvents Instancing must be set to Public.
ThisWorkbook class
Public XLApp As baseCXlEvents
Private Sub Workbook_Open()
Test
AddLocalReferences
AddModules
AddClassModules
End Sub
Standard module
Sub Auto_Open()
Set ThisWorkbook.XLApp = New baseCXlEvents
End Sub