BPSoftware.com
Home   Utilities   Purchase   FAQ   Support   Contact        
Shareware Utilities
 APrintDirect
 AIconExtract
 AFile Attribute Manager
Freeware Utilities
 AddrMon
 AFileSync
 ASysIcon
 B&P Table Utilities
 BPACLer
 BPSNMPMon
 BPSNMPUtil
 CharCount
 Delphi® Components
 MacAddr
Miscellaneous
 BPSoftware Blog
 Purchase Shareware
 Support

 Subscribe!

Thursday, November 22, 2007
A little System.Diagnostics

I am not going to get overly verbose here; however the importance of application logging is immeasurable. When applications are deployed, obtaining ‘real life’ application can assist in diagnostic and overall application health situations. Excessive logging can be ineffective (too much to analyze) and burdensome (logging to a text file that grows forever) if not implemented appropriately.

When developing and deploying an application having the flexibility of what, when and how to log can alleviate the burden and ineffectiveness of logging. The System.Diagnostics namespace has a number of classes available that allow for this type of flexible logging. The applied use of these classes allows a developer to specify what to log in an application at various levels of detail within one application without the need to deploy diagnostic builds. Depending on diagnostic needs, the application’s config file can specify the level of diagnostic information to write and where. A simple Logging class (this is the class I created to use this functionality) can be developed as follows:


1namespace BPSoftware
2{
3 public class Logging
4 {
5 /// <summary>
6 /// strNameSpace is the base namespace for use in the logging message.
7 /// </summary>

8 public static string strNameSpace;
9
10 /// <summary>
11 /// GeneralTraceSwitch is a traceswitch object for logging. Switch level is set through
12 /// the application config switches section. The destination listener is also set through
13 /// the application config file.
14 /// </summary>

15 public static TraceSwitch GeneralTraceSwitch = new TraceSwitch( "General", "General Trace Switch" );
16
17 Methods
86 }

87}

Throughout the application logging messages can be added:
1 catch ( Exception ex)
2 {
3 Logging.WriteLineTraceError( "ClassName", "Method", "Exception: " + ex.Message );
4 }

5
6 Logging.WriteLineTraceInfo( "ClassName", "Method",,
7 String.Format( "{0}: {1}", e.ActionType.ToString(), e.Message )
8 );


The application’s config file can then be altered to determine the appropriate level and location of the logging based upon certain diagnostic needs. The switches node specifies what level of logging should occur. The switches operate in a hierarchial fashion (3 includes 3, 2 and 1; 2 includes 2 and 1):
1 <system.diagnostics>
2 <trace autoflush="true" indentsize="4">
3 <!-- Specify listener output -->
4 <listeners>
5 <remove name="Default" />
6 <!-- Write to a log file -->
7 <add name="Default"
8 type="System.Diagnostics.TextWriterTraceListener"
9 initializeData="AFileSync.log" />
10 <!-- Write to the Event Log-->
11 <!-- <add name="Default"
12 type="System.Diagnostics.EventLogTraceListener"
13 initializeData="Application" /> -->
14 </listeners>
15 </trace>
16 <switches>
17 <!-- Off = 0, Error = 1, Warning = 2, Info = 3, Verbose = 4 -->
18 <add name="General" value="3" />
19 </switches>
20 </system.diagnostics>


Using the application’s config file to specify the level of logging allows for the capture of diagnostic information of a stable build to capture ‘real world’ information. The flexibility of the listener to write to (you can create customer listeners) is an added bonus!

Labels: , , ,

posted by Brad Prendergast at 8:49:00 AM (0 comments)
Links to this post
Permalink
Sunday, October 07, 2007
Control and List Windows Services

As I whipped up my file synchronizing service application, to synchronize files between my computer and my NAS), I worked with the System.ServiceProcess Namespace. The System.ServiceProcess Namespace "provides classes that allow you to implement, install, and control Windows service applications".

My file synchronization needs did not require that my application service to be "running" all the time. In, fact I only needed the service to run under certain situations. It was easy enough to get the service written and installed and now I wanted to 'automatically' (programmatically) start and stop my service when certain conditions were met.

