Saturday, March 5, 2016

I'm Excited About ASP.NET Core 1.0 (Former ASP.NET 5)

I love C# and .NET Framework - no doubt! If you've ever worked with C#/.NET, you won't want to use any other framework in your life. It's one language I know that was developed with the developer in mind. In fact, with so much abstraction built in, coding becomes something everyone can do with relative ease.

However, the fact that .NET framework is not cross-platform has always given me worries. Not because Microsoft platforms aren't rich enough - in fact, their platforms make life so much easier for you when you want to support your code in production environment; but because Windows hosting costs so much, compared to non-Windows (e.g. Linux) hosting. I understand that the reason is because of license fees for Windows OS, SQL Server, etc., but then, my pocket (and that of some of my clients) don't have the brains to understand that.

My alternative was to code in Java, PHP or other open source cross-platform languages, but having done C# for years, those languages appeared tortuous, verbose and inelegant, with so many disconnected bits and pieces of libraries scattered all around the internet (Well, Maven seems to address problem).

So, when I heard that Microsoft is working on ASP.NET 5 which will be both open source and cross platform, I was overjoyed! Sadly, shortly after announcing ASP.NET 5 RC 1 Update 1, Scott Hanselman declared the ASP.NET 5 project dead, and introduced the new ASP.NET Core 1.0 and .NET Core 1.0. Clearly that has set us a few months behind, but then I believe it's worth the wait.

ASP.NET Core 1.0

This means that in no time, I will be able to build cross-platform apps without having to switch from one language (and IDE) to another! Sure, the first release of .NET Core 1.0 will not be as feature rich as .NET Framework 4.6; it's cool to see the direction Microsoft is going.

As an aside, I now have a good reason to migrate from NHibernate to Entity Framework. NHibernate was so good to me, and I'll surely miss it. But EF7 has become a man, and it's time to consolidate all my development platforms into one.

Friday, March 4, 2016

C# - How To Get Logged In User's Username and Track Activities from Windows Service

If you've ever looked at Windows Event Logs, you must have noticed that it logs too many information, most of which are not useful when you're actually trying to do some kind of forensics. To reduce this noise, I went to build a proof-of-concept Windows Service whose only task is to log users' session activities (Lock, Unlock, Logout, Login, etc) in a different place, thus giving me a much smaller log file to dig through.

Typically, the main class in a Windows Service project is the class that inherits from ServiceBase. Luckily, ServiceBase has an override-able method that can be made to fire when a user's session activity changes. Add the code below in the class to override the method

protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
    //TODO: 
}

Unfortunately, the only information about the logged-on user that the incoming parameter carries is the Session ID, which is just an integer. To be useful, we have to find a way to get the username corresponding to that ID. In earlier versions of Windows, we would have easily written the code to run the powershell quser command to get us what we want; but that command is no longer available in Windows 10. To make matters a little worse, there is no (at least, I wasn't able to find any) .NET API to call to retrieve that info. So, I was left with no other option than to go old-school.

Add this code to the class inheriting from ServiceBase

[DllImport("Wtsapi32.dll")]
private static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WtsInfoClass wtsInfoClass, out IntPtr ppBuffer, out int pBytesReturned);
[DllImport("Wtsapi32.dll")]
private static extern void WTSFreeMemory(IntPtr pointer);

private enum WtsInfoClass
{
    WTSUserName = 5, 
    WTSDomainName = 7,
}

private static string GetUsername(int sessionId, bool prependDomain = true)
{
    IntPtr buffer;
    int strLen;
    string username = "SYSTEM";
    if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSUserName, out buffer, out strLen) && strLen > 1)
    {
        username = Marshal.PtrToStringAnsi(buffer);
        WTSFreeMemory(buffer);
        if (prependDomain)
        {
            if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSDomainName, out buffer, out strLen) && strLen > 1)
            {
                username = Marshal.PtrToStringAnsi(buffer) + "\\" + username;
                WTSFreeMemory(buffer);
            }
        }
    }
    return username;
}
If you don't have one already, add a constructor to the class; and add this line to it:
CanHandleSessionChangeEvent = true;
Now you're good to go! Access the username from the overridden method like this:
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
    string username = GetUsername(changeDescription.SessionId);
    //continue with any other thing you wish to do
}
- If you find this useful, let's hear from you.