Saturday, April 28, 2012

Fetch DLL information for storage (SQL Server)


Title says it all, following is the implementation I did some time back using SQL server. There are surely better implementations than this and will appreciate if you can give your feedback regarding this.

Objective:
To retrieve information of all DLLs in a specific location and store in database.

Reason:
There are many ways this information can be utilized:
  • Keep record of DLL information on a day to day basis
  • Use it to compare DLL file changes of different dates


I had a specific requirement of tracking DLLs but you can use it as per your requirement.

Implementation:
SQL table structure – following are the table columns in table

Table_1 – to store DLL infoamtion
  • UniqueIdentifier – You can use GUID for storing record at a specific instance
  • Name – DLL name
  • Version – DLL version (X.X.X.X)
  • ModifiedDt – Last modified date
  • FileSize – DLL file size


Table_2 – to store this instance details (this is something can be used to display in a list – 1 instance per import instance)
  • UniqueIdentifier – GUID (this should be same that stored in above table)
  • Date – Date of import
  • Time – Time of import
  • Product Version – something optional
  • User Name – User who performed
  • Computer Name – Network location which has been used for operation
  • Comment – User comment if any
  • Network Path – Location from where information has been retrieved


There are couple of salient features in the implementation I want to highlight. Our team is scattered across globe and need to have an implementation which is efficient
  • For storing all the file information in table individually in different rows executing only one SQL query (this reduced execution time to half). For adding multiple rows in a single query used “UNION ALL”.
  • There is a observation that using default time stamp lead to different time logged for same file accessed from different location. For that used TimeZoneInfo class object and set it to PST. Then before storing the date converted time in that time zone.


Implemented Code:


