Infosys Microsoft Alliance and Solutions blog

« Optimizing Application Performance with Visual Studio 2005 | Main | LocBAML to Localize WPF Applications »

Logging Events with User details

The other day a colleague asked about how to provide information for the "User" column in the Event log. The EventLog.WriteEntry method has various overloads, but none that allows you to pass the user details.

There is an EventLogEntry class also that .NET provides, which has a UserName member, but it is read only property and is used to query event entries from the log. This means that by using .NET APIs there isn't a way to provide the user name for event log entries.

All is not lost.  We can use pinvoke and work with Win32 API for this purpose. The API that helps log events is ReportEvent. I used to find out the C# equivalent for this API and followed this example to write the code. However I kept getting error for the User SID details.

This article showed the details of how to get the User SID (as C code) and following this, I then used the C# equivalent of GetTokenInformation method and got the code working. The code snippent is as below. A few points of explanation on the code

1. Quite a few Win32 APIs work in the mode where they need to be called twice. The first time they are used to return the size of buffer/memory required to hold the information and then another call to get the actual value. the GetTokenInformation is used in the same manner.

2. In .net, when using EventLog.WriteEntry method, the Source is either passed as a parameter or needs to be set upfront. The later technique is what is used in Win32APIs. You need to call RegisterEventSource with the source name and get the event log handle. This handle is then used to log messages. When done, the DeregisterEventSource cleans up.

3. Though ReportEvent method provides a mechanism to pass an array of strings which are concatenated when the event is logged. However I could not get it to work. Even if I passed more strings with the right value as number of strings, still only the first one got logged. However that isn't a big handicap as we can then do the concatenation in our code itself and then pass the final string.

4. Most Win32 APIs in case of error return FALSE and set a global error value which is retrieved using GetLastError API. In .NET, you should however, use the Marshal.GetLastWin32Error. The code snippet below shows the usage, but I haven't done any action based on it. Based on the error value,  you can view the list of system error codes and see what the error is all about and take corrective action.

Source Code

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Diagnostics;

using System.Runtime.InteropServices;

using System.Security.Principal;


namespace GenericTestApp


    public partial class EventLogTest : Form


        #region Imports for Event logging

        [DllImport("advapi32.dll", SetLastError = true)]

        static extern IntPtr RegisterEventSource(string machine, string source);


        [DllImport("advapi32.dll", SetLastError = true)]

        static extern bool DeregisterEventSource(IntPtr handle);


        [DllImport("advapi32.dll", SetLastError = true)]

        static extern bool ReportEvent(IntPtr hHandle, ushort wType, ushort wCategory, uint dwEventID, IntPtr uSid, ushort wStrings, uint dwDataSize, string[] lpStrings, byte bData);


        [DllImport("advapi32.dll", SetLastError = true)]

        static extern bool GetTokenInformation(IntPtr handle, TOKEN_INFORMATION_CLASS token, IntPtr uSid, uint size, out uint tokenSize);




            TokenUser = 1,



















        struct TOKEN_USER


            public SID_AND_ATTRIBUTES User;



        struct SID_AND_ATTRIBUTES



            public IntPtr Sid;

            public int Attributes;




        public EventLogTest()




                EventLog.CreateEventSource("TestApplication", "Application");



        private void btnLogEvent_Click(object sender, EventArgs e)


            IntPtr eventSrcHandle = RegisterEventSource(null, "TestApplication");

            uint tokenInfoSize = 0;

            IntPtr userTokenPtr = WindowsIdentity.GetCurrent().Token;


            //using this first call, get the length required to hold the token information in the tokenInfoSize parameter

            bool bresult = GetTokenInformation(userTokenPtr, TOKEN_INFORMATION_CLASS.TokenUser, IntPtr.Zero, tokenInfoSize, out tokenInfoSize);

            int error = Marshal.GetLastWin32Error();


            IntPtr userTokenInfo = Marshal.AllocHGlobal((int)tokenInfoSize);


            //get the user token now with the pointer allocated to the right size

            bresult = GetTokenInformation(userTokenPtr, TOKEN_INFORMATION_CLASS.TokenUser, userTokenInfo, tokenInfoSize, out tokenInfoSize);


            if (bresult)


                TOKEN_USER tokUser = (TOKEN_USER)Marshal.PtrToStructure(userTokenInfo, typeof(TOKEN_USER));


                string[] message = new string[1];

                message[0] = "Message Logged at - " + DateTime.Now.ToString();

                bresult = ReportEvent(eventSrcHandle, 0, 0, 0, tokUser.User.Sid, 1, 0, message, new byte());



            //Clean up







The code is the code behind file of a simple WinForm application with just a button on it, on click of which, the event log entry is logged using the Process Identity. The code works in a web application as well. 

Comments are welcome !


Very helpful example. Thanks.

Reaaly thanks for giving me this valuable info.

the messages are logging as information how to do error log?

also how to set the catogory?

thanks friend

I know this is a really old post, but it was helpful to me initially and I thought I'd add some info for others who happen upon it.

You don't need to call GetTokenInformation at all, which means you can significantly reduce this code. Most of this code exists to get the SID by calling GetTokenInformation. As it turns out, you can call ReportEvent with a byte array with the SID instead of the pointer. This improves reliability and removes the need to maintain pointers to keep the info around.

1. In the ReportEvent definition, change "IntPtr uSid" to "byte[] uSid"
2. Get a byte array for the SID by doing this:
var sid = WindowsIdentity.GetCurrent().User;
var sidBytes = new byte[sid.BinaryLength];
sid.GetBinaryForm(sidBytes, 0);
3. Call ReportEvent with the byte array for the SID parameter.

That's it. Hope this helps someone.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Please key in the two words you see in the box to validate your identity as an authentic user and reduce spam.

Subscribe to this blog's feed

Follow us on

Blogger Profiles

Infosys on Twitter