Entrance - the ServiceController Class. The ServiceController Class "Represents a Windows service and allows you to connect to a running or stopped service, manipulate it, or get information about it." In order to get acclimated to the ServiceController Class I created a simple application to list and display information about the installed services.


1using System;
2using System.Windows.Forms;
3using System.ServiceProcess;
4
5namespace Services1
6{
7 public partial class Form1 : Form
8 {
9 ServiceController controller;
10
11 public Form1()
12 {
13 controller = new ServiceController();
14 controller.MachineName = ".";
15 InitializeComponent();
16 lstServices.DisplayMember = "DisplayName";
17 lstServices.ValueMember = "ServiceName";
18 lstServices.DataSource = ServiceController.GetServices();
19 }

20
21 private void StartService(string servicename)
22 {
23 controller.ServiceName = servicename;
24 controller.Start();
25
26 }

27
28 private void PauseService(string servicename)
29 {
30 controller.ServiceName = servicename;
31 if (controller.CanPauseAndContinue)
32 controller.Pause();
33 }

34
35 private void StopService(string servicename)
36 {
37 controller.ServiceName = servicename;
38 if (controller.CanStop)
39 controller.Stop();
40 }

41
42
43 private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
44 {
45 if (lstServices.SelectedItems.Count > 0)
46 {
47 try
48 {
49 controller.ServiceName = lstServices.SelectedValue.ToString();
50 textBox1.Text = String.Format(
51 "Service Name: {0}\r\nDisplay Name: {1}\r\nType: {2}\r\nStatus: {3}",
52 controller.ServiceName,
53 controller.DisplayName,
54 controller.ServiceType.ToString(),
55 controller.Status.ToString()
56 );
57 }

58 catch (Exception ex)
59 {
60 textBox1.Text = String.Format("Error: \r\n{0}",ex.Message);
61 }

62 }

63 else
64 {
65 textBox1.Clear();
66 }

67 }

68
69 private void btnStart_Click(object sender, EventArgs e)
70 {
71 StartService(lstServices.SelectedValue.ToString());
72 }

73
74 private void btnPause_Click(object sender, EventArgs e)
75 {
76 PauseService(lstServices.SelectedValue.ToString());
77 }

78
79 private void btnStop_Click(object sender, EventArgs e)
80 {
81 StopService(lstServices.SelectedValue.ToString());
82 }

83 }

84}

Labels: , ,

posted by Brad Prendergast at 3:12:00 PM (0 comments)
Links to this post
Permalink
Monday, October 01, 2007
Let's Synchronize Some Files

Recently, I posted about the new addition to my backup strategy; a NAS. My intended use of this device is to have a hot backup of my files. The system I was using before was not capable of storing the amount of data I need backed up, nor was it easy to manage. I modified the set of xCopy batch files that I used for my backups to write the files to the NAS.

This system worked fine, but I needed more. I manage a lot of data and have a lot of files. I wouldn't want to lose any in the event of a system failure. The one problem I see with using this process is the lack of a real time solution.

I was in search of a solution that would offer me real-time synchronization between my files and the NAS. I didn't have to search very far. The .NET Framework includes the FileSystemWatcher Class.

The FileSystemWatcher Class "Listens to the file system change notifications and raises events when a directory, or file in a directory, changes." (quoted directly from MSDN) After reading through this class I quickly whipped up a solution. I developed my own FileSync class that allowed me to easily synchronize files between my remote and local storage. I created a Windows Service (threaded) built around my class (the screen shot in this post is from a sample app I used to test my class) so that I could have the FileSync class running and keeping my files in sync without me needing to do anything. Here is the unit I created:


1using System;
2using System.IO;
3using System.Diagnostics;
4
5namespace BPSoftware
6{
7 class FileSync
8 {
9 private FileSystemWatcher watcher;
10
11 Fields
40
41 Constructors
62
63 Methods
115
116 Events
165 }

166}

167

Labels: , ,

posted by Brad Prendergast at 1:09:00 AM (0 comments)
Links to this post
Permalink
Sunday, September 23, 2007
DSOFile and Summary Properties

