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.

Wednesday 29 July 2015

Get the size of files having long path in C#.Net 3.5

Get the size of files having long path in  C#.Net 3.5


Suppose you want to get the size of all the files present in a directory. You would do something like this:

Method 1:

DirectoryInfo di = new DirectoryInfo(path); // path: It’s the path to the directory
FileInfo[] fInfos = di.GetFiles();
long fileSize = 0;
foreach (FileInfo fi in fInfos)
{     
fileSize = fi.Length;                   
}

But if you have files having full path (including directory name) more than 260 characters, “fi.Length” will throw FileNotFoundException. Even if the file exists, you will get this error, as shown in the screenshot below:


To fix this issue, include the following namespace:
using System.Reflection;
And use the below code:
public const int MAX_PATH = 260;
public const int MAX_ALTERNATE = 14;

[StructLayout(LayoutKind.Sequential)]
public struct FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
};

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct WIN32_FIND_DATA
{
public FileAttributes dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ALTERNATE)]
public string cAlternate;
}

[DllImport("kernel32", CharSet = CharSet.Unicode)]public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);

Define the below function that will return the size of files having long path:

private long getFileSize(string fileName, out WIN32_FIND_DATA findData)
        {
            long size = 0;
            IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
            IntPtr findHandle;

            findHandle = FindFirstFile(@"\\?\" + fileName , out findData);
            if (findHandle != INVALID_HANDLE_VALUE)
            {
                if ((findData.dwFileAttributes & FileAttributes.Directory) == 0)
                {
                    size = (long)findData.nFileSizeLow + (long)findData.nFileSizeHigh * 4294967296;
                }
            }
            return size;
        }

Update the Method 1 with the below code:



DirectoryInfo di = new DirectoryInfo(path); // path: It’s the path to the directory
FileInfo[] fInfos = di.GetFiles();
long fileSize = 0;
if (fi.Exists)
{
fileSize = fi.Length;
}
else
{
// This will handle long paths
FieldInfo fld = typeof(FileSystemInfo).GetField( "FullPath",  BindingFlags.Instance |
BindingFlags.NonPublic);
string fullPath = fld.GetValue(fi).ToString();
WIN32_FIND_DATA findData;
fileSize = getFileSize(fullPath, out findData);
}



The function getFileSize outputs “findData” of type WIN32_FIND_DATA. It can be used to get the attribute, created date, last access date and other properties of file having long path as below:

string attribute = findData.dwFileAttributes.ToString();

DateTime CreationTime = DateTime.FromFileTimeUtc((((long)findData.ftCreationTime.dwHighDateTime) << 32) | ((uint)findData.ftCreationTime.dwLowDateTime));

DateTime LastAccessTime = DateTime.FromFileTimeUtc((((long)findData.ftLastAccessTime.dwHighDateTime) << 32) | ((uint)findData.ftLastAccessTime.dwLowDateTime));

DateTime LastWriteTime = DateTime.FromFileTimeUtc((((long)findData.ftLastWriteTime.dwHighDateTime) << 32) | ((uint)findData.ftLastWriteTime.dwLowDateTime)); 

Wednesday 24 June 2015

Dealing with PathTooLongException while scanning files with long paths in C#

Suppose you have a list of object of class ‘FileInfo’. This class provide properties and instance methods for the creation, copying, deletion, moving, and opening of files, and aids in the creation of FileStream objects.
List<FileInfo> AllFilesInfo;
Let say you have a DataTable 'dt' with columns ‘Name’ and ‘Path’. For all the files in AllFilesInfo object, you want to fill in the name and directory name in dataTable.
foreach (FileInfo fileInfo in AllFilesInfo)
{
       DataRow dr = dt.NewRow();
       dr[0] = fileInfo.Name;
       dr[1] = fileInfo.DirectoryName;
}
The above code works well if you don’t have any file with long path. But as soon as, your code finds any such file, you will encounter the below exception:
PathTooLongException  was caught: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
When you debug and add a watch for fileInfo object, you will find both Directory and DirectoryName are throwing this exception. Now if you expand the ‘Non-public members’ and then the ‘base’ object of ‘fileInfo’, you will see a property named ‘FullPath’ giving the fullpath, which is what you need.


‘FullPath’ is a protected property of class ‘FileSystemInfo’, which is a base class for both ‘FileInfo’ and ‘DirectoryInfo’. You can access the path as follows:
DataRow dr = dt.NewRow();
dr[0] = fileInfo.Name;
try
{
    dr[1] = fileInfo.DirectoryName;
}
catch (PathTooLongException ex)
{
    FieldInfo fld = typeof(FileSystemInfo).GetField(
                                                    "FullPath",
                                                     BindingFlags.Instance |
                                                     BindingFlags.NonPublic);
    string fullPath = fld.GetValue(fileInfo).ToString();
    fullPath = fullPath.Remove(fullPath.IndexOf(fileInfo.Name)); // Removing file name from the fullpath
    dr[1] = fullPath;
}
By using the above code, you can access the fullPath of files with long names.

Tuesday 16 June 2015

Determining the owner of a file\folder in C#.Net and handling with general exceptions

Determining the owner of a file\folder in C#.Net

In order to determine the owner of a file/folder, you would need to use the classes from the following namespaces:
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;

Put the following lines in a function and it will give you the owner name:
FileSecurity fileSecurity = File.GetAccessControl(path);
IdentityReference sid = fileSecurity.GetOwner(typeof(SecurityIdentifier));
NTAccount ntAccount = sid.Translate(typeof(NTAccount)) as NTAccount;
string owner = ntAccount.Value;

Here ‘path’ is the location of the file/folder. 

General Exceptions:

1.  UnauthorizedAccessException: If you don’t have access to the file, you will get this exception.

2.  IdentityNotMappedException: “Some or all identity references could not be translated”


Whenever any user is created on the machine, a unique security ID is assigned. This SID can be converted to Domain Name\Username format by translating the SID to NTAccount type, but if that user is removed from the machine then only SID exists. Now if you try to convert the SID to NTAccount type, the above exception would occur. In that case, you may just want to display the SID as follows: 
IdentityReference sid = null;
string owner = null;
try
{
FileSecurity fileSecurity = File.GetAccessControl(path);
sid = fileSecurity.GetOwner(typeof(SecurityIdentifier));
NTAccount ntAccount = sid.Translate(typeof(NTAccount)) as NTAccount;
owner = ntAccount.Value;
}
catch (IdentityNotMappedException ex)
{
if (sid != null)
owner = sid.ToString();
}

Optimizing the code if you want to display the owner name of large number of files :

If you have too many files, getting the SIDs and translating it to DomainName\username(s) would be very time consuming. You can optimize the code by finding the SIDs for all the files and once you have all the SIDs, translate them to DomainName\username.

So, for example: If you have 10000 files, determine the SID for all the 10000 files. Let say you get 7 SIDs. Now translate only those 7 SIDs to DomainName\username format. It will definitely speed up the process.