Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Working in ASP Net with EventLog

2.60/5 (3 votes)
1 Feb 2010CPOL2 min read 25K   161  
Working in ASP Net with EventLog

 


Introduction

Here is some kind of solution for web or desctop developer's that need to know what kind of errors or events do client and want to store all logs on one computer. .Net framework 2.0 have some unsupported fetures while working with system EventLog. As example you cannot fill in "User" field using methods in EventLog class. Shurely you can use Log4net or other 3-rd party open source libs. But sometimes it will cause DLL-Hell or customer dont accept 3-rd party. So you need to find solution for pretty nice logging that can be easely filtred and so on.

Background

I will use unmanaged code to get needed result. I do not recommened to use such implementations. This is for situations when you cant grade target framework from 2.0 to 3.0 or 3.5 only.

Using the code

My simple application is able to place into event log to type of messages. With User name and without.

When you use standart EventLog methods you should get next message in System Journal.

Image 1
 

As you see User field(mark as red) is not planted. Now we will try to add it.

First of all you need to import next functions and declare next structures, variables.

 

VB.NET
        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function RegisterEventSource(ByVal machine As String, ByVal source As String) As IntPtr
        End Function

        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function DeregisterEventSource(ByVal handle As IntPtr) As Boolean
        End Function

        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function ReportEvent(ByVal hHandle As IntPtr,
ByVal wType As UShort, ByVal wCategory As UShort, ByVal dwEventID As
UInteger, ByVal uSid As IntPtr, ByVal wStrings As UShort, _
        ByVal dwDataSize As UInteger, ByVal lpStrings As String(), ByVal bData As Byte) As Boolean
        End Function

        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function GetTokenInformation(ByVal handle As
IntPtr, ByVal token As TOKEN_INFORMATION_CLASS, ByVal uSid As IntPtr,
ByVal size As UInteger, ByRef tokenSize As UInteger) As Boolean
        End Function

        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function OpenEventLog(ByVal lpUNCServerName As String, ByVal lpSourceName As String) As IntPtr
        End Function



        Protected Declare Function LogonUserA Lib "advapi32.dll" (ByVal lpszUsername As String, _

                               ByVal lpszDomain As String, _

                               ByVal lpszPassword As String, _

                               ByVal dwLogonType As Integer, _

                               ByVal dwLogonProvider As Integer, _

                               ByRef phToken As IntPtr) As Integer



        Protected Declare Auto Function DuplicateToken Lib "advapi32.dll" ( _

                                ByVal ExistingTokenHandle As IntPtr, _

                                ByVal ImpersonationLevel As Integer, _

                                ByRef DuplicateTokenHandle As IntPtr) As Integer



        Protected Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long

        Protected Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long



#End Region



#Region "API adaptation"
 

        Protected Enum TOKEN_INFORMATION_CLASS

            TokenUser = 1

            TokenGroups

            TokenPrivileges

            TokenOwner

            TokenPrimaryGroup

            TokenDefaultDacl

            TokenSource

            TokenType

            TokenImpersonationLevel

            TokenStatistics

            TokenRestrictedSids

            TokenSessionId

            TokenGroupsAndPrivileges

            TokenSessionReference

            TokenSandBoxInert

            TokenAuditPolicy

            TokenOrigin

        End Enum



        Protected Structure TOKEN_USER

            Public User As SID_AND_ATTRIBUTES

        End Structure



        Protected Structure SID_AND_ATTRIBUTES

            Public Sid As IntPtr

            Public Attributes As Integer

        End Structure



        Private LOGON32_LOGON_INTERACTIVE As Integer = 2

        Private LOGON32_PROVIDER_DEFAULT As Integer = 0



#End Region

 

Now we can add UserName to that field. 

VB.NET
Dim eventSrcHandle As IntPtr = RegisterEventSource(Nothing, iSource)
       Dim tokenInfoSize As UInteger = 0
       Dim userTokenPtr As IntPtr = WindowsIdentity.GetCurrent().Token
       'using this first call, get the length required to hold the token information in the tokenInfoSize parameter
       Dim bresult As Boolean = GetTokenInformation(userTokenPtr, TOKEN_INFORMATION_CLASS.TokenUser, IntPtr.Zero, tokenInfoSize, tokenInfoSize)
       Dim [error] As Integer = Marshal.GetLastWin32Error()
       Dim userTokenInfo As IntPtr = Marshal.AllocHGlobal(CInt(tokenInfoSize))
       'get the user token now with the pointer allocated to the right size
       bresult = GetTokenInformation(userTokenPtr, TOKEN_INFORMATION_CLASS.TokenUser, userTokenInfo, tokenInfoSize, tokenInfoSize)
       If bresult Then
           Dim tokUser As TOKEN_USER = DirectCast(Marshal.PtrToStructure(userTokenInfo, GetType(TOKEN_USER)), TOKEN_USER)
           Dim message As String() = New String(0) {}
           message(0) = iMessage
           bresult = ReportEvent(eventSrcHandle, iType, iCategory, iEventID, tokUser.User.Sid, 1, _
             0, message, New Byte())
       End If
       'Clean up
       DeregisterEventSource(eventSrcHandle)
       Marshal.FreeHGlobal(userTokenInfo)

 

On ASP.net web site if we log error in such manner we should get next in System Journal.

 

Image 2

 That's look's much more better, than previous one. But what we might do if we want to log user that do some action with our web-application.

Here we must have a deep look on UserTokens. 

So the first point that we cannot make event with user 'X' if there is no such user in current domain.

Point second we must know password if our site is running on a muchine with not Server OS(for example Windows XP).

Third point if our Machine is Windows 2003 or something like this. You can get UserToken using UPN user name 'name@domainname.machinename.com'(UPN might look like this). And you don't need to import LogonUserA function to get UserToken. You can use System.Security.Principal namespace to get it

And at last. System call ReportEvent will logon event with owner of process UserName. So to change UserName you need to do impersonation. The next piece of code might help.

VB.NET
If impersonateValidUser("user", "domain", "password") Then
            undoImpersonation()
            Else
                ''TODO support impersonation fault
            End If 
VB.NET
 Private Function impersonateValidUser(ByVal userName As String, _
ByVal domain As String, ByVal password As String) As Boolean

            Dim tempWindowsIdentity As WindowsIdentity
            Dim token As IntPtr = IntPtr.Zero
            Dim tokenDuplicate As IntPtr = IntPtr.Zero
            impersonateValidUser = False

            If RevertToSelf() Then
                If LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token) <> 0 Then
                    If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
                        tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
                        impersonationContext = tempWindowsIdentity.Impersonate()
                        If Not impersonationContext Is Nothing Then
                            impersonateValidUser = True
                        End If
                    End If
                End If
            End If
            If Not tokenDuplicate.Equals(IntPtr.Zero) Then
                CloseHandle(tokenDuplicate)
            End If
            If Not token.Equals(IntPtr.Zero) Then
                CloseHandle(token)
            End If
        End Function

Points of Interest 

This can be used not only for logging. I think this piece of code can be used to do any type of secure actions in AD

History 

Version 1.0.0

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)