dotnet thoughts 

a dotnet developer's technical blog

How to get the current battery level in C#

Today I found one blogpost related to how to get laptop battery level. But it was using WMI, and it was slow. Then after searching in MSDN, I found PowerStatus class, which is part of System.Windows.Forms namespace, which can be used to get the battery level. Here is a simple implementation, which shows battery status as well as system connected to AC power.

And here is the code snippet.

PowerStatus p = SystemInformation.PowerStatus;
progressBar1.Value = (int)(p.BatteryLifePercent * 100);
label1.Text = string.Format("{0}%", (p.BatteryLifePercent * 100));
radioButton1.Checked = p.PowerLineStatus == PowerLineStatus.Online;
radioButton2.Checked = p.PowerLineStatus == PowerLineStatus.Offline;
radioButton3.Checked = p.PowerLineStatus == PowerLineStatus.Unknown;

And here is the application running on my laptop

Current Battery Status

Current Battery Status

Happy Coding

Drive combo box in C#

As most of the .Net developers, I am also started my development career with VB6. :) VB6 comes with few file system controls, like DriveListbox, DirectoryListbox, FileListbox etc. But in .Net framework didn’t support these controls. Here is a solution for creating a drive listbox(it is not a listbox, it is dropdown list).

Drive Combo box in C#

Drive Combo box in C#

The drive information can be retrieved using DriveInfo.GetDrives() method. And for getting the corresponding icons for drive letters, I am using the SHGetFileInfo WIN32 API call.

Here is the implementation.

private void Form1_Load(object sender, EventArgs e)
{
    comboBox1.DrawMode = DrawMode.OwnerDrawFixed;
    comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
    comboBox1.DrawItem += new DrawItemEventHandler(comboBox1_DrawItem);

    var drives = DriveInfo.GetDrives()
        .Where(drive => drive.Name != string.Empty).ToArray();
    comboBox1.Items.AddRange(drives);
}

And here is the DrawItem event

private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
    if (e.Index != -1)
    {
        e.DrawBackground();
        var icon = GetIcon(comboBox1.Items[e.Index].ToString());
        e.Graphics.DrawIcon(icon, 3, e.Bounds.Top);
        e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(),
            comboBox1.Font, Brushes.Black, icon.Width + 2, e.Bounds.Top);
        e.DrawFocusRectangle();
    }
}

And here is the GetIcon method and API declaration.

private const uint SHGFI_ICON = 0x100;
private const uint SHGFI_LARGEICON = 0x0;
private const uint SHGFI_SMALLICON = 0x1;

[DllImport("shell32.dll")]
private static extern IntPtr SHGetFileInfo(string pszPath,
                            uint dwFileAttributes,
                            ref SHFILEINFO psfi,
                            uint cbSizeFileInfo,
                            uint uFlags);
[StructLayout(LayoutKind.Sequential)]
private struct SHFILEINFO
{
    public IntPtr hIcon;
    public IntPtr iIcon;
    public uint dwAttributes;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
    public string szDisplayName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
    public string szTypeName;
};

private Icon GetIcon(string fileName)
{
    IntPtr hImgSmall;
    SHFILEINFO shinfo = new SHFILEINFO();
    hImgSmall = SHGetFileInfo(fileName, 0, ref shinfo,
                (uint)Marshal.SizeOf(shinfo),
                    SHGFI_ICON |
                    SHGFI_SMALLICON);
    return Icon.FromHandle(shinfo.hIcon);
}

This implementation is using normal Combobox, you can do same thing by extending Combobox. So that it can be used in any project without any dependency.

Fuslogvw.exe and diagnosing .NET assembly binding issues

Yesterday while working with NUnit, I got an exception like this

System.IO.FileLoadException : Could not load file or assembly 'nunit.framework, Version=2.5.7.10213, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)For further information, use the Exception Details menu item.

System.IO.FileLoadException : Could not load file or assembly 'nunit.framework, Version=2.5.7.10213, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)For further information, use the Exception Details menu item.

