delay sending mail with Exchange Web Services (EWS) Managed API - vb.net

My issue is I cannot seem to put a delay on sending the item (have tried 2 minutes through to 2 days, no luck).
The mail itself sends immediately and without fail - it's just the delay that doesn't work? any help would be appreciated.
Note -
I have been using this as an example for most of my code.
I am using exchange 2010 SP2
The mail sends fine, just no delay
Public Class Mail
Private Const DEFERREDSENDTIMEFLAG As Integer = 16367
Public Shared ReadOnly Property EXCHANGESERVICEURL As String
Get
Return ConfigurationManager.AppSettings("EXCHANGESERVICEURL")
End Get
End Property
Public Shared ReadOnly Property DOMAINNAME As String
Get
Return ConfigurationManager.AppSettings("DOMAINNAME")
End Get
End Property
Public Shared ReadOnly Property EXCHANGEUSERNAME As String
Get
Return ConfigurationManager.AppSettings("EXCHANGEUSERNAME")
End Get
End Property
Public Shared ReadOnly Property EXCHANGEPASSWORD As String
Get
Return ConfigurationManager.AppSettings("EXCHANGEPASSWORD")
End Get
End Property
Public Shared ReadOnly Property EXCHANGEVERSION As ExchangeVersion
Get
Return CType(System.Enum.Parse(GetType(ExchangeVersion), ConfigurationManager.AppSettings("EXCHANGEVERSION")), ExchangeVersion)
End Get
End Property
Public Shared Sub SendMessage(ByVal fromAddress As String, ByVal toAddress() As String, ByVal ccAddress() As String, ByVal bccAddress() As String, ByVal subject As String, ByVal body As String, ByVal minutesDelay As Integer)
ServicePointManager.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf ValidateCertificate)
Dim service As New ExchangeService(EXCHANGEVERSION)
service.Credentials = New WebCredentials(EXCHANGEUSERNAME, EXCHANGEPASSWORD, DOMAINNAME)
service.Url = New Uri(EXCHANGESERVICEURL)
Dim Message As New Microsoft.Exchange.WebServices.Data.EmailMessage(service)
'set delay send time
If minutesDelay > 0 Then
Dim sendTime As String = DateTime.Now.AddMinutes(minutesDelay).ToUniversalTime().ToString()
Dim PR_DEFERRED_SEND_TIME As New ExtendedPropertyDefinition(DEFERREDSENDTIMEFLAG, MapiPropertyType.SystemTime)
Message.SetExtendedProperty(PR_DEFERRED_SEND_TIME, sendTime)
End If
Message.From = fromAddress
If toAddress IsNot Nothing Then
For Each t As String In toAddress
Message.ToRecipients.Add(t)
Next
End If
If ccAddress IsNot Nothing Then
For Each t As String In ccAddress
Message.CcRecipients.Add(t)
Next
End If
If bccAddress IsNot Nothing Then
For Each t As String In bccAddress
Message.BccRecipients.Add(t)
Next
End If
Message.Subject = subject
Message.Body = body
Message.SendAndSaveCopy() 'save means make sure it's saved in the sent items folder
'message.Attachments
End Sub
Private Shared Function ValidateCertificate(sender As Object, certificate As X509Certificate, chain As X509Chain, sslPolicyErrors As SslPolicyErrors) As Boolean
Return True
End Function
End Class

I would suggest you used the typed variable rather then converting the Date-time to a string (like the example does) and then sending it. eg just use
Message.SetExtendedProperty(PR_DEFERRED_SEND_TIME, DateTime.Now.AddMinutes(minutesDelay).ToUniversalTime())
The library is designed to deal with typed variables but you can see the difference in the POST using a String vs Typed variable if you enable tracing eg
<t:ExtendedProperty>
<t:ExtendedFieldURI PropertyTag="16367" PropertyType="SystemTime"/>
<t:Value>2016-05-10T03:20:16.000</t:Value>
</t:ExtendedProperty>
vs
<t:ExtendedProperty>
<t:ExtendedFieldURI PropertyTag="16367" PropertyType="SystemTime"/>
<t:Value>2016-10-05T03:12:30.067Z</t:Value>
</t:ExtendedProperty>
You could also fix the string but using the typed varible makes more sense with that change you sample run fine for me.