public void StoreDLLFileInfo()
{
    MainScreen Main = (MainScreen)Application.OpenForms["MainScreen"]; // Open form instance
    string sQueryLine = "", sFileSize = "";
    bool bFirstTime = false;
    // tbPath - text box which stored and display the path
    // tbComment - text box which stored and display user comment
    // SqlConnection SQLConn maintains the SQL connection instance for firing query
    if (SQLConn.State == ConnectionState.Closed)
        SQLConn.Open();
    if (Directory.Exists(tbPath.Text))// first basic validation to check directory
    {
        // Operation 1 - to add single entry of the import operation
        DateTime SysTime = DateTime.Now.Date;
        string[] sDate = SysTime.GetDateTimeFormats('s');
        string sTime11 = DateTime.Now.TimeOfDay.ToString();
        string sUID = Guid.NewGuid().ToString();
        try
        {
            // fire query to add single entry for the import instance
            SQLCommand = SQLConn.CreateCommand();
            SQLCommand.CommandText = "INSERT INTO Table_2 values ('" + sUID + "', CONVERT(datetime, '" + sDate[0] + "', 126), '" + sTime11.Substring(0, 8) + "', '" + "Product 1.0.0" + "', '" + SystemInformation.UserName.ToString() + " (" + SystemInformation.ComputerName.ToString() + ") - " + tbComment.Text + "', '" + tbPath.Text + "')";
            SQLCommand.ExecuteNonQuery();
        }
        catch (SqlException w)
        {
            MessageBox.Show(w.Message, "SQL Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
            return;
        }
        // Operation 2 - to store DLL information of all DDLs in the stated directory
        string sVersion = "", sName = "";
        DateTime dtWriteDate;
        if (SQLConn.State == ConnectionState.Closed)
            SQLConn.Open();

        DirectoryInfo DirInfo = new DirectoryInfo(tbPath.Text);
        FileVersionInfo FileVerInfo;
        TimeZoneInfo hwZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");// assign specific time zone
        FileInfo[] FileDet = DirInfo.GetFiles("*.dll");// retrieving file information of DLLs only
        iFileCnt = FileDet.Count();
        iFlCntNow = 0;
        foreach (FileInfo File in FileDet)
        {
            FileVerInfo = FileVersionInfo.GetVersionInfo(File.FullName);
            sVersion = FileVerInfo.FileVersion;
            if (sVersion == null)
                sVersion = "N/A";
            sVersion.Replace(",", ".");// DLL version
            sName = File.Name;// DLL Name
            dtWriteDate = File.LastWriteTime;// DLL Last write time
            sFileSize = File.Length.ToString();// DLL file size
            if (bFirstTime)
            {
                sQueryLine += " UNION ALL ";
            }
            bFirstTime = true;
            dtWriteDate = TimeZoneInfo.ConvertTime(dtWriteDate, hwZone);
            sQueryLine += "SELECT '" + sUID + "', '" + sName.ToLower() + "', '" + sVersion.Replace(",", ".") + "', '" + dtWriteDate.GetDateTimeFormats('u')[0].Substring(0, 16) + "', '" + sFileSize + "'";
        }
        try
        {
            Main.Cursor = Cursors.WaitCursor;
            SQLCommand = SQLConn.CreateCommand();
            SQLCommand.CommandText = "INSERT INTO Table_1 (UniqueIdentifier, Name, Version, ModifiedDt, FileSize) " + sQueryLine;
            SQLCommand.ExecuteNonQuery();
            Main.Cursor = Cursors.Default;
        }
        catch (SqlException w)
        {
            MessageBox.Show(w.Message, "SQL Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
        }
    }
}

Wednesday, April 25, 2012

Object or Application memory footprint calculation – example


We many time encounter queries from management and architecture team about memory footprint of application. This is vital information which decides success of any implementation and innovation. Following is an example to find memory footprint of a class object created.

Main idea is basically finding system memory usage difference before and after creation of the object. This will not going to give you precise footprint but you can take 5 reading and take an average of it.
  • Namespace:  System
    • GC.GetTotalMemory Method - Retrieves the number of bytes currently thought to be allocated. A parameter indicates whether this method can wait a short interval before returning, to allow the system to collect garbage and finalize objects.
    • GC.WaitForFullGCComplete Method - Returns the status of a registered notification for determining whether a full garbage collection by the common language run time has completed.
    • GC.Collect Method - Forces an immediate garbage collection of all generations.


Sample code:
System.GC.Collect();
if (System.GC.WaitForFullGCComplete() == GCNotificationStatus.Succeeded)
{
    long GC_MemoryStart = System.GC.GetTotalMemory(true);
    // Create object or initialize application
    System.GC.Collect();
    if (System.GC.WaitForFullGCComplete() == GCNotificationStatus.Succeeded)
    {
        long GC_MemoryEnd = System.GC.GetTotalMemory(true);
        MessageBox.Show((GC_MemoryEnd - GC_MemoryStart).ToString()); // in bytes
    }
}

Report Generation in Excel (.xlsx) - Example

Following is an example fo building an excel sheet through C#. I have taken excel sheet as this been a very rich feature in office with we are building a tabular report. The report generated in excel is really easy to work with. Building an excel required building different objects individually and integrating it. Once done then write data in it. After saving the excel releasing all the resources is a key which we seldom forget.

During this implementation I came across an issue that once the excel get genetated and after closing the excel everythign is fine. But when shutting down the PC pop up came for saving excel which is not present in the desktop. That time realized that the objects of excel created through code are still open and active even though there is no excel instance on desktop. For that at reason end added code to release the resources and calling GC.

This example shows building an Excel (.xlsx) report which will diaplay data from list  List<string[]> ListGeneratedSize; String[] has array size of 7. This example has following aspects:
  • Merging cells
  • Setting cell color
  • Setting cell label
  • Setting cell font
  • Assigning excel worksheet features like autofit etc.
  • Releasing resources at the end

My opinion is that even though it would be bit complicated building a report in excel but this adds a lot of value to the report.

using Excel = Microsoft.Office.Interop.Excel; // Need to add to reference
public void BuildExcelReport()
{
    List ListGeneratedSize = new List();
    // In list added string[] of size 7
    Excel.Application app = null;
    Excel.Workbook workbook = null;
    Excel.Workbooks workbooks = null;
    Excel.Worksheet worksheet = null;
    Excel.Range workSheet_range = null;
    int irow = 1;
    try
    {
        app = new Excel.Application();
        workbooks = app.Workbooks;
        workbook = workbooks.Add(1);
        worksheet = (Excel.Worksheet)workbook.Sheets[1];
        worksheet.Cells[irow, 2] = lZAM1.Text.Substring(0, lZAM1.Text.IndexOf(";"));
        workSheet_range = worksheet.get_Range("B" + irow, "D" + irow);
        workSheet_range.Merge(0);
        workSheet_range.Font.Bold = true;

        worksheet.Cells[irow, 5] = lZAM2.Text.Substring(0, lZAM2.Text.IndexOf(";"));
        workSheet_range = worksheet.get_Range("E" + irow, "G" + irow);
        workSheet_range.Merge(0);
        workSheet_range.Font.Bold = true;
        workSheet_range = worksheet.get_Range("A" + irow, "G" + irow);
        workSheet_range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Gray);
        workSheet_range.Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.RosyBrown);
        workSheet_range.Font.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.AntiqueWhite);
        irow++;
        worksheet.Cells[irow, 1] = "Label 1";
        worksheet.Cells[irow, 2] = "Label 2";
        worksheet.Cells[irow, 3] = "Label 3";
        worksheet.Cells[irow, 4] = "Label 4";
        worksheet.Cells[irow, 5] = "Label 5";
        worksheet.Cells[irow, 6] = "Label 6";
        worksheet.Cells[irow, 7] = "Label 7";
        workSheet_range = worksheet.get_Range("A" + irow, "G" + irow);
        workSheet_range.Font.Bold = true;
        workSheet_range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
        workSheet_range.Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.RosyBrown);
        irow++;
        if (ListGeneratedSize.Count > 0)
        {
            workSheet_range = worksheet.get_Range("A" + irow, "G" + irow);
            workSheet_range.Merge(0);
            workSheet_range.Font.Bold = true;
            workSheet_range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
            workSheet_range.Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.RosyBrown);
            worksheet.Cells[irow, 1] = "Test Heading";
            irow++;
            foreach (string[] sData in ListGeneratedSize)
            {
                workSheet_range = worksheet.get_Range("A" + irow, "A" + irow);
                workSheet_range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Azure);
                workSheet_range.Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.RosyBrown);
                workSheet_range = worksheet.get_Range("B" + irow, "D" + irow);
                workSheet_range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LemonChiffon);
                workSheet_range.Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.RosyBrown);
                workSheet_range = worksheet.get_Range("E" + irow, "G" + irow);
                workSheet_range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Beige);
                workSheet_range.Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.RosyBrown);
                for (int i = 0; i <= 6; i++)
                {
                    worksheet.Cells[irow, (i + 1)] = sData[i];
                }
                irow++;
            }
        }
        File.Delete(@"C:\Test.xlsx");
        worksheet.Cells.Columns.AutoFit();
        workbook.SaveAs(@"C:\Test.xlsx",
            Excel.XlFileFormat.xlWorkbookNormal,
            Type.Missing,
            Type.Missing,
            Type.Missing,
            Type.Missing,
            Excel.XlSaveAsAccessMode.xlExclusive,
            Type.Missing,
            Type.Missing,
            Type.Missing,
            Type.Missing,
            Type.Missing);
    }
    finally // Releasing all the resources
    {
        if (workbook != null)
            workbook.Close(false, null, null);
        if (workbooks != null)
            workbooks.Close();
        if(app != null)
            app.Quit();
       
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(workSheet_range);
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(worksheet);
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(workbooks);
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(workbook);
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(app);
        workSheet_range = null;
        workbooks = null;
        worksheet = null;
        workbook = null;
        app = null;
       
        GC.Collect();
        GC.WaitForPendingFinalizers();

        // Launching excel sheet
        System.Diagnostics.Process p = new System.Diagnostics.Process();
        p.StartInfo.FileName = @"C:\Test.xlsx";
        p.Start();
    }
}