Yesterday I posted about using ShellExecuteEx to display the document properties dialog for a file or folder. Displaying the properties dialog is nice, however I also need to access the OLE document properties (Document Summary Properties), the information that is stored on the Summary and Custom tabs of the properties dialog. I did quite a bit of searching, reading and testing in an attempt to find a simple solution. I did get to learn a lot about the Document Summary Properties and it looked like my only option was to head down the road using the IPropertyStorage Interface. As I was testing some code I came across the best little ActiveX component that can quickly access the property information. The Dsofile.dll is an ActiveX component is available from Microsoft. They even include a sample application (although the samples are in VB6 and VB7 they are useable). This component simplifies (I like things simple) setting and getting the document summary properties. The KB article and download mention that this is meant for Office files, however I have found that it works fine on other files as well (the exception being that the Office documents seem to be the only types of files with Custom Properties). The other great thing about this component is that "You have a royalty-free right to use, to modify, to reproduce, and to distribute the Dsofile.dll sample file component and the C++ source code files in any way you find useful." That quote is taken directly from the DSOfile.dll Article ID: 224351 that is listed on Microsoft's Support site. My thoughts, why reinvent the wheel when you really don't need to. I highly recommend using this component if you need to access the extended properties. As usual, I whipped up a sample application testing this components functionality. The meager sample code is listed here (image of application screen is also included in this post):

1 public partial class Form1 : Form
2 {
3 public Form1()
4 {
5 InitializeComponent();
6 }

7
8 private void btnSelectFile_Click(object sender, EventArgs e)
9 {
10 if (openFileDialog1.ShowDialog() == DialogResult.OK)
11 {
12 textBox1.Text = openFileDialog1.FileName;
13 toolTip1.SetToolTip(textBox1, textBox1.Text);
14 lstSummary.Items.Clear();
15
16 OleDocumentPropertiesClass odpc = new OleDocumentPropertiesClass();
17 odpc.Open(
18 @openFileDialog1.FileName,
19 false,
20 dsoFileOpenOptions.dsoOptionOpenReadOnlyIfNoWriteAccess
21 );
22 // Summary "Standard" Properties
23 ListViewItem lvTitle = new ListViewItem();
24 lvTitle.Group = lstSummary.Groups[0];
25 lvTitle.Text = "Title";
26 lvTitle.SubItems.Add(odpc.SummaryProperties.Title);
27 lstSummary.Items.Add(lvTitle);
28
29 ListViewItem lvSubject = new ListViewItem();
30 lvSubject.Group = lstSummary.Groups[0];
31 lvSubject.Text = "Subject";
32 lvSubject.SubItems.Add(odpc.SummaryProperties.Subject);
33 lstSummary.Items.Add(lvSubject);
34
35 ListViewItem lvCategory = new ListViewItem();
36 lvCategory.Group = lstSummary.Groups[0];
37 lvCategory.Text = "Category";
38 lvCategory.SubItems.Add(odpc.SummaryProperties.Category);
39 lstSummary.Items.Add(lvCategory);
40
41 ListViewItem lvKeyword = new ListViewItem();
42 lvKeyword.Group = lstSummary.Groups[0];
43 lvKeyword.Text = "Keywords";
44 lvKeyword.SubItems.Add(odpc.SummaryProperties.Keywords);
45 lstSummary.Items.Add(lvKeyword);
46
47 ListViewItem lvComments = new ListViewItem();
48 lvComments.Group = lstSummary.Groups[0];
49 lvComments.Text = "Comments";
50 lvComments.SubItems.Add(odpc.SummaryProperties.Comments);
51 lstSummary.Items.Add(lvComments);
52
53 ListViewItem lvAuthor = new ListViewItem();
54 lvAuthor.Group = lstSummary.Groups[1];
55 lvAuthor.Text = "Author";
56 lvAuthor.SubItems.Add(odpc.SummaryProperties.Author);
57 lstSummary.Items.Add(lvAuthor);
58
59 ListViewItem lvRevision = new ListViewItem();
60 lvRevision.Group = lstSummary.Groups[1];
61 lvRevision.Text = "Revision Number";
62 lvRevision.SubItems.Add(odpc.SummaryProperties.RevisionNumber);
63 lstSummary.Items.Add(lvRevision);
64
65 // Custom Properties
66 foreach (DSOFile.CustomProperty cp in odpc.CustomProperties)
67 {
68 ListViewItem lvc = new ListViewItem();
69 lvc.Group = lstSummary.Groups[2];
70 lvc.Text=cp.Name;
71 lvc.SubItems.Add(cp.get_Value().ToString());
72 lstSummary.Items.Add(lvc);
73 }

74 odpc.Close(false);
75 }

76 }

77 }

