Infosys Microsoft Alliance and Solutions blog

« August 2007 | Main | October 2007 »

September 13, 2007

Webinar: Leap Ahead with Windows Vista

Lead ahead with Windows Vista, September 19, 2007. Read More

September 6, 2007

LocBAML to Localize WPF Applications

Recently I was trying to work with LocBAML (default installation location - C:\Program Files\Microsoft SDKs\Windows\v6.0\Samples\WPFSamples\GlobalizationLocalization\LocBaml) to generate localized version of my WPF application. Things worked fine, but I did observe a few differences from what is documented on MSDN Site and this another detailed article. Two main observations

The command LocBaml.exe /parse HelloApp.resources.dll /out:c:\Hello.csv (as discussed in the MSDN article) didn't work. I got a "cannot load HelloApp.resources.dll or one of dependencies error". What worked for me was this - LocBaml.exe /parse HelloApp.g.en-US.resources /out:c:\Hello.csv.

Note that the *.g.en_US.resources file is found in the \Obj\Debug directory (for Debug build configuration).

The second observation is while generating the resources assembly by using the localized .csv file (say for fr-CA culture), the command LocBaml.exe /generate HelloApp.resources.dll /trans:Hello.csv /out:c:\ /cul:fr-CA works only if HelloApp.resources.dll is in the same folder already. This is strange, since the command is used to create the localized resource assembly and hence it can't be already present. I had to copy the existing english resource file in the same folder from where I was running this command and then pointed the output to a different location to get the french version of the localized resources assembly and then deploy it appropriately.

Will try this on VS 2008 Beta 2 also and see if there is a difference of behavior.

September 3, 2007

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 pinvoke.net 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);

 

        enum TOKEN_INFORMATION_CLASS

        {

            TokenUser = 1,

            TokenGroups,

            TokenPrivileges,

            TokenOwner,

            TokenPrimaryGroup,

            TokenDefaultDacl,

            TokenSource,

            TokenType,

            TokenImpersonationLevel,

            TokenStatistics,

            TokenRestrictedSids,

            TokenSessionId,

            TokenGroupsAndPrivileges,

            TokenSessionReference,

            TokenSandBoxInert,

            TokenAuditPolicy,

            TokenOrigin

        }

 

        struct TOKEN_USER

        {

            public SID_AND_ATTRIBUTES User;

        }

 

        struct SID_AND_ATTRIBUTES

        {

 

            public IntPtr Sid;

            public int Attributes;

        }

        #endregion

 

        public EventLogTest()

        {

            InitializeComponent();

            if(!EventLog.SourceExists("TestApplication"))

                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

            DeregisterEventSource(eventSrcHandle);

            Marshal.FreeHGlobal(userTokenInfo);

        }

    }

}

 

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 !

Optimizing Application Performance with Visual Studio 2005

To ensure optimum application performance in terms of throughput and processing speed, performance tuning can play a critical role. However, it could entail risks and unforeseen costs without the right approach. In order to reduce risks and costs, performance tuning should be treated as an integral part of a project life cycle. In most projects, however, it is conducted during the final stages of testing or when customers complain about performance post production deployment.

Performance tuning of .NET 2.0 applications can be achieved using Visual Studio 2005 tools. In this paper, I explore these tools and also provide guidelines on how to use them.

[Updated: 28th January 2008] This and other Infosys papers/handbooks on MS technologies can be found here.

Subscribe to this blog's feed

Follow us on

Blogger Profiles

Infosys on Twitter