Using Process related API calls in VB.NET

I’m working on an app that will monitor the number of GDI Objects of another process (in this case, the spooler)

To do this in VB.NET requires an API call to GetGuiResources. While testing, I was using Process.GetProcesses to get a list of all the available processes, passed the handle of each process to the API function, and writing the result to a textbox.

Problem was, all process that I did not own throw security errors, even when I set the app to run with elevated privs. The internet once again failed me, and I stumbled across the solution while browsing the system.diagnostics.process documentation on MSDN.

The solution is to call Process.EnterDebugMode() before getting your process list and calling the API, then calling Process.LeaveDebugMode() when you are done.

MSDN EnterDebugMode Documentation

Here is an example. remember to run with elevate privs, and create a multiline textbox called txtOut.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Public Class Form1
  Declare Function GetGuiResources Lib "user32" (ByVal hProcess As Long, ByVal uiFlags As Long) As Long
  ' uiFlags: 0 - Count of GDI objects
  ' uiFlags: 1 - Count of USER objects
 
  Private Sub btnGo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGo.Click
    Dim procList As Process()
 
    Dim TYPE_GDI As UInteger = 0
 
    Process.EnterDebugMode()
    procList = Process.GetProcesses
 
    For Each proc As Process In procList
      Dim objCnt As Long
      Try
        objCnt = GetGuiResources(proc.Handle, TYPE_GDI)
      Catch ex As Exception
        objCnt = -1
      End Try
 
      txOut.AppendText(proc.ProcessName & vbTab & "GDI: " & objCnt.ToString & vbNewLine)
    Next proc
 
    Process.LeaveDebugMode()
 
 
  End Sub
End Class

Getting Active Directory info for the current user in VB.net in 2 lines

Don’t listen to the internet – getting account information from active directory for the current user is simple and easy in Visual Basic .NET – particularly if you are using Framework 3.5 or higher

Note: if you are using ASP.NET, there is a different way to do it – see the update below

You don’t need to do any stinking LDAP queries, or lookups, or credential passings – it’s all made simple using System.DirectoryServices.AccountManagement. Observe.

First, go to the references tab in project properties, click add reference, and find “System.DirectoryServices.AccountManagement” – no need to add “System.DirectoryServices”.

Now, in your application, add the following lines:

1
2
Dim currentADUser As System.DirectoryServices.AccountManagement.UserPrincipal
currentADUser = System.DirectoryServices.AccountManagement.UserPrincipal.Current

(It’s even simpler if you import the namespace)

Poof. That’s it! You are done.

currentADUser is a strongly typed object containing attributes for most of the active directory properties you need = such as display name, email address, primary group membership, exchange mailbox info, etc, etc.

Say you want to get the current user’s email address. You could do it like so (after the previous code):

1
Dim userEmail as string = currentADUser.EmailAddress

That’s it. 1 additional line.

How about a concrete example – here is the problem I wanted to solve. Send an email message from the current user for error reporting – Make sure to change the To: email address, and the smtp server name, and this should be a drop-in solution:

1
2
3
4
5
6
7
8
9
Private Sub report_error(ByVal errorMessage As String)
  Dim currentADUser As System.DirectoryServices.AccountManagement.UserPrincipal
  currentADUser = System.DirectoryServices.AccountManagement.UserPrincipal.Current
  Dim mailClient As New System.Net.Mail.SmtpClient("smtpserver.company.local")
  mailClient.Send(currentADUser.DisplayName & " <" & currentADUser.EmailAddress & ">", _
                  "notifications@company.com", _
                  "ERROR REPORT: Application error for " & currentADUser.DisplayName, _
                  errorMessage)
End Sub

Hope this helps!

Update – ASP.NET
The “better” way to get this info in an ASP page is to take advantage of the “User” object available to the ASP.NET Page class. User.Ientity can be cast to a System.Security.Principal.WindowsIdentity, giving you access to the User attribute (yes, User.Identity.User essentially) which is the domain SID. You use this SID to lookup the user in AD.

You check the User.Identity.IsAuthenticated to make sure that IIS has taken care of verifying the identity of the user.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Imports System.Security.Principal
Imports System.DirectoryServices.AccountManagement
 
Public Class WebForm1
    Inherits System.Web.UI.Page
 
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not User.Identity.IsAuthenticated Then
            Throw New InvalidOperationException("Specified user is not authenticated")
        End If
 
        If Not User.Identity.Name.StartsWith("DOMAIN\") Then
            Throw New InvalidOperationException("Specified user is not member of Domain")
        End If
 
        Dim userSid As String = CType(User.Identity, WindowsIdentity).User.Value
 
        Dim adContext As PrincipalContext = New PrincipalContext(ContextType.Domain, "Domain.local")
 
        Dim adUser As UserPrincipal = UserPrincipal.FindByIdentity(adContext, IdentityType.Sid, userSid)
 
        emaillbl.Text = adUser.EmailAddress
 
 
 
    End Sub
 
End Class

This example is c# (I switched…) – hopefully you can translate?

1
2
3
4
5
6
7
8
9
10
11
using System.Security.Principal;
using System.DirectoryServices.AccountManagement;
 ...
  
if (!User.Identity.IsAuthenticated) throw new InvalidOperationException("Specified user is not authenticated");
 if (!User.Identity.Name.StartsWith("Domain\\")) throw new InvalidOperationException("Specified user is not member of Domain");
  
string userSid = ((WindowsIdentity)User.Identity).User.Value;
 PrincipalContext adContext = new PrincipalContext(ContextType.Domain, "Domain.local");
 UserPrincipal adUser = UserPrincipal.FindByIdentity(adContext, IdentityType.Sid, userSid);
 ...