Thursday, 26 November 2015

How to make the Windows Desktop application work well on High-DPI Displays and Fix Blurry Fonts

If you change the DPI of your machine from 100% to any other higher value, your desktop applications may appear somewhat blurry when you compare them with other applications on the screen. Alternately, display problems may exist such as truncated text.



In order to fix this issue, you would need to Disable DPI virtualization for the application. To do this, right-click the application’s shortcut and then click Properties. On the Compatibility tab, select Disable Display Scaling on High DPI Settings, and then click OK.




Alternatively, you may also disable this setting using the below Registry keys.

Registry key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers

String: “Path to your application exe”
Value:   ~ HIGHDPIAWARE
Please note there is a space between ‘tilde’ and ‘HIGHDPIAWARE’.



There are number of ways to set a value in the registry. Below steps will show the process to set this value in registry using Inno setup:

var
str_MyAppFullExePath: String;

str_MyAppFullExePath := ExpandConstant('{app}') ;

RegWriteStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers', str_MyAppFullExePath + '\' + '{#MyAppExeName}' , '~ HIGHDPIAWARE');

Possible Issues:

If your application is 32 bit, then the above command will set the value at

“HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers” 

instead of 

“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers”. 

This will not set the “Disable Display Scaling on High DPI Settings” property. This can be fixed by using the below command:

if IsWin64 then
 begin
 RegWriteStringValue(HKLM64, 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers', str_MyAppFullExePath + '\' + '{#MyAppExeName}' , '~ HIGHDPIAWARE');
 end
 else
 begin
 RegWriteStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers', str_MyAppFullExePath + '\' + '{#MyAppExeName}' , '~ HIGHDPIAWARE');
 end ;

It will force the setup to store value in 64 bit registry.

Monday, 23 November 2015

How to extract the associated icon of any file in C#.Net

To extract icon of any file, define the below class and the structure:

[StructLayout(LayoutKind.Sequential)]
        public 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;
        };


class Win32
        {
            public const uint SHGFI_ICON = 0x100;
            public const uint SHGFI_LARGEICON = 0x0;    // 'Large icon
            public const uint SHGFI_SMALLICON = 0x1;    // 'Small icon

            [DllImport("shell32.dll")]
            public static extern IntPtr SHGetFileInfo(string pszPath,
                                        uint dwFileAttributes,
                                        ref SHFILEINFO psfi,
                                        uint cbSizeFileInfo,
                                        uint uFlags);

            [DllImport("User32.dll")]
            public static extern int DestroyIcon(IntPtr hIcon);
        }

Now define the following function:

SHFILEINFO shinfo = new SHFILEINFO();
/* Define it globally so that you can use the same object for all  the files. */
public Bitmap extractAssociatedIcon(string fName)
        {
            Icon myIcon = null;
            try
            {
                IntPtr hImgSmall;    //the handle to the system image list              

                hImgSmall = Win32.SHGetFileInfo(fName, 0, ref shinfo,
                                 (uint)Marshal.SizeOf(shinfo),
                                  Win32.SHGFI_ICON |
                                  Win32.SHGFI_SMALLICON);

                if (shinfo.hIcon != IntPtr.Zero)
                    myIcon = Icon.FromHandle(shinfo.hIcon);              

            }
            catch (Exception ex)
            {
                // Handle any exception here
            }
            return myIcon.ToBitmap();
        }


Get the icon by calling the above defined function.

Bitmap image = extractAssociatedIcon("fullPath to the file");

Once you get the icon, make sure you destroy the icon. This is very important, else you will run into memory issues.

Win32.DestroyIcon(shinfo.hIcon);




Friday, 20 November 2015

Dealing with “Application has stopped responding” issue in C#.Net

Suppose in your application, on a click event you are doing some calculations which is taking a lot of time. During that time, if a user perform unnecessarily clicks on the screen, it might give a “Page is not responding” or "Application has stopped responding" error.

This can be resolved by following the below steps:

1.   Put your long running code in a thread.

2. Set “Cursor.Current = Cursors.WaitCursor;”

3. UseWaitCursor = true;

4. Inside the thread use the try-finally block.

5. In the finally block, set “this.Cursor = Cursors.Default;”

6. UseWaitCursor = false;

7. Start that thread.

Example:
     Cursor.Current = Cursors.WaitCursor;
     UseWaitCursor = true;

            new Thread(() =>
            {
     try
            {                             
                // Your long running code                                   
            }
            catch (Exception ex)
            {
                 // Handle any exception here
            }
            finally
            {                           
  Cursor.Current = Cursors.Default;
                UseWaitCursor = false;
            }
           }) { Name = "longTask" }.Start();


Now even if you click 100 times, the page will not show “Not Responding” error.

Wednesday, 18 November 2015

Adding a new item to context menu of Windows Explorer in C#.Net



Requirement: Whenever user right clicks on any folder in windows explorer, display a new option and if user selects that option, launch your application. The below steps will add a new option on right click of any folder:

You would need to add 2 sub keys in registry at the below location:

HKEY_CLASSES_ROOT\Folder\shell

Key 1: Folder\\shell\\MyProduct

Set the value of above key and the same will be shown in the context menu. You can also add a icon to the context menu item by adding a value to the key 1 and name it "Icon" as shown below:

Icon  "Path to your icon file"

Key 2: Folder\\shell\\MyProduct\\command

Set the value of above key and it will launch the application:

Eg. C:\Program Files (x86)\myProduct.exe "%1"

Here "%1" will give the path of the folder which is right clicked.



Programmatically this can be done as below:

private static void updateRegistryToAddContextMenu()
        {
            string MenuName = "Folder\\shell\\MyProduct";
            string Command = "Folder\\shell\MyProduct\\command";

            RegistryKey regmenu = null;
            RegistryKey regcmd = null;
            try
            {
                regmenu = Registry.ClassesRoot.CreateSubKey(MenuName);
                if (regmenu != null)
                    regmenu.SetValue("","New context menu option");
                regcmd = Registry.ClassesRoot.CreateSubKey(Command);
                if (regcmd != null)
                {
                    string exePath = Application.ExecutablePath;
                    regcmd.SetValue("", exePath + " " + "\"" + "%1" + "\"");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);

            }
            finally
            {
                if (regmenu != null)
                    regmenu.Close();
                if (regcmd != null)
                    regcmd.Close();
            }
        }

Thursday, 1 October 2015

Enumerate folder with “No Name” (blank space folder, AKA non-breaking space) in C#.Net:

A folder with no name can be created by following the below steps:
  1. Right-click on the folder and select Rename.
  2. Now press the Alt key and from the Numeric keypad, press 0160.
  3. Now press Enter or click anywhere on the desktop.


If you want to retrieve all the files and folders from any directory (in C#), you would do something like this:

string path = “Path to the directory”;
DirectoryInfo di = new DirectoryInfo(path);
foreach (DirectoryInfo dirInfo in di.GetDirectories())
{
       FileInfo[] fInfos = dirInfo.GetFiles();
       // You can easily iterate through the files here
}

But if you have any folder with a blank space as name, the above method would give you wrong result. You would need to make few changes as shown below:

string path = “Path to the directory” + “\\”;
DirectoryInfo di = new DirectoryInfo(path);
foreach (DirectoryInfo dirInfo in di.GetDirectories())
{
     if (dirInfo.Name == "\u00A0")
     {
          string fullPath = dirInfo.FullName + "\\";
          dirInfo = new DirectoryInfo(fullPath);
     } 

     FileInfo[] fInfos = dirInfo.GetFiles();
     // You can easily iterate through the files
}
  

Reduce flickering from controls in C#.Net

Flickering effect can be reduced by double buffering the controls. The below code will help you in reducing the flickering effect from any type of control.

public static class ControlExtensions
    {
        public static void SetDoubleBuffered(System.Windows.Forms.Control c)
        {
            if (System.Windows.Forms.SystemInformation.TerminalServerSession)
                return;

            System.Reflection.PropertyInfo aProp =
                  typeof(System.Windows.Forms.Control).GetProperty(
                        "DoubleBuffered",
                        System.Reflection.BindingFlags.NonPublic |
                        System.Reflection.BindingFlags.Instance);

            aProp.SetValue(c, true, null);
        }
    }

Whether it is a panel, or a Tab control, you can call the above defined function:

ControlExtensions.SetDoubleBuffered(MenuPanel);
ControlExtensions.SetDoubleBuffered(settingsTabControl);

Friday, 25 September 2015

Copy, Move and Delete Files and Folders in C#:


Below are the commonly used methods (using “System.IO” namespace):

For deleting:
 File:          File.Delete(“Path to the file”);
 Folder:     Directory.Delete(“Path to the folder”);

The above methods will delete the file \ folder permanently. User cannot restore them back.

For Copying:
File:          File. Copy(“source path” , “destination path”);
 Folder:     You will have to recursively traverse through the source directory and create directory and copy files.

For Moving:
File:          File.Move(“source path” , “destination path”);
 Folder:     Directory.Move(“ source path” , “destination path”);

The above methods wouldn’t display the error dialogs, in case of any error OR the progress dialog to show the progress.

"Microsoft.VisualBasic.FileIO"namespace provides a class “FileSystem” that shows all the dialog such as Error dialog, Progress dialog etc. and it’s very simple to use. 



For deleting:
 File:          FileSystem.DeleteFile(“Path to the file”, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);

 Folder:     FileSystem.DeleteDirectory(“Path to the file”, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);

Here  3rd Argument:  RecycleOption also provide 2 options:
  • Delete to Recycle Bin.
  • Delete permanently.


For Copying:
File:          FileSystem.CopyFile(“ source path” , “destination path”, UIOption.OnlyErrorDialogs);
Folder:     FileSystem.CopyDirectory(sourceFile, destFile, UIOption.OnlyErrorDialogs);

For Moving:
File:          FileSystem.MoveFile(sourcePath, targetPath, UIOption.OnlyErrorDialogs);
Folder:     FileSystem.MoveDirectory(sourcePath, targetPath, UIOption.OnlyErrorDialogs);


Argument UIOption passed above provide 2 options:
  • To show only error dialogs
  •  To show all dialogs.