This SO answer explains how to refresh a Microsoft Outlook mailbox in Powershell using $mapi.SendAndReceive()
I would like to extend the question with:
How can I use Powershell to refresh a specific mailbox in Outlook? For example, if there are 5 mailboxes and I only wish to refresh one.
How can I get Powershell to wait for SendAndReceive() to complete? According to the documentation it is an asynchronous method.
Accepted solution must be in Powershell script.
How can I get Powershell to wait for SendAndReceive() to complete
The SyncEnd event of the SyncObject class is fired immediately after Microsoft Outlook finishes synchronizing a user's folders using the specified Send/Receive group.
Dim WithEvents mySync As Outlook.SyncObject
Sub Initialize_handler()
Set mySync = Application.Session.SyncObjects.Item(1)
mySync.Start
End Sub
Private Sub mySync_SyncEnd()
MsgBox "Synchronization is complete."
End Sub
How can I refresh a specific mailbox in Outlook?
A Send\Receive group lets users configure different synchronization scenarios, selecting which folders and which filters apply.
Use the Item method to retrieve the SyncObject object from a SyncObjects object. Because the Name property is the default property of the SyncObject object, you can identify the group by name.
The SyncObject object is read-only; you cannot change its properties or create new ones. However, note that you can add one Send/Receive group using the SyncObjects.AppFolders property which will create a Send/Receive group called Application Folders.
The Start method of the SyncObject class begins synchronizing a user's folders using the specified Send\Receive group.
Public Sub Sync()
Dim nsp As Outlook.NameSpace
Dim sycs As Outlook.SyncObjects
Dim syc As Outlook.SyncObject
Dim i As Integer
Dim strPrompt As Integer
Set nsp = Application.GetNamespace("MAPI")
Set sycs = nsp.SyncObjects
For i = 1 To sycs.Count
Set syc = sycs.Item(i)
strPrompt = MsgBox( _
"Do you wish to synchronize " & syc.Name &"?", vbYesNo)
If strPrompt = vbYes Then
syc.Start
End If
Next
End Sub
Related
I have emails in my Outbox in Outlook and I'd like to run a vb script to change the from field of these emails to some other email
I managed to do the following but it doesn't work as I'd like to and therefore I am missing the main piece. I'd appreciate if someone could help.
intFolderOutbox = 4
msoFileDialogOpen = 1
' Load requied objects
Set WshShell = WScript.CreateObject("WScript.Shell") ' Windows Shell
Set ObjOlApp = CreateObject("Outlook.Application") ' Outlook
Set ns = ObjOlApp.GetNamespace("MAPI") ' Outlook
Set box = ns.GetDefaultFolder(intFolderOutbox) ' Outlook
For Each Item In box.Items
*** HERE IS WHAT I NEED TO REPLACE THE FROM FIELD ****
Item.sender = "email2#gmail.com"
Item.Update
Item.Save
Next
Something like the following works adding a recipient but I couldn't find the equivalent to the from field.
Item.Recipients.Add "email2#gmail.com"
Here is something that could help but it doesn't work in my case
Set oAddrEntry = CreateObject("MAPI.AddressEntry")
oAddrEntry.Name = SYSTEM_ADDRESS
oAddrEntry.resolve
Set oNewMsg.sender = oAddrEntry
oNewMsg.Update
oNewMsg.Send
Thanks
Firstly, once a message is submitted (and moved to Outbox) it cannot be touched - it belongs to the spooler.
Secondly, you cannot send on behalf of an arbitrary user. In case of Exchange, set the MailItem.SentOnBehalfOfName property to the name of the Exchange mailbox on whose behalf the current user can send. In case of POP3/SMTP accounts, set the MailItem.SendUsingAccount property to one of the accounts from the Namespace.Accounts collection.
I have an unbound form used to enter constituent data. I can't use subforms because data is spread across multiple tables and linked with foreign keys. Since I want to use the field validation rules to validate, I use a transaction to make sure new records are committed in an all-or-nothing fashion. I don't want names committed without addresses, etc.
The form is designed to iterate over a recordset, "tempImportRs", to give the user an opportunity to make sure the data looks OK and to manually correct or skip invalid entries. It works by populating the form's fields with values from tempImportRs for each entry. There's also a subform that searches for similar records and displays them to the user to decide if this is really a new person or if they already exist in the DB with somewhat different info.
I am running into a problem using this "tempImportRs" recordset, which persists for the lifetime of the form and is used by various methods in the form, alongside Access's transactions (which I don't understand very well, admittedly). If at any point I close the transaction like
WrkSp.Close
then my tempImportRs object will disappear and give me "Requires object" type errors. If I don't close the transaction, which is bad practice anyway, my form will work like I want for up to maybe a dozen records before giving me the error "Could not start transaction; too many transactions already nested."
How can I close the transaction cleanly, without destroying "tempImportRs"?
Here is a very simplified version of the VBA code for my form:
Public tempImportRs As RecordSet
Sub TryToAddRecords()
'Try to add the data from the now-populated form fields to my tables'
On Error GoTo Error_TryToAddRecords
Dim WrkSp As Workspace
Set WrkSp = DBEngine.Workspaces(0)
WrkSp.BeginTrans
'In my real code I use several recordsets to store the imported data'
'but I am simplifying here by using just one:'
Dim someOtherRs As Recordset
Set someOtherRs = CurrentDb.OpenRecordset("tblNamesAddressesEtc")
'...Do stuff using several other recordsets and '
'the form fields populated from tempImportRs'
someOtherRs.Update
WrkSp.CommitTrans
Exit_TryToAddRecords:
someOtherRs.Close
'I leave tempImportRs open for now.'
'WrkSp.Close <--This is what messes up tempImportRs'
Set someOtherRs = Nothing
Set WrkSp = Nothing
Exit Sub
Error_TryToAddRecords:
MsgBox Err.Description & vbNewLine & _
"Please fix the field and try again."
Resume Exit_TryToAddRecords
End Sub
Private Sub GoButton_click
Set tempImportRs = CurrentDb.OpenRecordset("tblTempImport")
If !tempImportRs.EOF
'<populate the form fields with the current '
'tempImportRs data here>'
TryToAddRecords
tempImportRs.MoveNext
Else
tempImportRs.close
Set tempImportRs = Nothing
MsgBox "All records imported"
End If
End Sub
This is not a problem specific to transactions and workspaces, but a more general matter of program structure. You should just make the workspace persist for the duration of the form, and commit to it multiple times. You don't need to make a new workspace for each transaction.
So, treat the workspace just like tempImportRs is treated in the code example. You can both set and close WrkSp in the GoButton_click sub. Don't set or close it in the TryToAddRecords function.
You will also have to make sure to roll back with WrkSp.Rollback if there's an error, add that line to Error_TryToAddRecords.
Public tempImportRs As RecordSet
Dim WrkSp As Workspace
'...
Private Sub GoButton_click
Set tempImportRs = CurrentDb.OpenRecordset("tblTempImport")
Set WrkSp = DBEngine.Workspaces(0) 'move this here
If !tempImportRs.EOF
'<populate the form fields with the current '
'tempImportRs data here>'
TryToAddRecords
tempImportRs.MoveNext
Else
tempImportRs.Close
Set tempImportRs = Nothing
'close the workspace here when you're done with tempImportRs
WrkSp.Close
Set WrkSp = Nothing
MsgBox "All records imported"
End If
End Sub
PS: Sorry folks for asking and answering this dumb question, I had a mental block and thought the issue was about managing workspaces and not a simpler matter of code design. Maybe this will still help someone someday.
OK, I've been out of Access programming for a couple of versions, but I could swear I used to be able to point controls at form global variables. Sample code as follows:
Option Compare Database
Option Explicit
Dim Testvar As String
Private Sub Form_Load()
Testvar = "Load"
End Sub
Private Sub Form_Open(Cancel As Integer)
Testvar = "open"
End Sub
Private Sub Text0_Click()
Testvar = "settest"
End Sub
I should be able to put a text box on the control that can see the TestVar variable, but the controls don't do it. Also, I used to be able to do that with the form's record source.
So, the questions -
Am I crazy - that was never possible?
Or have I forgotten how to address the form?
And then the most important question - what is the best way to get around this?
The most common way this is used is to pass in OpenArgs (record keys in this case) which is then parsed in to global vars and then a couple of controls display the open args and/or look up values to display from the keys.
I really hate to have to build routines that rebuild and load the record sources for the controls. Hope someone knows a better approach
In addition to your existing event procedures, you can add a function in the form module which retrieves the value of the Testvar module variable.
Function GetTestvar() As String
GetTestvar = Testvar
End Function
Then use =GetTestvar() as the Control Source for Text0.
You have to actually set the value of the text box. There's no way (to the best of my knowledge) to bind a text box to a variable.
Option Compare Database
Option Explicit
Private Sub Form_Load()
Text0.Value = "Load"
End Sub
Private Sub Form_Open(Cancel As Integer)
Text0.Value = "open"
End Sub
Private Sub Text0_Click()
Text0.Value = "settest"
End Sub
Of course, you could store the value in a variable and use it to set the value instead, but it makes little sense to do so in this simple example.
The TempVars collection is a feature introduced in Access 2007. So, if your Access version is >= 2007, you could use a TempVar to hold the string value. Then you can use the TempVar as the control source for your text box.
With =[TempVars]![Testvar] as the Control Source for Text0, the following event procedures do what you requested.
Private Sub Form_Open(Cancel As Integer)
TempVars.Add "Testvar", "Open"
End Sub
Private Sub Form_Load()
TempVars("Testvar") = "Load"
End Sub
Private Sub Text0_Click()
TempVars("Testvar") = "settest"
Me.Text0.Requery
End Sub
Note: [TempVars]![Testvar] will then be available throughout the application for the remainder of the session. If that is a problem in your situation, you could remove the TempVar at Form Close: TempVars.Remove "Testvar"
Requirement was: To show the login Id of the application user on all forms in the application.
Here is how I implemented this:
Create a module: module_init_globals
with the following code:
Option Compare Database
'Define global variables
Global GBL_LogonID as string
Option Explicit
Public Sub init_globals ()
'Initialize the global variables
'Get_Logon_Name is a function defined in another VBA module that determines the logon ID of the user
GBL_LogonID = Get_Logon_Name()
End Sub
On the main/first form - we need to call the module that will initialize the global variables:
In the code for "on open" event I have:
Private Sub Form_Open (Cancel as Integer)
call init_globals
End Sub
then on each of the forms in the app, I have a text control - txtUserID to display the logon id of the user
and I can set it's value in the "on open" event of the form.
txtUserID.value = GBL_LogonID
Is there a way to run a function in VBA the moment data in any control element changes? I've tried Form_AfterUpdate and Form_DataChange but they seem not to do anything
You do not have to code After Update/Change event of the controls, check out Key Preview
You can use the KeyPreview property to specify whether the form-level
keyboard event procedures are invoked before a control's keyboard
event procedures. Read/write Boolean.
Use it carefully.
For example, with KeyPreview on:
Private Sub Form_KeyPress(KeyAscii As Integer)
MsgBox "You pressed a key"
End Sub
Step 1: Create a function
Function DoStuff()
Call RunMySub
End Function
Step 2: Create a Macro (Named RunMyCode)
RunCode
Function Name DoStuff()
Step 3: Modify the Form_Load() sub
Private Sub Form_Load()
Dim cControl As Control
On Error Resume Next
For Each cControl In Me.Controls
if cControl.ControlType = 109 'this is for text boxes
'Depending on what your code does you can use all or some of these:
cControl.OnExit = "RunMyCode"
cControl.OnEnter = "RunMyCode"
cControl.OnLostFocus = "RunMyCode"
cControl.OnGotFocus = "RunMyCode"
If cControl.OnClick = "" Then cControl.OnClick = "RunMyCode"
end if
Next cControl
On Error GoTo 0
You can use any of the attributes from the control I find the pairs of 'OnExit/OnEnter' and 'OnLostFocus/OnGotFocus' to be the most effective. I also like 'OnClick' but I use the if statement to not overwrite actions (for buttons and stuff). There are a dozen other methods you can assign the control action to -- I'm sure you'll be able to find one/several that meet your goal.
Note -- I use the on error enclosure because I wrap this code around multiple different types of controls and not all have all of the methods.
I have a Word userform with 60+ controls of varying types. I would like to evaluate the form every time a control_change event is triggered and change the enabled state of the form's submit button. However, I really don't want to write and maintain 60 on change event handlers.
You can create an event-sink class that will contain the event-handling code for all of your controls of a particular type.
For example, create the a class called TextBoxEventHandler as follows:
Private WithEvents m_oTextBox as MSForms.TextBox
Public Property Set TextBox(ByVal oTextBox as MSForms.TextBox)
Set m_oTextBox = oTextBox
End Property
Private Sub m_oTextBox_Change()
' Do something
End Sub
Now you need to create & hook up an instance of that class for each control of the appropriate type on your form:
Private m_oCollectionOfEventHandlers As Collection
Private Sub UserForm_Initialise()
Set m_oCollectionOfEventHandlers = New Collection
Dim oControl As Control
For Each oControl In Me.Controls
If TypeName(oControl) = "TextBox" Then
Dim oEventHandler As TextBoxEventHandler
Set oEventHandler = New TextBoxEventHandler
Set oEventHandler.TextBox = oControl
m_oCollectionOfEventHandlers.Add oEventHandler
End If
Next oControl
End Sub
Note that the reason you need to add the event handler instances to a collection is simply to ensure that they remain referenced and thus don't get discarded by the garbage collector before you're finished with them.
Clearly this technique can be extended to deal with other types of control. You could either have separate event handler classes for each type, or you could use a single class that has a member variable (and associated property & event handler) for each of the control types you need to handle.
In that case you have few options, because event handlers cannot be shared in VBA/VB6
Option 1: Use a central handling function which is called from every event handler.
Sub Control1_ChangeEvent()
CommonChangeEvent // Just call the common handler, parameters as needed
End Sub
Sub Control2_ChangeEvent()
CommonChangeEvent
End Sub
...
Sub CommonChangeEvent(/* Add necessary parameters */)
//Do the heavy lifting here
End Sub
Option 2: Organize your controls in control arrays.
Sub TextBox_ChangeEvent(Index As Integer)
CommonChangeEvent
End Sub
Sub OtherControlType_ChangeEvent(Index As Integer)
CommonChangeEvent
End Sub
Combining both options your total event handler count will shrink considerably and the remaining handlers are just brainless stubs for the one true event handler.