It was working fine day before yesterday, and I didn’t modified any system settings, so I didn’t understand what went wrong. Instead of looking into more details, I reinstalled NUnit, but it didn’t worked. :( Then I tried with NUnit console application, and it was working fine. :) So I come to know, it was not NUnit problem. Then I tried to diagnose the problem, using Fusion Log Viewer. (FUSLOGVW.exe). From MSDN The Assembly Binding Log Viewer displays details for assembly binds. This information helps you diagnose why the .NET Framework cannot locate an assembly at run time. These failures are usually the result of an assembly deployed to the wrong location, a native image that is no longer valid, or a mismatch in version numbers or cultures. The common language runtime’s failure to locate an assembly typically shows up as a TypeLoadException in your application.You can launch FUSLOGVW.exe, from Visual Studio command prompt, or from Programs > Microsoft Windows SDK v7.1 > Fusion Log Viewer. If your OS is UAC enabled, make sure you are running Fusion Log Viewer as Administrator, otherwise few settings will be disabled. You also need to modify few settings of Fusion Log Viewer for enabling the Assembly binding logging. Click on the settings button, Enable Log all binds to disk and Custom log path, and provide a path in the textbox for storing the log files.

 Fusion Log Viewer - Settings

Fusion Log Viewer - Settings

After launching Fusion Log Viewer run the application to reproduce the issue.

 Fusion Log Viewer - All binding details

Fusion Log Viewer - All binding details

After you getting the error, click Refresh button of Fusion Log Viewer, it will display binding details in the list view and you can double click on each item and find the exact issue. In my scenario, I some how copied some old NUnit Framework assembly to the location of test assembly, and NUnit GUI application was trying to load that assembly instead of the valid one. I removed the assembly and it started working fine.

The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command-line sxstrace.exe tool for more detail

Today I got a comment from Vitor, Vitor is getting an exception like this while starting application.

The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command-line sxstrace.exe tool for more detail.

So I thought of work around this issue. To reproduce the issue, I tampered the exe.config file of one of my application, by removing <configuration> tag from the file. And it started throwing the exception.

The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command-line sxstrace.exe tool for more detail.

The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command-line sxstrace.exe tool for more detail.

I verified the same with EventViewer, and I found a message like this, under Windows Logs > Application

Activation context generation failed for “C:\CaptureItPlus\Binaries\CaptureItPlus.exe”. Error in manifest or policy file “C:\CaptureItPlus\Binaries\CaptureItPlus.exe.Config” on line 1. Invalid Xml syntax.

The error message is pretty self explanatory. If you are not able to access the event viewer, the other option is using sxstrace.exe tool. You can enable run the sxstrace in trace mode, you can do this by following command.

sxstrace.exe Trace -logfile:C:\captureittrace.log

And execute the application. After getting exception, stop the tracing and you can parse the log file using following command.

sxstrace.exe Parse -logfile:C:\captureittrace.log -outfile:C:\captureittrace.txt

It is required because .log is a binary file. Now open the .txt file, you can see similar message,

INFO: Parsing Application Config File C:\CaptureItPlus\Binaries\CaptureItPlus.exe.Config.
ERROR: Line 1: XML Syntax error.
ERROR: Activation Context generation failed.
End Activation Context Generation.

Now open the .exe.config and add the missing <configuration>, now double click on the application, this issue will be resolved. :)

How to add MEX endpoints programmatically

The Metadata Exchange Endpoint (MEX) is a special endpoint in WCF that exposes metadata used to describe a service.Without the MEX, you will not be able to use svcutil.exe to automatically generate a proxy class. Fortunately, it is a simply process to enable the MEX for your service. Here is the code which will add Mex endpoint programmatically.

var metadataBehavior = serviceHost.Description.Behaviors.Find<ServiceMetadataBehavior>()
    ?? new ServiceMetadataBehavior();
serviceHost.Description.Behaviors.Add(metadataBehavior);
serviceHost.AddServiceEndpoint(typeof(IMetadataExchange),
    MetadataExchangeBindings.CreateMexHttpBinding(),
    "http://localhost:8080/IService/Mex");

It should be noted that you are not required to enable HttpGet. SvcUtil will still be able to access the MEX without it. However, it is useful if you want to view the WSDL from a browser by going to the address of the service. You cannot do so without enabling the HttpGet.

« Newer PostsOlder Posts »