This is something I'd also like to port to Delphi for use in those applications. Maybe this week sometime......

Labels: , ,

posted by Brad Prendergast at 6:52:00 PM (0 comments)
Links to this post
Permalink
Saturday, September 22, 2007
ShellExecuteEX and ShellExecuteInfo Revisited

Who says history doesn't repeat itself? I am working on a project that needs to display and access file properties. A quick search through the .NET 2.0 Framework didn't yield anything that popped out. I did however, remember our wonderful friend - the SHELL32 API. Previously, I had posted on using ShellExecuteEX with Delphi (I am a huge Delphi fan; I may not be posting much about it recently, but I did not forget it). Here is that same sample in using C#.


1 public const uint SW_SHOW =0x5;
2 public const uint SEE_MASK_INVOKEIDLIST = 0xC;
3
4 public enum verbs
5 {
6 properties,
7 open,
8 edit,
9 explore,
10 print
11 }

12
13 [StructLayout(LayoutKind.Sequential)]
14 public struct SHELLEXECUTEINFO
15 {
16 public int cbSize;
17 public uint fMask;
18 public IntPtr hwnd;
19 public String lpVerb;
20 public String lpFile;
21 public String lpParameters;
22 public String lpDirectory;
23 public uint nShow;
24 public int hInstApp;
25 public int lpIDList;
26 public String lpClass;
27 public int hkeyClass;
28 public uint dwHotKey;
29 public int hIcon;
30 public int hProcess;
31 }

32
33 [DllImport("shell32.dll")]
34 static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo);
35
36 public void MyShellExecuteInfo(string filename, verbs verb)
37 {
38 SHELLEXECUTEINFO info = new SHELLEXECUTEINFO();
39 info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
40 switch (verb)
41 {
42 case verbs.properties:
43 info.lpVerb = "properties";
44 break;
45 case verbs.print:
46 info.lpVerb = "print";
47 break;
48 case verbs.open:
49 info.lpVerb = "open";
50 break;
51 case verbs.explore:
52 info.lpVerb = "explore";
53 break;
54 case verbs.edit:
55 info.lpVerb = "edit";
56 break;
57 }

58
59 info.lpFile = filename;
60 info.nShow = SW_SHOW;
61 info.fMask = SEE_MASK_INVOKEIDLIST;
62 ShellExecuteEx(ref info);
63 }

64

Labels: , ,

posted by Brad Prendergast at 7:26:00 PM (0 comments)
Links to this post
Permalink
Monday, September 17, 2007
A ToolStripMenuItem and a Check

One nice addition to the .NET 2.0 framework is the ToolStripMenuItem Class. The ToolStripMenuItem Class replaces the MenuItem Class. The ToolStripMenuItem has a Checked property that gets or sets a value indicating if the item is checked or not. Coupled with the ToolStripMenuItem.CheckOnClick property, this can be useful for identifying which item has been selected or activated. Unfortunately, there isn't some sort of 'allow only one checked' property which allows for only one item in a menu to be 'Checked' at a time. This functionality can be easily added in code (or you could derive your own class that has this functionality; a nice addition to a personal control library). Create an OnClick and OnCheckChanged method that are referenced by each of the ToolStripMenuItems. Basically, you check to see if the current item is checked or not and then toggle the check for the other menu items. You could also check another property of the sender (such as the Tag property) to perform some other action.

Here is an example:

1 private void CheckedIconsToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
2 {
3 if (sender is ToolStripMenuItem)
4 {
5 if (!((ToolStripMenuItem)sender).Checked) return;
6 foreach (ToolStripMenuItem item in (((ToolStripMenuItem)sender).GetCurrentParent().Items))
7 {
8 if (item != null && item != sender && item.Checked)
9 {
10 item.Checked = false;
11 return;
12 }

13 }

14 }

15 }

