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.
Related
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.
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.
I have a DLL with several properties and a function that generates runs a SSRS report in the background and saves it to a PDF file.
I have a DataTable with all the reports that need to be generated and where they need to be saved.
I want to make each instance of the DLL run in a separate thread. I took a stab at it found the 2nd row in the DataTable overrode the first row.
Here is the Class/DLL Code
Public Class SSRSFunctions
Private Shared _Formated_Parameters As String
Private Shared _Report_Parameters As Dictionary(Of String, String)
Public Property FORMATED_PARAMETERS() As String
Get
Return _Formated_Parameters
End Get
Set(ByVal value As String)
_Formated_Parameters = value
End Set
End Property
Public Sub New()
_Report_Parameters = New Dictionary(Of String, String)
End Sub
Public Function RenderReportToFile() As String
'RenderReportHere
End Function
Public Sub AddParameter(ByVal Name As String, ByVal Value As String)
If _Report_Parameters.ContainsKey(Name) Then
_Report_Parameters.Remove(Name)
_Report_Parameters.Add(Name, Value)
Else
_Report_Parameters.Add(Name, Value)
End If
End Sub
End Class
Here is the calling Code
Private Sub CheckForNewRequests()
'Filter DataTable for Reports to Run
For Each dr As DataRow in DateTable.Rows
Dim rpt As New SSRSFunctions
Dim t1 As New Threading.Thread(AddressOf StartNewThread)
rpt.FORMATED_PARAMETERS = (dr("REPORT_PARAMS"))
t1.Start(rpt)
Next
End Sub
Private Function StartNewThread(ByVal report As SSRSFunctions) As String
Return report.RenderReportToFile()
End Function
I am trying to figure out why the "Dim rpt As New SSRSFunctions" is not creating a new instance of the DLL and so the second row of the dataTable has a new instance to store it's parameters.
The second row is overriding the first.
Help?
Thanks
jlimited
Dont make the private properties shared, remove the Shared keyword from the declarations.
change
Private Shared _Formated_Parameters As String
Private Shared _Report_Parameters As Dictionary(Of String, String)
to
Private _Formated_Parameters As String
Private _Report_Parameters As Dictionary(Of String, String)
By sharing them you are saying that no matter how many instances of the class is created always use (share) the same instance of the shared internal variable.
I have a WebService defined as this:
<WebMethod(Description:="Retrieve a list of account profiles.")> _
<System.Web.Services.Protocols.SoapHeader("MyHeader")> _
Public Function RetrieveAccountProfile(<XmlElement(IsNullable:=True, Type:=GetType(WSRetrieveAccountProfile))> ByVal myAccount As WSRetrieveAccountProfile) As <XmlElement(IsNullable:=True)> WSResponseRetrieveAccountProfile
...
The class I am passing as an input parameter WSRetrieveAccountProfile is defined like this:
<Serializable()> _
<XmlType(Namespace:="http://mynamespace/", TypeName:="WSRetrieveAccountProfile")> _
Public Class WSRetrieveAccountProfile
<XmlElement(IsNullable:=False)> Public accountNumber As List(Of Integer)
Public Sub New()
End Sub
Public Function Validate() As Boolean
'Validations here
Return True
End Function
End Class
The issue: I consume web-service and I am working on the client side where I am trying to declare an object of type WSRetrieveAccountProfile so I can pass it as an input parameter when calling the web-service, but I get compilation error - it cannot recognize the type.
After checking Reference.vb I notice that an input parameter for the function is a plain array of Integer ByVal myAccount() As Integer. Now, if I add another variable to the WSRetrieveAccountProfile class definition the ByVal myAccount() As Integer suddenly changes to ByVal myAccount As WSRetrieveAccountProfile and problem is solved.
How do I solve this WITHOUT adding an unnecessary variable? I tried with XmlType attribute with no luck.
* UPDATE *
This definition works:
<Serializable()> _
<XmlType(Namespace:="http://mynamespace/", TypeName:="WSRetrieveAccountProfile")> _
Public Class WSRetrieveAccountProfile
<XmlElement(IsNullable:=False)> Public accountNumber As List(Of Integer)
<XmlElement(IsNullable:=True)> Public TEST As String
Public Sub New()
End Sub
Public Function Validate() As Boolean
'Validations here
Return True
End Function
End Class
* UPDATE - SOLUTION *
If I change
<XmlElement(IsNullable:=False)> Public accountNumber As List(Of Integer)
to
<XmlArray(IsNullable:=True)> Public accountNumber As List(Of Integer)
then the proxy is generated properly and I no longer have an issue.
Change
<XmlElement(IsNullable:=False)> Public accountNumber As List(Of Integer)
to
<XmlArray(IsNullable:=True)> Public accountNumber As List(Of Integer)
to fix proxy generation.
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.