A little FileIconInit for the System Image list

I can hear the theme for Welcome Back Kotter as I write this. It seems that things have been quiet on here for quite sometime, however those that know me can attest to the fact that life away from here has been anything but that. Thankfully, things have returned to a point where a lot more content should find its way onto this world.

As things progress, I am doing a substantial amount of development using C# and the .NET Framework. I still continue to develop new utilities that satisfy my specific needs. I also try to update some of the available utilities. The most recent update has been to ASysIcon (this application was redeveloped using the .NET Framework). I've done quite a bit of work with icons in the past (also take a look at AIconExtract) and ASysIcon is a utility that displays the images found in the system image list. The system image list contains the icons that are associated with the different registered file types on a computer.

The retrieval of the system image list is a straight forward process. There are a few unmanaged API calls that work quite well for this task. One thing that may not be so apparent is that the the images retrieved will be those images that are cached in the system image list. If the system hasn't had a need to load them, well, they won't be in the list. This could pose some concern if you would like to retrieve the entire list. The trick is to get all the system images cached. Actually, this isn't really a trick. With little searching the FileIconInit function surfaces. This function will initialize or reinitialize this image list. FileIconInit has a boolean parameter determines if it should restore the image cache from disk. Passing true as this parameter will load all of the images, not just the recently cached ones. The following sample code shows how to display the system image list in a ListView:


1using System;
2using System.Windows.Forms;
3
4 using System.Runtime.InteropServices; // http://support.microsoft.com/kb/319350
5
6namespace SysIcons1
7{
8 public partial class Form1 : Form
9 {
10 [StructLayout(LayoutKind.Sequential)]
11 public struct SHFILEINFO
12 {
13 public IntPtr hIcon;
14 public IntPtr iIcon;
15 public uint dwAttributes;
16 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
17 public string szDisplayName;
18 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
19 public string szTypeName;
20 }
;
21 //Retrieve the handle to the icon that represents the file and the index of the icon within the system image list
22 public const uint SHGFI_ICON = 0x100;
23
24 // Retrieve the index of a system image list icon.
25 public const uint SHGFI_SYSICONINDEX = 0x4000;
26
27 // Large icon
28 public const uint SHGFI_LARGEICON = 0x0;
29
30 // Small icon
31 public const uint SHGFI_SMALLICON = 0x1;
32
33 public const uint LVM_FIRST = 4096;
34 public const uint LVSIL_NORMAL = 0;
35 public const uint LVSIL_SMALL = 1;
36 public const uint LVM_SETIMAGELIST = LVM_FIRST + 3;
37 // need this style on Windows 9x
38 public const uint LVS_SHAREIMAGELISTS = 0x0040;
39
40 // Retrieves information about an object in the file system, such as a file, folder, directory, or drive root.
41 [DllImport("shell32.dll")]
42 public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
43
44 // The SendMessage function sends the specified message to a window or windows.
45 [DllImport("user32.dll")]
46 private static extern UInt32 SendMessage(IntPtr hWnd, UInt32 Msg,
47 UInt32 wParam, UInt32 lParam);
48
49 // Destroys an icon and frees any memory the icon occupied.
50 [DllImport("User32.dll")]
51 private static extern int DestroyIcon(System.IntPtr hIcon);
52
53 // Initializes or reinitializes the system image list.
54 // FileIconInit is not included in a header file. You must call it directly from Shell32.dll, using ordinal 660.
55 [DllImport( "Shell32.dll", EntryPoint = "#660")]
56 private static extern bool FileIconInit( bool fRestoreCache );
57
58 // Retrieves the number of images in an image list.
59 [DllImport( "comctl32.dll")]
60 private static extern int ImageList_GetImageCount( IntPtr himl);
61
62 public Form1()
63 {
64 InitializeComponent();
65 }

66
67 private void Form1_Load(object sender, EventArgs e)
68 {
69 IntPtr hImgSmall; //handle to the small system image list
70 IntPtr hImgLarge; //handle to the large system image list
71 SHFILEINFO shinfo = new SHFILEINFO();
72
73 FileIconInit(true);
74
75 //Small Icons
76 hImgSmall = SHGetFileInfo("", 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_SYSICONINDEX SHGFI_SMALLICON);
77
78 //Large Icons
79 hImgLarge = SHGetFileInfo("", 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_SYSICONINDEX SHGFI_LARGEICON);
80
81 // Set Icon Images
82 SendMessage(listView1.Handle, LVM_SETIMAGELIST, LVSIL_SMALL, (uint)hImgSmall.ToInt32());
83 SendMessage(listView1.Handle, LVM_SETIMAGELIST, LVSIL_NORMAL, (uint)hImgLarge.ToInt32());
84
85 for ( int i = 0; i < ImageList_GetImageCount(hImgLarge) ; i++ )
86 {
87 listView1.Items.Add(i.ToString(),i);
88 }

89 }

90
91 private int Get_Image_Index(string filename)
92 {
93 IntPtr hImgSmall;
94 SHFILEINFO shinfo = new SHFILEINFO();
95
96 hImgSmall = SHGetFileInfo(filename, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo.GetType()), SHGFI_ICON SHGFI_SMALLICON);
97 DestroyIcon(shinfo.hIcon);
98 return (int)shinfo.iIcon;
99 }

100 }



   