16
17 private void CheckedToolStripMenuItem_Click(object sender, EventArgs e)
18 {
19 if (sender is System.Windows.Forms.ToolStripMenuItem)
20 {
21 if (!((ToolStripMenuItem)sender).Checked)
22 {
23 ((ToolStripMenuItem)sender).Checked = !((ToolStripMenuItem)sender).Checked;
24 }
;
25 switch (((ToolStripMenuItem)sender).Tag.ToString())
26 {
27 case "1":
28 break;
29 case "2":
30 break;
31 case "3":
32 break;
33 case "4":
34 break;
35 case "5":
36 break;
37 default:
38 break;
39 }
;
40 }

41 }

42

Labels: , ,

posted by Brad Prendergast at 9:10:00 PM (0 comments)
Links to this post
Permalink
Sunday, September 16, 2007
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 }

Labels: , ,

posted by Brad Prendergast at 9:55:00 PM (0 comments)
Links to this post
Permalink
Saturday, March 03, 2007
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.
1namespace DrawInDataGrid1
2{
3 public partial class Form1 : Form
4 {
5 private DataTable myTable;
6 private DataGridViewCheckBoxColumn checkcolumn;
7 private DataGridViewButtonColumn buttoncolumn;
8 private DataColumn colItem1, colItem2, colItem3;
9 private DataRow NewRow;
10 private DataView myView;
11
12 public Form1()
13 {
14 InitializeComponent();
15 }

16
17 private void Form1_Load(object sender, EventArgs e)
18 {
19 // DataTable to hold data that is displayed in DataGrid
20 myTable = new DataTable("myTable");
21
22 // the three columns in the table
23 colItem1 = new DataColumn("CheckBox", Type.GetType("System.Boolean"));
24 colItem2 = new DataColumn("Button", Type.GetType("System.String"));
25 colItem3 = new DataColumn("String", Type.GetType("System.String"));
26
27 // add the columns to the table
28 myTable.Columns.Add(colItem1);
29 myTable.Columns.Add(colItem2);
30 myTable.Columns.Add(colItem3);
31
32 checkcolumn = new DataGridViewCheckBoxColumn(false);
33 checkcolumn.HeaderText = "CheckBox";
34 checkcolumn.DataPropertyName = "CheckBox";
35
36 buttoncolumn = new DataGridViewButtonColumn();
37 buttoncolumn.HeaderText = "Button";
38 buttoncolumn.DataPropertyName = "Button";
39
40 dataGridView1.Columns.Add(checkcolumn);
41 dataGridView1.Columns.Add(buttoncolumn);
42
43
44 // Fill in some data
45 NewRow = myTable.NewRow();
46 NewRow[0] = true;
47 NewRow[1] = "0";
48 NewRow[2] = "Test";
49 myTable.Rows.Add(NewRow);
50
51 NewRow = myTable.NewRow();
52 NewRow[0] = false;
53 NewRow[1] = "1";
54 NewRow[2] = "Next One";
55 myTable.Rows.Add(NewRow);
56
57 // DataView for the DataGridView
58 myView = new DataView(myTable);
59 myView.AllowDelete = false;
60 myView.AllowEdit = false;
61 myView.AllowNew = false;
62
63 // add an event to check for button click
64 this.dataGridView1.CellContentClick +=
65 new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellContentClick);
66
67 // Assign DataView to DataGrid
68 dataGridView1.DataSource = myView;
69 }

70
71
72 private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
73 {
74 if (sender is DataGridView)
75 {
76 if ((((DataGridView)sender).Columns[e.ColumnIndex] is DataGridViewButtonColumn) &&
77 (e.RowIndex >= 0))
78 {
79 MessageBox.Show(e.RowIndex.ToString());
80 }

81 }

82 }

83 }

84}



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

Labels: , ,

posted by Brad Prendergast at 1:52:00 PM (1 comments)
Links to this post
Permalink
Sunday, February 04, 2007
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.


