I have Access Macros that run a series of queries, form a tables, and send those tables to multiple recipients. This works fine, but I'm being increasingly asked to supply an increasing number of reports on a set day of the week.
Rather than use my calendar to remind me to open Access and run these Macros, can I run VBA in Outlook to run an Access macro on a certain day?
Rather than use my calendar to remind me to open Access and run these Macros, can I run VBA in Outlook to run an Access macro on a certain day?
There are ways you can do this with .BAT files and windows task scheduler, but you can also do it with Outlook VBA. I have a few calendar events which have this exact purpose, to execute code and clear the reminder.
First you need to add an event handler to fire when your reminders get set.
Private WithEvents olRemind As Outlook.Reminders
You then need to initialize the WithEvents. I do this everytime a reminder happens because I sometimes break code or hard stop, which loses the event handler.
Private Sub Application_Reminder(ByVal Item As Object)
Set olRemind = Outlook.Reminders
End Sub
Then, you want to process the event on your calendar. The way I have mine setup is I add a specific category for the event I want to fire. This helps make my calendar clearer. You can do this in a variety of ways.
I also then dismiss the reminder so it doesn't show up.
'fire off automatic macros based on recurring reminders
Private Sub olRemind_BeforeReminderShow(Cancel As Boolean)
Dim objRem As Reminder
For Each objRem In olRemind
'get categories
Dim rmdrCategories As String
rmdrCategories = objRem.Item.categories
'call the macro based on category
If InStr(rmdrCategories, "whateverYouWantTheReminder") > 0 Then
'only run if this reminder is visible
If objRem.IsVisible Then
'This code is specific to whatever macro you want to run
Dim mydb As Object
Set mydb = GetObject("...pathToDatabase.mdb")
mydb.Application.Run "YourMacroName"
mydb.Application.Quit
Set mydb = Nothing
objRem.Dismiss
Cancel = True
End If
End If
Next objRem
End Sub
Related
I use Task Scheduler to manage a VBScript which starts a vba sub procedure in Workbook X. This procedure opens two other Workbooks and move data to Workbook X and closes them again. The procedure outcome is an updated Workbook X which I need to save. The VBA procedure works fine and is triggered by the VBScript as expected. However, the part of the VBScript which should save the file doesn't work. The VBScript is based on code I have found on Stackoverflow.com and other sites.
When I open the file X I tried to save with the VBScript, It asks me to decide whether I want to save the file. If I do, I can see the data the above procedure has created has worked.
I can see in the Task Manager that the Excel instance I have created with the VBScript is never closed although I tried to do it in the VBScript. I have also tried the Save method, but with no luck. I have tried different parameters for the methods with no luck. I have search on this site and other sites for how the issue could be solved. However, they suggest Save and SaveAs methods and I tried them with the different suggested parameter configurations without success.
I have now found the root cause but not the solution. It has nothing to do with the script itself. In the open workbook event.
I have code which activate a userform (StartUpForm.Show). In the workbook before close and open events I call procedure RemoveFilter which remove a filter on a set of filtered data. If I mark UserForm.Show and the RemoveFilter in the before close workbook event as notation it works. It is not enough to avoid call StartUpForm.Show. StartUpForm.Show needs to be marked as notation or deleted. How can that be? And is there a workaround?
This doesn't work:
Private Sub Workbook_Open()
Dim batRun As Boolean
RemoveFilter
ResolveStartUp (batRun)
If Not batRun Then
StartUpForm.Show vbModal
End If
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
RemoveFilter
End Sub
This works:
Private Sub Workbook_Open()
Dim batRun As Boolean
RemoveFilter
ResolveStartUp (batRun)
If Not batRun Then
' StartUpForm.Show vbModal
End If
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
' RemoveFilter
End Sub
VBScript:
On Error Resume Next
ImportAccesFilesToHolidaysAndWorkshops
Sub ImportAccesFilesToHolidaysAndWorkshops()
Dim xlApp
Dim xlBook
Set xlApp = CreateObject("Excel.Application")
xlApp.DisplayAlerts = False
Set xlBook = xlApp.Workbooks.Open ("Z:\ResourceManagement\Holiday and workshops input.xlsm", 0, False,,,,,,,,True)
xlApp.Run "ImportResourcesAndProjects"
xlApp.ActiveWorkbook.Close
xlBook.SaveAs "Z:\ResourceManagement\Holiday and workshops input.xlsm",,,,,,xlExclusive, xlLocalSessionChanges
xlBook.Close
Set xlBook = Nothing
xlApp.Quit
Set xlApp = Nothing
WScript.Echo "Finished."
WScript.Quit
End Sub
Try saving to your C: drive. If that works you are probably running into a scheduled task problem. If the task runs whether or not a user is logged in or it is set to use highest privileges, you aren't running under the security context you think you are. In either of those cases job scheduler will use S4U authentication which will prevent you from accessing network resources. I'm guessing that Z: drive is a mapped network drive. Mapped drives belong to the user who mapped the drive so it won't be available unless you are running as the user that mapped the drive and that user is logged in.
If you use "run with highest privileges", you are actually running under the built in Administrator account. That is a separate account and it won't have your drive mappings.
Task Security Context
Some Programs Cannot Access Network Locations When UAC Is Enabled
windows 7 scheduled tasks run with highest privileges
I am looking for a way to automatically turn on Snap to Grid every time I start Excel.
I worked out this code and put it in an Add-In (.xlam) that I always load on Excel start-up.
Private Sub Workbook_Open()
Dim cbc As CommandBarControl
Set cbc = Application.CommandBars.FindControl(ID:=549)
If Not cbc.Enabled Then cbc.Execute
End Sub
But when I start Excel, it throws this error at cbc.Execute:
Can anyone tell me what is wrong?
I think you have two problems:
Problem 1
I believe that what you are trying to do with the Enabled property is to check the toggle state of the control. Instead, I think you want the State property instead (which doesn't show in intellisense). Your code should be something like this:
Public Sub ActivateSnapToGrid()
Dim cbc As CommandBarControl
Set cbc = Application.CommandBars.FindControl(ID:=549)
If Not cbc Is Nothing Then
If cbc.Enabled Then
'if snap to grid is off...
If cbc.State = 0 Then
cbc.Execute
'State should now = -1
End If
End If
End If
End Sub
Problem 2
The 'Snap to grid' control is not enabled if there is no workbook present (check this by closing all workbooks). In its current state, your code tries to execute in this case i.e. If Not cbc.Enabled Then cbc.Execute because I think you are trying to check if it is 'on' not if it is enabled.
Because an Excel add-in will load as a 'hidden' workbook, I don't believe it would enable the 'Snap to Grid' command bar control. Therefore, in the Workbook_Open event of the add-in then the control will be disabled and that's why you get the error.
You need an application-level event handler in your add-in. This is very common for Excel add-ins. See here on MSDN for some explanation. Plus also see this article by Chip Pearson which is very useful. It will allow you to write an event for any Workbook_Open event generated after your add-in loads.
So you will end up with this code (per Chip Pearson) in your add-in to call the sub I presented above (in Problem 1 section):
Private WithEvents App As Application
Private Sub Workbook_Open()
Set App = Application
End Sub
Private Sub App_NewWorkbook(ByVal Wb As Workbook)
Debug.Print "New Workbook: " & Wb.Name
Call ActivateSnapToGrid
End Sub
I haven't tested this thoroughly as it can't be known how your wrote your add-in. However, this is a very (if not the) standard pattern for doing this kind of stuff. See MSDN and the Chip Pearson article and you will figure out which code goes in a Class, in a Module etc.
I want to block out some of the command bar options, but the method I am currently using is a bit too restrictive. I currently use this code:
Sub Auto_Open()
Application.CommandBars.FindControl(ID:=847).Enabled = False
End Sub
Sub Auto_Close()
Application.CommandBars.FindControl(ID:=847).Enabled = True
End Sub
The problem with this is that as long as the workbook that has this code stays open no other workbook can use voided controls. I think a solution might be to use:
Private Sub Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
Application.CommandBars.FindControl(ID:=847).Enabled = False
End Sub
This will stop the user from using my voided controls when they right click, say to delete a sheet.
I now need a way to remove those voids as soon as they click away, so the controls are available to use in any other workbooks they may have open. Is there an event that I can use for this which isn't so common that it will make the code run every 10 seconds? I also wondered whether you can use delays in vba to run a piece of code 5 seconds after a previous piece of code?
In the workbook module in which you want to disable the command bar options enter:
Private Sub Workbook_Activate()
Application.CommandBars.FindControl(ID:=847).Enabled = False
End Sub
Private Sub Workbook_Deactivate()
Application.CommandBars.FindControl(ID:=847).Enabled = True
End Sub
Whenever you are working in the workbook the commands should be disabled. I'm not certain it will work in Excel 2007 because Application.CommandBars.FindControl(ID:=847).Caption is &Delete and Delete is still enabled on both the cell right click and tab right click.
In Excel 2007 you can disable Ribbon commands for the active workbook but it's not easy. You have to change the workbook extension to .zip then edit the .rels file in _rels folder within the zip. Change the extension back to what it was after editing and the targeted commands or groups should be disabled. Disabling the Ribbon command also disables any associated right-click commands. The kicker is that sheet add/delete commands aren't available in the Ribbon.
My question is: using VBA in Excel 2013 how can I gracefully close an entire instance of Excel when the user decides they don't want to fill out a UserForm and clicks quit or cancel?
Currently, if the user clicks quit or cancel, I check to see if my instance is the only one open. If it is not, I can use ThisWorkbook.Close and I think I will be okay. However, if it is, I do not want the application to still be present, so I used Application.Quit. This, though, tries to finish running the macro, throws errors (originally "type mismatch" because I unload the form), and only closes after I click "Debug" or "End" (which it does so fast for either I cannot actually debug). I'm ignoring the first case for now and just trying to exit the entire application. It's a very long macro with a lot of subroutines and functions, so for debugging and posting here, I have shortened it. The type mismatch error no longer occurs, but I believe that was a consequence of the actual error: code running after the command to close the application is called.
First, here's the code that starts everything:
Private Sub CommandButton1_Click()
Call form_variables
frm_REQUEST.Show
Call a_REQUEST_main
End Sub
The subroutine
form_variables
is a subroutine that creates public variables so I can store the data from the UserForm.
frm_REQUEST.Show
initializes (including calling a function that finds another workbook, extracts a list, does some formatting, closes the workbook and enters the list into the userforms drop down box) and shows the form, and finally
a_REQUEST_main
uses the public variables (where UserForm data is stored) and does its thing (but shouldn't do anything if the UserForm is closed).
The code that is executed when .Show is called is:
Private Sub UserForm_Initialize()
' Get job numbers from other workbook
Dim job_selection_list As Variant
job_selection_list = get_job_list()
With frm_REQUEST.Job_Number_ComboBox
.List = job_selection_list
End With
' set focus on Job Numbers
JN_combobox.SetFocus
End Sub
Private Sub cancel_button_Click()
Set job_selection_list = Nothing
Unload Me
Application.Quit
End Sub
Private Sub submit_button_Click()
' Values from userform saved as global (?) variables so other subroutines can access.
End Sub
I stepped through the program and saw that, once Application.Quit is called in the UserForm, the macro, in the main subroutine, steps to
Call a_REQUEST_main
but it should really just close everything out. I tried doing "save" commands, and changing the order of things, and read about objects needing to be set to nothing (hence the setting of the job_selection_list which is created when the drop down list is initialized), but I cannot seem to get this to work, or find anything online. Can anyone provide some guidance or let me know of a better way to close an excel instance? Help me Stack-Overflow Kenobi, you're my only hope!
Thanks.
Just add a variable to account for when the user closes the form
in the form
'hold flag if users cancels form
Public btnCancel As Boolean
Private Sub CommandButton1_Click()
Unload Me
btnCancel = True
End Sub
'set the flag each time the form active
Private Sub UserForm_Activate()
btnCancel = False
End Sub
then in your code
Call form_variables
frm_REQUEST.Show
If frm_REQUEST.btnCancel Then
Application.Quit
Else
Call a_REQUEST_main
End If
Put Application.Quit in the form's Terminate event handler instead of in the button's Click event handler.
The reason is that clearly the procedure will keep running even if the form has unloaded. So use the events to your advantage.
Putting it in the Click event will unload the form, but the procedure will keep running which of course may raise errors or other undesired effects.
Note: You may be prompted to save/discard changes (if any) to the workbook.
I use Outlook (MS Exchange) and have an individual as well as two group inboxes (I'm working logged in with the individual profile through which I also have access to the group inboxes).
When I send an email, I chose either my individual or one of the two group email addresses in the From field. When the email is sent, I want a copy saved in the inbox of myIndividualMailbox, groupAMailbox, or groupBMailbox depending on which From email address I used.
Example: If I send an email From groupA#myCompany.com, I want a copy of the email saved in the inbox of the groupAMailbox (and not in my individual inbox).
I have understood that this is not possible by setting up a rule in Outlook but that it could be done with a VBA macro. I don't now how to write the VBA macro and don't know if this is a just a short script or more complicated. In fact I have never written a macro in Outlook so I don't even know how to begin. Can anyone show how to do this?
I started looking for a solution with this question: Outlook send-rule that filter on the 'From' field
I made this for you as far as I can tell, it works. You should put this in the Microsoft Outlook Objects - ThisOutlookSession Module.
Note that the myolApp_ItemSend event will never trigger unless you run enableEvents first. And you will need to make sure it is enabled every time you close an re-open Outlook. This will take some customization, but it should give you the general idea.
Option Explicit
Public WithEvents myolApp As Outlook.Application
Sub enableEvents()
Set myolApp = Outlook.Application
End Sub
Private Sub myolApp_ItemSend(ByVal item As Object, Cancel As Boolean)
Dim items As MailItem
Dim copyFolder As Outlook.Folder
Dim sentWith As String
'Identify sender address
If item.Sender Is Nothing Then
sentWith = item.SendUsingAccount.SmtpAddress
Else
sentWith = item.Sender.Address
End If
'Determin copy folder based on sendAddress
Select Case sentWith
Case "groupA#myCompany.com"
'get groupAMailbox's inbox
Set copyFolder = Application.GetNamespace("MAPI").folders("groupAMailbox").folders("Inbox")
Case "myE-mailAddress"
'get My inbox
Set copyFolder = Application.GetNamespace("MAPI").folders("myE-mailAddress").folders("Inbox")
End Select
'copy the Item
Dim copy As Object
Set copy = item.copy
'move copy to folder
copy.Move copyFolder
End Sub
EDIT: It looks like they've actually built the event functionality into the Application object for Outlook directly now, but it from testing you still have to do what I outlined above.
Outlook stores all sent items in default sent items folders. however you can apply a patch to save sent items in its own folder.
http://support.microsoft.com/kb/2181579