Monday, April 23, 2012

SMTP Send Mail - example


MailMessage represents an e-mail message that can be sent using the SmtpClient class. Using MailMessage we can integrate auto mail feature where we have an SMTP client already available. 
Following is the sample mail implementation where subject and mail body passed as parameter. We can pass the MailAddress for sender and recipient as well.

Sample Code:
public bool Send_Mail(string csSubject, string csBody)
{
    MailAddress from = new MailAddress("aaa@abc.com", "aaa bbb");
    MailAddress to = new MailAddress("ccc@abc.com", "ccc ddd");
    MailMessage mMail = new MailMessage(from, to);
    mMail.Bcc.Add(from);
    mMail.Subject = "Test Subject";
    mMail.Body = csBody;
    mMail.IsBodyHtml = true;
    mMail.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;
    mMail.ReplyTo = from;

    SmtpClient client = new SmtpClient("xxx-exg99.xxxx.xx.com");
    client.Credentials = CredentialCache.DefaultNetworkCredentials;

    try
    {
        client.Send(mMail);
    }
    catch (Exception exc)
    {
        MessageBox.Show(exc.Message, "Not able to send mail", MessageBoxButtons.OK, MessageBoxIcon.Hand);
        mMail.Dispose();
        return false;
    }
    mMail.Dispose();
    return true;
}

Sunday, April 22, 2012

ProgressBar example - cracked easy