1 System.Diagnostics.PerformanceCounter perfcounter;
2
3 private void cbxPerfCategories_SelectedIndexChanged(object sender, EventArgs e)
4 {
5 cbxInstances.Enabled = true;
6 cbxInstances.Items.Clear();
7 cbxCounters.Items.Clear();
8 lstOutput.Items.Clear();
9 timer1.Enabled = false;
10
11 if (cbxPerfCategories.SelectedIndex != -1)
12 {
13 if (PerformanceCounterCategory.Exists(cbxPerfCategories.Text))
14 {
15 PerformanceCounterCategory perfcategory =
16 new PerformanceCounterCategory(cbxPerfCategories.Text);
17
18 cbxInstances.Items.AddRange(perfcategory.GetInstanceNames());
19 if (cbxInstances.Items.Count == 0)
20 {
21 cbxInstances.Enabled = false;
22 // Get the counters for a category that has only one
23 // instance
24 GetCounters(perfcategory, "",cbxCounters);
25 }

26 }

27 }

28 }

29
30 private void GetCounters(PerformanceCounterCategory perfcategory,
31 string instancename,
32 ComboBox combobox)
33 {
34 System.Diagnostics.PerformanceCounter[] counters;
35 if (instancename.Equals(""))
36 {
37 counters = perfcategory.GetCounters();
38 }

39 else
40 {
41 counters = perfcategory.GetCounters(instancename);
42 }

43 for (int i = 0; i < counters.Length; i++)
44 {
45 combobox.Items.Add(counters[i].CounterName);
46 }

47 }

48
49 private void frmMain_Load(object sender, EventArgs e)
50 {
51 PerformanceCounterCategory[] perfcategories;
52 try
53 {
54 perfcategories = PerformanceCounterCategory.GetCategories();
55 for (int i = 0; i < perfcategories.Length; i++)
56 {
57 cbxPerfCategories.Items.Add(perfcategories[i].CategoryName);
58 }

59 }

60 catch (Exception ex)
61 {
62 MessageBox.Show(ex.Message, ex.Source, MessageBoxButtons.OK,
63 MessageBoxIcon.Error);
64 }

65 }

66
67 private void cbxInstances_SelectedIndexChanged(object sender, EventArgs e)
68 {
69 cbxCounters.Items.Clear();
70 lstOutput.Items.Clear();
71 timer1.Enabled = false;
72
73 if (cbxInstances.SelectedIndex != -1)
74 {
75 if (PerformanceCounterCategory.InstanceExists(cbxInstances.Text, cbxPerfCategories.Text))
76 {
77 PerformanceCounterCategory perfcategory =
78 new PerformanceCounterCategory(cbxPerfCategories.Text);
79 GetCounters(perfcategory, cbxInstances.Text, cbxCounters);
80 }

81 }

82 }

83
84 private void cbxCounters_SelectedIndexChanged(object sender, EventArgs e)
85 {
86 lstOutput.Items.Clear();
87 if (cbxInstances.Text.Equals(""))
88 {
89 perfcounter = new System.Diagnostics.PerformanceCounter(cbxPerfCategories.Text,
90 cbxCounters.Text, true);
91 }

92 else
93 {
94 perfcounter = new System.Diagnostics.PerformanceCounter(cbxPerfCategories.Text,
95 cbxCounters.Text, cbxInstances.Text,true);
96 }

97 timer1.Enabled = true;
98 }

99
100 private void timer1_Tick(object sender, EventArgs e)
101 {
102 float value;
103 value = perfcounter.NextValue();
104 lstOutput.Items.Insert(0,value.ToString("#,##0.00"));
105 }

Labels: , ,

posted by Brad Prendergast at 5:14:00 PM (0 comments)
Links to this post
Permalink
Saturday, February 03, 2007
Cryptography hash and a class - to go

One of the things I appreciate the most in OOP is the concept of classes. I have found that the use of classes allows for code to be easily maintained and reused. I look at classes as neat little packaged objects that can be ‘moved around’ and ‘worked with’ as their own. Another big benefit of classes is the concept of inheritance and polymorphism. In three previous posts (1, 2, 3) I discussed using the System.Security.Cryptography Namespace to calculate the hash values of text (strings) and files. I actually did create a class (that I actively use) from that sample.


1using System;
2using System.Text;
3using System.Security.Cryptography;
4using System.IO;
5
6 public class CryptoHash
7 {
8 /// <summary>
9 /// public enum hashtypes { MD5, SHA1, SHA256, SHA384, SHA512 };
10 /// </summary>

11 public enum hashtypes { MD5, SHA1, SHA256, SHA384, SHA512 };
12
13 Properties
44
45 Constructors
55
56 Methods