I have an Outlook add-in that monitors sent items and moves specific emails to a folder. I am not sure what have changed, but when I send an email, I get this error:
This method can't be used with an inline response mail item.
Here is my code. It fails while moving the mail item (Mail.Move(TargetFolder)) when IsSentItem is true:
Private Sub MoveMailToFolder(ByVal TargetFolder As Outlook.Folder, ByVal Mail As Outlook.MailItem, ByVal IsSentItem As Boolean)
Try
If Mail.ReadReceiptRequested Then
''continue /keep original mail as is
Else
'Mail.UnRead = False
Mail.UnRead = True
End If
If IsSentItem Then
Mail.UnRead = False
Mail.Move(TargetFolder)
Else
Mail.Move(TargetFolder)
'Mail.SaveSentMessageFolder = TargetFolder
End If
Catch ex As Exception
MsgBox(ex.Message.ToString & " " & ex.HResult.ToString & " " & ex.GetBaseException.ToString)
Finally
''
End Try
End Sub
Can you advise how this can be fixed?
Yes, the inline response needs to be closed first. You also need to avoid calling methods like that from inline response or MailItem event handlers. In the latter case, you can start a timer (use the Timer class from the Forms namespace rather than Threading as it fires on the main thread) and call code like yours in the timer event handler (when you are out of the MailItem event handler).
To close an inline response, you can try to use Accessibility API to simulate a click on the "Discard" button or, if using Redemption (I am its author) is an option, its SafeExplorer object - it exposes ActiveInlineResponseDiscard method:
set sExplorer = CreateObject("Redemption.SafeExplorer")
sExplorer.Item = Application.ActiveExplorer
sExplorer.ActiveInlineResponseDiscard
Related
I need to automatically check incoming meeting-requests:
If the request is from someone external nothing should happen
If the request is from someone of my company (checking the E-Mail Max.Mustermann#mycompany.com) it should check whether I already have an accepted meeting in the asked timeframe and decline the request by default (plus answering).
I know basic Excel VBA but not familiar with Outlook. I tried to trigger the code with every incoming mail checking if it's a meeting request but don't get how to import the message and it's message type. I found some snippets while researching but it will throw an error.
This is where I am:
Private Sub Application_NewMail(oRequest As MeetingItem)
If oRequest.MessageClass <> "IPM.Schedule.Meeting.Request" Then
Exit Sub
End If
Dim oAppt As AppointmentItem
Set oAppt = oRequest.GetAssociatedAppointment(True)
'Dim oResponse
' Set oResponse = oAppt.Respond(olMeetingDeclined, True)
' oResponse.Display
MsgBox ("Testing")
End Sub
The event won't trigger.
First of all, you need to handle the NewMailEx event of the Application class which is fired when a new item is received in the Inbox. This event fires once for every received item that is processed by Microsoft Outlook. The item can be one of several different item types, for example, MailItem, MeetingItem, or SharingItem. The EntryIDsCollection string contains the Entry ID that corresponds to that item.
The NewMailEx event fires when a new message arrives in the Inbox and before client rule processing occurs. You can use the Entry ID returned in the EntryIDCollection array to call the NameSpace.GetItemFromID method and process the item.
Private Sub outApp_NewMailEx(ByVal EntryIDCollection As String)
Dim mai As Object
Set mai = Application.Session.GetItemFromID(strEntryId)
MsgBox mai.Subject
End Sub
Then you can check the message class and cast the object to the right type. Or just check the type name:
If TypeName(Item) = "MeetingItem" Then
Later you may check the sender related properties, for example, the SenderEmailAddress property returns a string that represents the email address of the sender of the Outlook item.
I'm not able to update the TextBox content from another module.
The TextBox is in a Form called frm_main and the EventHandler in another module called md_zeiss.
Init() is called by a button on frm_main.
Problem:
If I directly call Test() from frm_main it does change the text.
If called by the event, it does not change the text, but displays the correct MessageBox.
Code:
Module md_zeiss
Sub Init()
Dim fsw As New FileSystemWatcher
fsw.Path = "C:\Output"
fsw.Filter = "*.txt"
fsw.NotifyFilter = NotifyFilters.Attributes Or NotifyFilters.CreationTime Or NotifyFilters.DirectoryName _
Or NotifyFilters.FileName Or NotifyFilters.LastAccess Or NotifyFilters.LastWrite Or NotifyFilters.Security Or NotifyFilters.Size
fsw.EnableRaisingEvents = True
AddHandler fsw.Changed, AddressOf md_zeiss.Main
End Sub
Sub Main(sender As Object, e As IO.FileSystemEventArgs)
Do While IsLocked(e.FullPath) = True
Application.DoEvents()
Loop
Dim fs As New FileStream(e.FullPath, FileMode.Open, FileAccess.Read)
Dim sr As New StreamReader(fs, System.Text.Encoding.Default)
Dim textline As String = vbNullString
Dim nr As String
Dim gi As String
Dim le As String
Do Until sr.Peek = -1
textline = sr.ReadLine
Select Case True
Case InStr(textline, vbTab & "Ø MOLDING_NR_SIDE" & vbTab)
nr = ReadVal(textline, 5)
Case InStr(textline, vbTab & "LENGTH" & vbTab)
gi = ReadVal(textline, 5)
Case InStr(textline, vbTab & "Ø MOLDING_GI_SIDE" & vbTab)
le = ReadVal(textline, 5)
End Select
Loop
Test()
End Sub
Sub Test()
frm_Main.TextBox1.Text = "Test"
MsgBox(frm_Main.TextBox1.Text)
End Sub
The FileSystemWatcher raises it events on a secondary thread by default. Default instances of forms are thread-specific so if you display the default instance of a form on the UI thread and then try to access the default instance from the handler of a FileSystemWatcher event (or a method called from that handler) then you're actually referring to two different form objects.
The simplest option is to set the SynchronizingObject property of the FileSystemWatcher on the UI thread. You can assign a form or other control to that property and the FileSystemWatcher will then raise its events on the thread that owns that control, i.e. the UI thread. If you do go down that route, just be sure that your event handler is executed quickly. You don;t want to tie up the UI thread with long-running code, which is why a secondary thread is used by default.
Another option is to use the SynchronizationContext class in your module to allow you to marshal a method call to the UI thread. You would continue to have the FileSystemWatcher raise its events on a secondary thread and do the background work there, then call Send or Post on the SynchronizationContext to invoke a method on the UI thread, where using the default instance of that form would refer to the same instance as you have already displayed.
Basically though, the architecture there is bad. You should almost certainly be using a class rather than a module and the form can then keep a reference to an instance of that class. The class could then raise an appropriate event that the form could handle and then the form could update its own TextBox. If you are access the controls on a form outside that form then the code is inherently bad. Default form instances make doing that easier, which is one reason that seasoned developers generally don't like them. They make it easier for beginners to get up and running, but it also makes it easier for beginners to paint themselves into a corner when things get remotely complex.
I am parsing email from Outlook from vb.net. The main form displays the total count of mail for the selected Outlook folder and allows me to override this number so that I can process 1, 10, or all. When I click a button I open a new form passing in ref to my outlook mailClient class to provide access to the mail items. The form new and show events set up things and within a try/catch make a call to an async function like so.
Try
AllEMails = Await ScanMailItems(ref_clsEmailClient.ECFMailToScan,progressIndicator, tokenSource.Token)
Catch ag As AggregateException
If ag.InnerExceptions IsNot Nothing Then
MsgBox(ag.InnerExceptions.Count.ToString)
For Each ex In ag.InnerExceptions
MsgBox(ex.Message)
Next
End If
Catch ex As Exception
MsgBox(ex.Message & vbNewLine & vbNewLine & _
ex.StackTrace)
End Try
In this ScanMailItems() I am setting up a For Each Next to loop the mail items like so.
Dim EmailsFraction As Integer = Await Task(Of Integer).Run(
Function()
For i = ECFMailItems.Count To 1 Step -1
'other code
Throw New Exception("Test Error 1")
UpdateFormDelegate()
counter += 1
Next
Return counter
End Function)
Return EmailsFraction
The code this far is working and in the loop I am updating controls with some delegate subs to show subject and the email body of each email that will be parsed in the loop. My trouble is that I have placed some Throw New Exception("Test Error") in the for loop and in the delegate subs to be sure they will propagate back to my try/catch in my shown event. They do not and I have tried AggregateException. I can post actual code but think it is all in how the exception propagates through the async task.
I am just learning to do this, is there a better way to set it up? The main thing is that on each iteration of the For Loop my progress bar and the other controls need to update and I will be calling into other classes.
So as i'm learning more and more stuff of UWP and XAML i bumped into two issues, one is (i think) "navigation" related and the second a threading issue. What i'm trying to achieve is simple. I have two pages, one "home" and one "Settings". On the Home page i show the connected clients as Custom_Buttons. On the Settings page i can change some settings regarding the app and Connected Clients
Navigation Issue
On my MainPage is setup all my declarations and object classes i need. When i navigate to a page i pass me (that is the MainPage) through to the page i'm loading so i can use the properties and objects in the that i declared on the MainPage. Then when i load the page i use the page event OnNavigatedTo to handle the passed MainPage and do local stuf with it. When i switch often between the pages the app crashes and opens the page app.g.i.vb and point to the following code:
#If Debug AndAlso Not DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION Then
AddHandler Me.UnhandledException,
Sub(sender As Global.System.Object, unhandledExceptionArgs As Global.Windows.UI.Xaml.UnhandledExceptionEventArgs)
If Global.System.Diagnostics.Debugger.IsAttached Then
**Here--->>>** Global.System.Diagnostics.Debugger.Break()
End If
End Sub
#End If
And the navigation code:
Private Sub ListBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
If Home.IsSelected AndAlso Not ScenarioFrame.CurrentSourcePageType Is GetType(Home) Then
BackButton.Visibility = Visibility.Collapsed
ScenarioFrame.Navigate(GetType(Home), Me)
ElseIf Settings.IsSelected AndAlso Not ScenarioFrame.CurrentSourcePageType Is GetType(Settings) Then
BackButton.Visibility = Visibility.Visible
ScenarioFrame.Navigate(GetType(Settings), Me)
End If
End Sub
Threading Issue
On the MainPage I declare a class i wrote called TCP_Server. This class has a StreamSocketListener that uses the event ConnectionReceived to accept new incoming clients. I then simply create a new Object that represents a UI form of the client and pass it the StreamSocket that comes in the Event Args in the sub new. In this way each object can handles it's own Read and Write directly from the StreamSocket Then i add this new object to a ObservableCollection(Of Object) which is held in the TCP_Server Class. This list is bound to the ItemsSource of a Canvas that i use on the HomePage which is not my MainPage.
Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
MyBase.OnNavigatedTo(e)
If ButtonsList.ItemsSource = Nothing Then ButtonsList.ItemsSource = DirectCast(e.Parameter, MainPage).TCP_Server.Clients
End Sub
When i create this new object in the ConnectionReceived i get an error System.Exception: 'The application has called an interface that has been marshalled for another thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD)) '. It only works when i use the Dispatcher.RunAsync
Private Async Sub TCP_Listener_ConnectionReceived(sender As StreamSocketListener, args As StreamSocketListenerConnectionReceivedEventArgs) Handles TCP_Listener.ConnectionReceived
'Check if the client already excists or not.
Dim client As Client_Button = Clients.FirstOrDefault(Function(x) x.IPaddr = args.Socket.Information.RemoteAddress.ToString)
rootPage.NotifyUser("New Client connected! : [" & args.Socket.Information.RemoteAddress.ToString & "] Total Connected clients = " & Clients.Count, NotifyType.Message)
If client Is Nothing Then
Await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Function()
'Create New object
Dim x As New Client_Button(args.Socket)
'Create new task that runs Async to process incomming data
Dim tsk As Task = Task.Run(Sub() x.ProcessClientAsync())
'Add to the task list so we can stop it later on
ClientTasks.Add(tsk)
'Add it to the Clients List so we can work with the objects
Clients.Add(x)
Return True
End Function)
Else
Await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Function()
client = Nothing
Clients.Remove(client)
'Create New object
Dim x As New Client_Button(args.Socket)
'Create new task that runs Async to process incomming data
Dim tsk As Task = Task.Run(Sub() x.ProcessClientAsync())
'Add to the task list so we can stop it later on
ClientTasks.Add(tsk)
'Add it to the Clients List so we can work with the objects
Clients.Add(x)
Return True
End Function)
End If
End Sub
For "Navigation Issue" you described here, navigation between pages several times it will crash, please try to set NavigationCacheMode of the page to Required or Enabled as follows:
Public Sub New()
Me.InitializeComponent()
Me.NavigationCacheMode = NavigationCacheMode.Required
End Sub
Details please reference remarks of Page class. If you still have issues please provide the details about the "UnhandledException" .
For "Threading Issue", using CoreDispatcher is the correct way and this is by design. ConnectionReceived triggered in a non-UI thread, but you invoked UI thread inside this event handle, so you need Dispatcher.RunAsync. More details you can reference this similar thread.
My overall goal is to change the "From" sender on an incoming email to something else. Specifically, I get chat logs from Zopim chat and they're all coming from Zopim's "no-reply" email. However, I want these chat logs to be associated with in my CRM and thus I want them associated to people who we chat with.
I've created this VBA script, it runs without errors, however, no change is done to the incoming email. What am I doing wrong?
Option Explicit
Sub ZopimChatMessageRule(Item As Outlook.MailItem)
Dim body As String
Dim sndr As String
On Error GoTo Finally
body = Trim(Right(Item.body, Len(Item.body) - (InStrRev(Item.body, "EMAIL") + 5)))
sndr = Trim(Left(body, InStr(body, vbCrLf) - 1))
Item.sender.Address = sndr
Item.sender.Name = sndr
Item.sender.Update
Item.Recipients.ResolveAll
Item.Save
Finally:
End Sub
Your code is updating the name and address of a one-off address entry that does not exist anywhere. What you need to do is change the Sender and SentOnBehalfOf properties, which are read-only in the Outlook Object Model.
You can use MailItem.PropertyAccessor.SetProperty to update dozen or so PR_SENDER_xyz and PR_SENT_REPRESENTING_xyz MAPI properties - take a look at a message with OutlookSpy (I am its author - click IMessage button). Keep in mind that SetProperty will prevent you from modifying some properties Outlook considers "special".
If using Redemption is an option (I am also its author), you can set the Sender and SentOnBehalfOf properties directly:
set rSession = CreateObject("Redemption.RDOSession")
rSession.MAPIOBJECT = Application.Session.MAPIOBJECT
set Msg = rSession.GetRDOObjectFromOutlookObject(Item )
strEntryID = rSession.AddressBook.CreateOneOffEntryID("Fake User", "SMTP", "someuser#fake.domain.com", false, true)
set addressEntry = rSession.AddressBook.GetAddressEntryFromID(strEntryID)
Msg.Sender = addressEntry
Msg.SentOnBehalfOf = addressEntry
Msg.Save