Its been sometime that I am working with ProgressBar. Initially its been challenging to implement it; how to manage the process as well as progress bar simultaneously. Now a days I prefer using AsyncOperationManager for having the progress bar in a different thread. It has very less overhead and stable. There are many implementation example and I think all are fine but I was looking for something very portable and with minor modification can be integrated in any application, even with minor changes can be used in same application is different flavors.

In the example below created new form with ProgressBar which I have used for all the processes.
To make the implementation simple kept the main process in the main form thread e.g. copy files and folders; launched the ProgressBar in a separate process which basically puts the main form in the background. Even though progress bar form thread doesn't do anything in my case it is on the user discretion to use it.

In my case I have user Marquee progress bar type. This example has 3 section and details given below.

SECTION 1
Declare the delegares as give below:

private delegate void AppDeligate();
private AppDeligate _psudoShowProgress;
private delegate void CloseDeligate();
private CloseDeligate _psudoCloseDialog;

SECTION 2:
Launch the progress bar form using Async process and invoke it. 

private void CopyShowProgressBar()
{
    ProcessBar PPForm = new ProcessBar ();
    PPForm.ShowDialog();
}
private void PsudoProcessBarLaunch()
{
    _psudoShowProgress = CopyShowProgressBar;
    var async = AsyncOperationManager.CreateOperation(null);
    _psudoShowProgress.BeginInvoke(null, async);
    async.OperationCompleted();
}

SECTION 3:
Once the main process completed then close the progress bar which is running on the foreground.

private void PsudoCloseDlg()
{
    ProcessBar Main = (ProcessBar)Application.OpenForms["ProcessBar"];
    if (Main != null)
        Main.Invoke(Main.CloseDialog);
}
private void PsudoCompletedCallback(IAsyncResult ar)
{
    if (ar.IsCompleted)
        _psudoCloseDialog.EndInvoke(ar);
}
private void PsudoProcessBarClose()
{
    _psudoCloseDialog = PsudoCloseDlg;
    var asyncClose = AsyncOperationManager.CreateOperation(null);
    _psudoCloseDialog.BeginInvoke(PsudoCompletedCallback, asyncClose);
    asyncClose.OperationCompleted();
}

Saturday, April 21, 2012

Functional pointer for beginner


This is how I started understanding concept of functional pointer in C++.
This helped me getting understanding of how methods are invoked which now helped me in implementation through System.Reflection in C#.


String^ CALL_FUNCTION() // sample function to invoke dynamically
{
       return "I write you read…";
}
void FuncMain(String^ (*pt2Func)())
{
       String^ i = pt2Func();
       Console::WriteLine(i);
}
int main(array ^args) // Console application
{
    Console::WriteLine("Hello World");
    FuncMain(&CALL_FUNCTION);
    return 1;
}

Output:
Hello World
I write you read…

Thursday, April 19, 2012

Invoke any Method Dynamically


Objective:
To invoke any method in an instance dynamically by passing Method name as parameter.

Achieved by:
System.Reflection namespace which contains types that retrieve information about assemblies, modules, members, parameters, and other entities in managed code by examining their metadata.
Used MethodInfo Class - Discovers the attributes of a method and provides access to method metadata.

Hierarchy:
·          System.Object
o    System.Reflection.MemberInfo
§   System.Reflection.MethodBase
·          System.Reflection.MethodInfo

Method Used:
Invoke(Object, Object[]) - Invokes the method or constructor represented by the current instance, using the specified parameters.
Advantage:
·          Thread safe
·          Simple to use
·          Has the scalability for future enhancement.

My Implementation:

TestClass TestCl = new TestClass();
Type FuncType = typeof(TestClass);
MethodInfo miTest_Method = FuncType.GetMethod(“Test_Method”);
List lCodeList = new List();
object[] mParam = new object[] { lCodeList };
miTest_Method.Invoke(TestCl, mParam);// you can get the return of method as object

Wednesday, April 18, 2012

WiX - Research analysis of Heat.exe

Following is the research analysis of Heat.exe in WiX toolkit. Purpose of Heat.exe is to create .wxs output file. This analysis is specifically with Directory as well as individual file. Analysis includes command line execution instruction and .wxs file extract.
Presently using a folder “Main Folder” with following structure:
·         Folder – Main Folder
o   Folder – Folder A
§  File – Word Doc AA.docx
o   File – Word Doc A.docx

Directory analysis:

Try 1:
Instruction:
D:\bin>heat.exe dir "d:\Main Folder" -out "d:\Main Folder.wxs"

Output:

    
        
            
        
    
    
        
            
                
            
        
    
    
        
            
                
            
        
    
    
        
            
        
    