A DataGridView, DataGridViewCheckBoxColumn, DataGridViewButtonColumn and DataColumn.

As I continue to work on moving (upgrading?) applications and code to dotNET 2.0 I continue to find that in some cases certain implemenations have made changes that simplify things. One case in particular, is the displaying of CheckBoxes and Buttons in a DataGrid. One old way was to override the painting of a cell. Another might have been to overaly a control in the clipped rectangle of the DataGrid.

As with any “upgrade” new “things” are introduced. In this case I will refer to the DataGridView, DataGridViewCheckBoxColumn and the DataGridViewButtonColumn classes. The allows for the displaying of “data” in a grid (Note: when working with large amounts of data don’t forget to set the VirtualMode property). This source of the data can be anything that implements a IList, IListSource, IBindingList or IBindingListView interface. The data displayed doesn’t always have to be from a database, a one dimmensional array alos works well with a datagrid (take a look at the sample method PopulateDataGridView). It may sound intimidating, but this is all fairly straght forward.

using System;
using System.Data;
using System.Windows.Forms;

namespace DrawInDataGrid1
{
    public partial class Form1 : Form
    {
        private DataTable myTable;
        private DataGridViewCheckBoxColumn checkcolumn;
        private DataGridViewButtonColumn buttoncolumn;
        private DataColumn colItem1, colItem2, colItem3;
        private DataRow NewRow;
        private DataView myView;

        public Form1()
        {
            InitializeComponent ();
        }

        private void Form1_Load( object sender, EventArgs e )
        {
            // DataTable to hold data that is displayed in DataGrid
            myTable = new DataTable ( "myTable" );

            // the three columns in the table
            colItem1 = new DataColumn ( "CheckBox", Type.GetType ( "System.Boolean" ) );
            colItem2 = new DataColumn ( "Button", Type.GetType ( "System.String" ) );
            colItem3 = new DataColumn ( "String", Type.GetType ( "System.String" ) );

            // add the columns to the table
            myTable.Columns.Add ( colItem1 );
            myTable.Columns.Add ( colItem2 );
            myTable.Columns.Add ( colItem3 );

            checkcolumn = new DataGridViewCheckBoxColumn ( false );
            checkcolumn.HeaderText = "CheckBox";
            checkcolumn.DataPropertyName = "CheckBox";

            buttoncolumn = new DataGridViewButtonColumn ();
            buttoncolumn.HeaderText = "Button";
            buttoncolumn.DataPropertyName = "Button";

            dataGridView1.Columns.Add ( checkcolumn );
            dataGridView1.Columns.Add ( buttoncolumn );


            // Fill in some data
            NewRow = myTable.NewRow ();
            NewRow[0] = true;
            NewRow[1] = "0";
            NewRow[2] = "Test";
            myTable.Rows.Add ( NewRow );

            NewRow = myTable.NewRow ();
            NewRow[0] = false;
            NewRow[1] = "1";
            NewRow[2] = "Next One";
            myTable.Rows.Add ( NewRow );

            // DataView for the DataGridView
            myView = new DataView ( myTable );
            myView.AllowDelete = false;
            myView.AllowEdit = false;
            myView.AllowNew = false;

            // add an event to check for button click
            this.dataGridView1.CellContentClick +=
                new System.Windows.Forms.DataGridViewCellEventHandler ( this.dataGridView1_CellContentClick );

            // Assign DataView to DataGrid
            dataGridView1.DataSource = myView;
        }


        private void dataGridView1_CellContentClick( object sender, DataGridViewCellEventArgs e )
        {
            if ( sender is DataGridView )
            {
                if ( ( ( (DataGridView) sender ).Columns[e.ColumnIndex] is DataGridViewButtonColumn ) &&
                    ( e.RowIndex >= 0 ) )
                {
                    MessageBox.Show ( e.RowIndex.ToString () );
                }
            }
        }
    }
}


Another “column” I am glad they added is the DataGridViewImageColumn.



   

System Information and the PerformanceCounter Class

The other day I discussed retrieving system information using the .NET framework. I guess the main focus of that post was to discuss using PInvoke to call unmanaged WIN32 DLL exports, more so than actually retrieving different pieces of system information. The objective of that post was retrieving memory information (I also retrieve disk information in my ‘AboutBox’ discussed in that post). In the 'managed codeworld' there is another way to get different information values. Using the System.Diagnostics.PerformanceCounter Class you can retrieve ‘counter’ information from a local or remote machine. Many of you may be familiar with some of the information accessible through PerformanceCounters if you run the
performance monitor control panel applet.
The information accessible via the System.Diagnostics.PerformanceCounter Class is not limited to just system information. Many services and applications also make ‘counter’ information available for retrieval. Performance Counters are broken out by category (System.Diagnostics.PerformanceCounterCategory). Within each category there many be multiple instances, for example the Logical Disk category will have an instance for each drive, as well as an instance for the _Total of all drives. Each instance will have its own set of counters that can be retrieved. In keeping with the same example, the _Total instance of the Logical Disk category has counters such as ‘% Free Space’, ‘Free Megabytes’ and ‘% Disk Write Time’ to name a few.
Reading counter information is a lot easier than one would expect (the .NET Framework almost made things too easy). I had created a basic-sample PerformanceCounter application that allows for the selection of one of the available (on the local machine) performance counter categories. Once the category is selected, each instance is listed (if there is more than one). Once the counter to be retrieved is selected the value is displayed in a System.Windows.Forms.ListBox (using a System.Timers.Timer for interval). Note: When I use Delphi for development I use the TChart component to display graphs (as I had in BPSNMPMon). If anyone knows of a decent charting control that works with Visual Studio 2005 let me know.

System.Diagnostics.PerformanceCounter perfcounter;

        private void cbxPerfCategories_SelectedIndexChanged( object sender, EventArgs e )
        {
            cbxInstances.Enabled = true;
            cbxInstances.Items.Clear ();
            cbxCounters.Items.Clear ();
            lstOutput.Items.Clear ();
            timer1.Enabled = false;

            if ( cbxPerfCategories.SelectedIndex != -1 )
            {
                if ( PerformanceCounterCategory.Exists ( cbxPerfCategories.Text ) )
                {
                    PerformanceCounterCategory perfcategory =
                        new PerformanceCounterCategory ( cbxPerfCategories.Text );

                    cbxInstances.Items.AddRange ( perfcategory.GetInstanceNames () );
                    if ( cbxInstances.Items.Count == 0 )
                    {
                        cbxInstances.Enabled = false;
                        // Get the counters for a category that has only one
                        // instance
                        GetCounters ( perfcategory, "", cbxCounters );
                    }
                }
            }
        }

        private void GetCounters( PerformanceCounterCategory perfcategory,
            string instancename,
            ComboBox combobox )
        {
            System.Diagnostics.PerformanceCounter[] counters;
            if ( instancename.Equals ( "" ) )
            {
                counters = perfcategory.GetCounters ();
            }
            else
            {
                counters = perfcategory.GetCounters ( instancename );
            }
            for ( int i = 0; i < counters.Length; i++ )
            {
                combobox.Items.Add ( counters[i].CounterName );
            }
        }

        private void frmMain_Load( object sender, EventArgs e )
        {
            PerformanceCounterCategory[] perfcategories;
            try
            {
                perfcategories = PerformanceCounterCategory.GetCategories ();
                for ( int i = 0; i < perfcategories.Length; i++ )
                {
                    cbxPerfCategories.Items.Add ( perfcategories[i].CategoryName );
                }
            }
            catch ( Exception ex )
            {
                MessageBox.Show ( ex.Message, ex.Source, MessageBoxButtons.OK,
                    MessageBoxIcon.Error );
            }
        }

        private void cbxInstances_SelectedIndexChanged( object sender, EventArgs e )
        {
            cbxCounters.Items.Clear ();
            lstOutput.Items.Clear ();
            timer1.Enabled = false;

            if ( cbxInstances.SelectedIndex != -1 )
            {
                if ( PerformanceCounterCategory.InstanceExists ( cbxInstances.Text, cbxPerfCategories.Text ) )
                {
                    PerformanceCounterCategory perfcategory =
                      new PerformanceCounterCategory ( cbxPerfCategories.Text );
                    GetCounters ( perfcategory, cbxInstances.Text, cbxCounters );
                }
            }
        }

        private void cbxCounters_SelectedIndexChanged( object sender, EventArgs e )
        {
            lstOutput.Items.Clear ();
            if ( cbxInstances.Text.Equals ( "" ) )
            {
                perfcounter = new System.Diagnostics.PerformanceCounter ( cbxPerfCategories.Text,
                    cbxCounters.Text, true );
            }
            else
            {
                perfcounter = new System.Diagnostics.PerformanceCounter ( cbxPerfCategories.Text,
                    cbxCounters.Text, cbxInstances.Text, true );
            }
            timer1.Enabled = true;
        }

        private void timer1_Tick( object sender, EventArgs e )
        {
            float value;
            value = perfcounter.NextValue ();
            lstOutput.Items.Insert ( 0, value.ToString ( "#,##0.00" ) );
        }