Related

how to handle properties with this FxCopAnalyzer warning: "CA2227: Set "Images" as read-only by removing the setter for the property"

What is this programme about:
This program was developed with the VB.Net language, the .NET Framework 4.8 and Visual Studio 2019 CE. The idea of this program is to run a rudimentary database. The list is similar to a classic Internet forum—there are threads, there are different numbers of postings in the threads and in each post there are different numbers of pictures and long texts. If the thread is selected using the ComboBox, all posts with their images and texts are displayed one below the other. When you click on a specific post, only its images are displayed.
The user can create threads and posts. For reasons of legibility, only extended Latin letters are allowed when writing the text (e.g. René, Chloë).
When you close the program, you will be asked whether you want to save the data. These data are read in when the program is loaded. If images are not found, their paths will be displayed in a window.
The user also has the option of searching through all threads and viewing the results with various sorting options. In this case, only the posts found are listed in the ListBox, and here, too, the user can select the posts individually.
The program also reads in user data when it starts. A user can log in and, depending on his role, has certain power to make decisions. A “normal” user can create threads and posts, but only an administrator or moderator can edit and delete texts. If you are not logged in, you can only read threads and posts.
What I would like to know from you
I use the FxCopAnalyzer, which is currently giving me 8 warnings. I am aware that it sometimes exaggerates a little or criticizes certain things that were done on purpose. But I'd like to let you guys have a look to make it better. What exactly is this supposed to mean: "CA2227: Set "Images" (Bilder) as read-only by removing the setter for the property". Because when I remove the setter, I get an error message.
I feel the same way with other properties in other classes. So I need to know, how to fix this, and I need a general solution. I appreciate.
Class structure
There is the class Forum, the class Class_Thread and the class Class_Post. Class_Thread inherits from forum. In the Class_thread there is a list(of Class_Post) which contains instances of Class_Post. ("The thread knows what posts it has"). In FormMain, a List(of Class_Thread) is created, in which the instances of Class_Thread are.
In Class_Post's constructor, the following parameters are transferred: 1) the heading of the posting, 2) the text, 3) the number, 4) images as List(Of System.Drawing.Bitmap), 5) the date, 6) the image paths As List(Of String).
Class_Post.vb
#Disable Warning CA1707 ' Bezeichner dürfen keine Unterstriche enthalten
Public Class Class_Post : Inherits Class_Forum
Private _ueberschrift As String = ""
Private _text_dazu As String = ""
Private _nummer As UInt16
Private _bilder As List(Of System.Drawing.Bitmap)
Private _erstelldatum_dieses_Posts As Date
Private _pfade_der_Bilder As List(Of String)
Public Property Ueberschrift As String
Get
Return _ueberschrift
End Get
Set(value As String)
_ueberschrift = value
End Set
End Property
Public Property Text_dazu As String
Get
Return _text_dazu
End Get
Set(value As String)
_text_dazu = value
End Set
End Property
Public Property Nummer As UShort
Get
Return _nummer
End Get
Set(value As UShort)
_nummer = value
End Set
End Property
Public Property Bilder As List(Of Bitmap)
Get
Return _bilder
End Get
Set(value As List(Of Bitmap))
_bilder = value
End Set
End Property
Public Property Erstelldatum_dieses_Posts As Date
Get
Return _erstelldatum_dieses_Posts
End Get
Set(value As Date)
_erstelldatum_dieses_Posts = value
End Set
End Property
Public Property Pfade_der_Bilder As List(Of String)
Get
Return _pfade_der_Bilder
End Get
Set(value As List(Of String))
_pfade_der_Bilder = value
End Set
End Property
Public Sub New(ByVal Ueberschrift As String, ByVal Text As String, ByVal nr As UInt16, ByVal uebergebene_Bilder As List(Of System.Drawing.Bitmap), ByVal _date As Date, ByVal Pfade As List(Of String))
Me.Ueberschrift = Ueberschrift
Me.Text_dazu = Text
Me.Nummer = nr
Me.Bilder = uebergebene_Bilder
Me.Erstelldatum_dieses_Posts = _date
Me.Pfade_der_Bilder = Pfade
End Sub
End Class
#Enable Warning CA1707 ' Bezeichner dürfen keine Unterstriche enthalten
This is in FormMain, where a new Instance of Class_Post is created This Sub is using a second Form from which the data is taken.
Private Sub Button_neueAntwort_Click(sender As Object, e As EventArgs) Handles Button_neueAntwort.Click
' doppelt abgesichert, falls die Variable SI aus irgendeinem Grunde nicht (-1) ist, obwohl sie das sollte.
If SI <> (-1) OrElse ComboBox1.SelectedItem IsNot Nothing Then
Using FA As New Form_Antwort
Dim DR As DialogResult = FA.ShowDialog(Me)
If DR = DialogResult.Yes Then
Dim neuerPost As New Class_Post(FA.Titel_des_Posts, FA.Beschreibung, CUShort(Liste_mit_allen_Threads(SI).Posts_in_diesem_Thread.Count + 1), FA.NeueListeBitmaps, Date.Now, FA.Liste_mit_Pfaden)
Liste_mit_allen_Threads(SI).Posts_in_diesem_Thread.Add(neuerPost)
alle_Posts_in_diesem_Thread_anzeigen()
End If
End Using
Else
MessageBox.Show("Bitte zuerst einen Thread auswählen.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End Sub
Thanks to Jimi and Craig, I can come up with a solution here. The thing is, unfortunately I let the FxCopAnalyzer influence me a bit. It had suggested using getters and setters instead of the normal fields, and that's why the warning I reported about came up. But the solution looks very simple, just use properties:
Public Class Class_Post : Inherits Class_Forum
Public ReadOnly Property Ueberschrift As String = ""
Public ReadOnly Property Text_dazu As String = ""
Public ReadOnly Property Nummer As UInt16
Public ReadOnly Property Bilder As List(Of System.Drawing.Bitmap)
Public ReadOnly Property Erstelldatum_dieses_Posts As Date
Public ReadOnly Property Pfade_der_Bilder As List(Of String)
Public Sub New(ByVal Ueberschrift As String, ByVal Text As String, ByVal nr As UInt16, ByVal uebergebene_Bilder As List(Of System.Drawing.Bitmap), ByVal _date As Date, ByVal Pfade As List(Of String))
Me.Ueberschrift = Ueberschrift
Me.Text_dazu = Text
Me.Nummer = nr 'kann maximal 65535 werden
Me.Bilder = uebergebene_Bilder
Me.Erstelldatum_dieses_Posts = _date
Me.Pfade_der_Bilder = Pfade
End Sub
End Class
Edit:
Of course, this is only true until you decide to create a button where a moderator wants to delete an image. Then, ReadOnly has to be removed again and the FxCopAnalyzer complains again.

Extracting property values from a dictionary

I am attempting to write a subroutine that will deserialize a dictionary from a .ser file (this bit works fine) and then repopulate several lists from this dictionary (this is the bit I cannot do).
The dictionary contains objects (I think) of a custom class I wrote called "Photo Job" which has properties such as ETA, notes, medium etc. (Declared as such)
Dim photoJobs As New Dictionary(Of String, PhotoJob)
In short, I want to be able to extract every entry of each specific property into an separate arrays (one for each property) and I can go from there.
Any help would be appreciated, I may be going about this completely the wrong way, I'm new to VB. The relevant code is below:
Photo Job Class:
<Serializable()> _Public Class PhotoJob
Private intStage As Integer 'Declare all local private variables
Private ID As String
Private timeLeft As Integer
Private material As String '
Private note As String
Private path As String
Private finished As Boolean = False
'Declare and define properties and methods of the class
Public Property productionStage() As Integer
Get
Return intStage
End Get
Set(ByVal Value As Integer)
intStage = Value
End Set
End Property
Public Property photoID() As String
Get
Return ID
End Get
Set(ByVal Value As String)
ID = Value
End Set
End Property
Public Property ETA() As Integer
Get
Return timeLeft
End Get
Set(ByVal Value As Integer)
timeLeft = Value
End Set
End Property
Public Property medium() As String
Get
Return material
End Get
Set(ByVal Value As String)
material = Value
End Set
End Property
Public Property notes() As String
Get
Return note
End Get
Set(ByVal Value As String)
note = Value
End Set
End Property
Public Property imagePath() As String
Get
Return path
End Get
Set(ByVal Value As String)
path = Value
End Set
End Property
Public Property complete() As Boolean
Get
Return finished
End Get
Set(value As Boolean)
finished = value
End Set
End Property
Public Sub nextStage()
If intStage < 4 Then
intStage += 1
ElseIf intStage = 4 Then
intStage += 1
finished = True
End If
End Sub
End Class
Subroutines involved in de/serialisation:
Private Sub BackupAllToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles BackupAllToolStripMenuItem.Click
Dim formatter As New BinaryFormatter
Dim backupFile As New FileStream(Strings.Replace(Strings.Replace(Now, ":", "_"), "/", ".") & ".ser", FileMode.Create, FileAccess.Write, FileShare.None)
formatter.Serialize(backupFile, photoJobs)
backupFile.Close()
MsgBox("Collection saved to file")
End Sub
Private Sub RestoreFromFileToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles RestoreFromFileToolStripMenuItem.Click
With OpenFileDialog 'Executes the following sets/gets/methods of the OpenFileDialog
.FileName = ""
.Title = "Open Image File"
.InitialDirectory = "c:\"
.Filter = "Serial Files(*.ser)|*ser"
.ShowDialog()
End With
Dim backupPathStr As String = OpenFileDialog.FileName
Dim deSerializer As New BinaryFormatter
Dim backupFile As New FileStream(backupPathStr, FileMode.Open)
photoJobs = deSerializer.Deserialize(backupFile)
backupFile.Close()
End Sub
From what I can see using the autos menu, the saving/restoring of the dictionary works just fine.
First, if you are using VS2010+, you can greatly reduce boilerplate code using autoimplemented properties:
<Serializable()>
Public Class PhotoJob
Public Property productionStage() As Integer
Public Property photoID() As String
Public Property ETA() As Integer
etc
End Class
That is all that is needed, all the boilerplate code is handled for you. Second, with this line:
photoJobs = deSerializer.Deserialize(backupFile)
Your deserialized photojobs will be a generic Object, not a Dictionary. You should turn on Option Strict so VS will enforce these kinds of errors. This is how to deserialize to Type:
Using fs As New FileStream(myFileName, FileMode.Open)
Dim bf As New BinaryFormatter
PhotoJobs= CType(bf.Deserialize(fs), Dictionary(Of String, PhotoJob))
End Using
Using closes and disposes of the stream, CType converts the Object returned by BF to an actual dictionary
To work with the Dictionary (this has nothing to do with Serialization) you need to iterate the collection to get at the data:
For Each kvp As KeyValuePair(Of String, PhotoJob) In PhotoJobs
listbox1.items.Add(kvp.value.productionStage)
listbox2.items.Add(kvp.value.ETA)
etc
Next
The collection is a made of (String, PhotoJob) pairs as in your declaration, and when you add them to the collection. They comeback the same way. kvp.Key will be the string key used to identify this job in the Dictionary, kvp.Value will be a reference to a PhotoJobs object.
As long as VS/VB knows it is a Dictionary(of String, PhotoJob), kvp.Value will act like an instance of PhotoJob (which it is).

Problems with extending s.ds.am.UserPrincipal class

I have been trying to extend the s.ds.am.UserPrincipal class in VS 2010 VB app as I require access to AD User properties that are not by default a part of the UserPrincipal. This is a new concept to me and I have found some useful code on here as well but I have run into 2 problems with my extensions that I cannot seem to figure out.
Problem 1: When I attempt to use my UserPrincipalEx to retrieve a user I receive the following error.
ERROR:
Principal objects of type MyApp.UserPrincipalEx can not be used in a query against this store
CODE TO PRODUCE ERROR:
Dim ctx As New PrincipalContext(ContextType.Domain, DomainController)
Dim user As UserPrincipalEx = UserPrincipalEx.FindByIdentity(ctx, samAccountName)
Problem 2: When I attempt to create a new user with my UserPrincipalEx I receive the following error. When I trace the PrincipalContext (ctx) into the class it appears to disconnect from the DomainController.
ERROR:
Unknown error (0x80005000)
CODE TO PRODUCE ERROR:
Dim ctx As New PrincipalContext(ContextType.Domain, DomainController, DirectoryOU)
Dim NewADUser As New UserPrincipalEx(ctx, newsamAccountName, newPassword, True)
CLASS:
Imports System.DirectoryServices.AccountManagement
Public Class UserPrincipalEx
Inherits UserPrincipal
Public Sub New(ByVal context As PrincipalContext)
MyBase.New(context)
End Sub
Public Sub New(ByVal context As PrincipalContext, ByVal samAccountName As String, ByVal password As String, ByVal enabled As Boolean)
MyBase.New(context, samAccountName, password, enabled)
End Sub
<DirectoryProperty("Title")> _
Public Property Title() As String
Get
If ExtensionGet("title").Length <> 1 Then
Return Nothing
End If
Return DirectCast(ExtensionGet("title")(0), String)
End Get
Set(ByVal value As String)
Me.ExtensionSet("title", value)
End Set
End Property
<DirectoryProperty("physicalDeliveryOfficeName")> _
Public Property physicalDeliveryOfficeName() As String
Get
If ExtensionGet("physicalDeliveryOfficeName").Length <> 1 Then
Return Nothing
End If
Return DirectCast(ExtensionGet("physicalDeliveryOfficeName")(0), String)
End Get
Set(ByVal value As String)
Me.ExtensionSet("physicalDeliveryOfficeName", value)
End Set
End Property
<DirectoryProperty("Company")> _
Public Property Company() As String
Get
If ExtensionGet("company").Length <> 1 Then
Return Nothing
End If
Return DirectCast(ExtensionGet("company")(0), String)
End Get
Set(ByVal value As String)
Me.ExtensionSet("company", value)
End Set
End Property
' Implement the overloaded search method FindByIdentity.
Public Shared Shadows Function FindByIdentity(ByVal context As PrincipalContext, ByVal identityValue As String) As UserPrincipalEx
Return DirectCast(FindByIdentityWithType(context, GetType(UserPrincipalEx), identityValue), UserPrincipalEx)
End Function
' Implement the overloaded search method FindByIdentity.
Public Shared Shadows Function FindByIdentity(ByVal context As PrincipalContext, ByVal identityType As IdentityType, ByVal identityValue As String) As UserPrincipalEx
Return DirectCast(FindByIdentityWithType(context, GetType(UserPrincipalEx), identityType, identityValue), UserPrincipalEx)
End Function
End Class
PROBLEM 1
I was able to solve Problem 1 by adding the following to the top of the class after the Imports
<DirectoryRdnPrefix("CN")> _
<DirectoryObjectClass("user")> _
PROBLEM 2
I have also been banging my head against this for to long and was finally able to resolve Problem 2. The DirectoryOU variable did not contain a correct path the intended AD OU. I was missing a comma and after staring at everything for 30 minutes I finally noticed the issue.

update values in a dictionary with new fields obtained from a wmi query vb.net

EDIT
I still can't assign the process the username of the session id that is associated with it.
This is the code i use to retrieve user details:
Public Sub GetUsers()
Using server As ITerminalServer = manager.GetRemoteServer(strHostName)
server.Open()
For Each session As ITerminalServicesSession In server.GetSessions()
If Not String.IsNullOrEmpty(session.UserName) Then
dictuser.Add(session.SessionId, New User(session.SessionId, session.UserName))
End If
Next
End Using
End Sub
my user class is defined simply as:
Public Class User
Private _SessionID As Integer
Private _UserName As String
Sub New(ByVal SessionID As Integer, ByVal UserName As String)
_SessionID = SessionID
_UserName = UserName
End Sub
Public ReadOnly Property SessionID As String
Get
Return _SessionID
End Get
End Property
Public ReadOnly Property UserName As String
Get
Return _UserName
End Get
End Property
End Class
i have created a function in my process class:
Public Sub AddUserInfo(ByVal UserName As String)
_UserName = UserName
End Sub
This replaces the process if a process with the same id is found in the dictionary and automatically adds a new process otherwise.
dictProcess(process.ProcessId) = process
EDIT (in response to your edited question):
I would change the name of the Processes class to Process, since it is not a collection, but is supposed to represent one process. You could change the constructor of the Process class to
Public Sub New(ByVal ProcessId As Integer, ByVal ProcessName As String, ByVal SessionId As Integer)
_ProcessId = ProcessId
_ProcessName = ProcessName
_SessionId = SessionId
End Sub
Then add a method
Public Sub AddWmiInfo (ByVal PageFileUsage As Integer, ByVal WorkingSetSize As Integer)
_PageFileUsage = PageFileUsage
_WorkingSetSize = WorkingSetSize
End Sub
Alternatively, you could also make these properties read/write, however you get a better encapsulation like this.
Add the basic process information to the dictionary using Cassia. Note that ProcessId is declared as Integer and thus the check Not String.IsNullOrEmpty(process.ProcessId) makes no sense.
For Each process As ITerminalServicesProcess In server.GetProcesses()
dictprocess.Add( _
process.ProcessId, _
New Process(process.ProcessId, process.ProcessName, process.SessionId))
Next
Finally, you would add the information from the WMI like this
Dim p As Process = Nothing
For Each wmiProcess In prowmi
If dictprocess.TryGetValue(wmiProcess.ProcessID, p) Then
p.AddWmiInfo(wmiProcess.PageFileUsage, wmiProcess.WorkingSetSize)
End If
Next
TryGetValue returns a process in the variable p. The second parameter of TryGetValue is a ByRef parameter.
EDIt #2:
Your loop should read
Dim p As Process = Nothing
For Each user As User In dictuser.Values
If dictprocess.TryGetValue(user.SessionID, p) Then
p.AddUserInfo(user.UserName)
End If
Next
However, if you are never accessing the user dictionary through the key (session id), then a list would be more appropriate than a dictionary.

Null reference issue when adding services running on a terminal server to a dictionary

In VB.NET, I am trying to retrieve what services are running on a TS using the following code:
Imports System.ServiceProcess
...
Dim dictservice As Generic.Dictionary(Of String, Services)
Public Sub GetService()
Dim localServices As ServiceController() = ServiceController.GetServices()
For Each service As ServiceController In localServices
dictservice.Add(service.DisplayName, New Services(service.DisplayName, service.ServiceName, service.Status.ToString))
Next
End Sub
My services class is as follows:
Class Services
Private _displayName As String
Private _serviceName As String
Private _serviceStatus As String
Sub New(ByVal DisplayName As String, ByVal ServiceName As Object, ByVal ServiceStatus As String)
_displayName = DisplayName
_serviceName = ServiceName
_serviceStatus = ServiceStatus
End Sub
Public Overrides Function ToString() As String
Return _serviceStatus
End Function
End Class
When i step through in debug mode it seems to being to populate the dictionary
display name: Application Experience
service name: AElookUpSVC
service status: Running (4)
When it tries to move onto the next item i get the following error:
Null reference exception was unhandled:
Object reference not set to an instance of an object.
I can't for the life of me work out where it is finding a null reference?
You need to initialize your dictionary with New:
Dim dictservice As New Generic.Dictionary(Of String, Services)
Public Sub GetService()
Dim localServices As ServiceController() = ServiceController.GetServices()
For Each service As ServiceController In localServices
dictservice.Add(service.DisplayName, New Services(service.DisplayName, service.ServiceName, service.Status.ToString))
Next
End Sub
Right now it's Nothing, hence the NullReferenceException.
The most likely problem appears to be that dictService is Nothing and hence you get a NullReferenceException. Where do you initialize / declare dictService and are you certain the initialization happens before this method?
If this is not the problem have you tried running this with the debugger attached? it should point you to the line that is failing.