Try 2:
Instruction:
D:\bin>heat.exe dir "d:\Main Folder" -sfrag -out "d:\Main Folder.wxs"

Output:

    
        
            
                
                    
                
                
                    
                        
                    
                
            
        
    



Try 3:
Instruction:
D:\bin>heat.exe dir "d:\Main Folder" –sfrag -ag -out "d:\Main Folder.wxs"

Output:

    
        
            
                
                    
                
                
                    
                        
                    
                
            
        
    



Try 4:
Instruction:
D:\bin>heat.exe dir "d:\Main Folder" – sfrag –cg cgGrpNm -out "d:\Main Folder.wxs"

Output:

    
        
            
                
                    
                
                
                    
                        
                    
                
            
        
    
    
        
            
            
        
    



Try 5:
Instruction:
D:\bin>heat.exe dir "d:\Main Folder" –sfrag –dr ProductName -out "d:\Main Folder.wxs"

Output:

    
        
            
                
                    
                
                
                    
                        
                    
                
            
        
    

Try 6:
Instruction:
D:\bin>heat.exe dir "d:\Main Folder" –sfrag –gg -out "d:\Main Folder.wxs"

Output:

    
        
            
                
                    
                
                
                    
                        
                    
                
            
        
    


Try 7:
Instruction:
D:\bin>heat.exe dir "d:\Main Folder" –sfrag –template product -out "d:\Main Folder.wxs"

Output:

    
        
        
            
                
                    
                
                
                    
                        
                    
                
            
        
        
        
    



Try 8:
Instruction:
D:\bin>heat.exe dir "d:\Main Folder" –sfrag –srd –template product -out "d:\Main Folder.wxs"

Output:

    
        
        
            
                
                    
                
                
                    
                        
                    
                
            
        
        
        
    


Try 9:
Instruction:
D:\bin>heat.exe dir "d:\Main Folder" –sfrag –suid –template product -out "d:\Main Folder.wxs"

Output:

    
        
        
            
                
                    
                
                
                    
                        
                    
                
            
        
        
        
    


Try 10:
Instruction:
D:\bin>heat.exe dir "d:\Main Folder" –sfrag –var D: –template product -out "d:\Main Folder.wxs"

Output:

    
        
        
            
                
                    
                
                
                    
                        
                    
                
            
        
        
        
    



Try FINAL: Tried to get the best possible refined .wxs file so that final .wxs file can be generated with very minimal change.
Instruction:
D:\bin>heat.exe dir "d:\Main Folder" -template product -gg -suid -cg Complete -s
frag -ke -var var.MyVar -out "d:\Main Folder.wxs"

Output:

    
        
        
            
                
                    
                
                
                    
                        
                    
                
            
        
        
            
        
        
    
    
        
            
            
        
    



Able to execute Candle successfully
Candle.exe -dMyVar="d:\Main Folder" "d:\Main Folder.wxs" -out "d:\Main Folder.wixobj"

Subsequently created installer Main Folder.msi using Light.exe
Light.exe "d:\Main Folder.wixobj" -out "d:\Main Folder.msi"

Thursday, April 12, 2012

TreeView - Checkbox (double click) - workaround

Windows - Forms - TreeView - C#


While working on TreeView implementation came across a strange issue.
Double click is event is not been properly called w.r.t. check box associated with node.


Following are the scenario that has been observed:

  • TreeView does not fire DoubleClick event the very first time.
  • TreeView may fire DoubleClick event when a user only single clicks.
  • TreeView does not fire BeforeCheck/AfterCheck events at all when user double clicks the checkbox image.
Solution: 
This is something Microsoft need to figure it out.

Workaround:
Disable the double click event from firing in TreeView so that unreliable functionality can be taken out.
Only click event will be allowed and as far as end user will find no issue with double click and single click.

Step 1:
Build you application with all the relevant functionality.

Step 2:
In Form.Designer.cs file add following code at end.
Inheriting TreeView class and suppressing the double click (WM_LBUTTONDBLCLK message) in derived class. Then derived class is being used for processing.

namespace System.Windows.Forms
{
    public class TestTreeView : TreeView
    {
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 0x203) // identified double click
                m.Result = IntPtr.Zero;
            else 
                base.WndProc(ref m);
        }
    }
}

Step 3:
In Form.Designer.cs file change references of TreeView class with inherited class TestTreeView.
private void InitializeComponent()
{
     this.tvBaseDirectory = new System.Windows.Forms.TestTreeView();
}

Step 4: