Saturday, May 15, 2010

Transferring a Windows Application to Another Computer

Let's say you want to migrate a Windows application from one PC to another. Not the entire hard drive, not your MP3 collection, but just one application.

Maybe for whatever reason you like the application, a lot, and don't want to fork over money for another license, nor do you want to compromise and purchase a replacement that does what you need it to, but makes you long for some quirky goodness your old app offers.

Utilities like Laplink PCMover and Move Me let you migrate applications, settings, files and all from one PC directly (via USB cable) or indirectly (via external hard drive) to another. There's also the excellent Acronis True Image, which enables you to clone your hard drive and copy every bit of data. Another possibility is a tool like the freeware Application Mover, which lets you move an installed application along with its files and registry settings to a new path on the same hard drive, or a new hard drive entirely.

Of these utilities, arguably the first few do the best, since they not only a working application, but most if not all data in the source and migrates it en masse to the target. If you wish to only transport one app, however, you're practically out of luck.

You could try simply copying the application folder, updating registry settings and all that manually, and it might work, but it's rather hit-or-miss. What if some crucial file the app needs for a very specific task wasn't copied over? What if a very old app has some stupid bug that didn't emerge when it lived in an old 8.3 naming system, but does once you try to stick it into a folder under "C:\Program Files"?

Thankfully, a solution exists, in the form of a utility included in the Symantec Ghost Solution Suite called Client Migration. As this article mentions, one of its features includes the ability to create installation packages for installed applications.
  "Client Migration 3.0 by Symantec migrates data and application settings to a new computer. It enables you to
  create application packages for the purpose of updating or installing applications on the new computer..."



Being able to simply migrate a single application can be handy by itself, but the ability to create an installation package is even better, for several reasons.
  1. Portability. If you enjoy a particular application, and don't want to give up its usefulness, you can just install it on new hardware and enjoy.

  2. Licensing. Rather than being stuck with buying another license for a program you already bought, you can simply move it to a new system.

  3. Comfort. You love PhotoShop, but in the far future it becomes abandonware and is eclipsed by DodoShop, yet you just loved PhotoShop's way of doing things. Migration capability enables you the comfort of extending the usability of this one app that may no longer be available for purchase, let alone for download off some random site.

There can, as with everything in life, be a few pitfalls. 
  • If the app you're migrating has any low-level conversations with your hardware, these peculiarities might translate with difficulty, if at all, to a new system with updated hardware and firmware. The app itself might not function, or worse, might cause your system to crash.

  • If the operating system evolves to the extent that certain OS-specific resources the application depends on to do it's thing are no longer present in the current OS, you might need to enable any compatibility features (e.g. Windows ability to run applications in compatibility mode), or use something like Microsoft Virtual PC to emulate the desired operating system environment.

Clearly, there are advantages and disadvantages in preserving a very useful old application. At least the Symantec product described above gives you the valuable option of being able to breathe new life into an old application despite upgrades to your hardware and operating system.

Tuesday, April 27, 2010

Create CheckBox Controls Inside A GroupBox Programmatically

I'm working on a WinForms app to interface with SQL server to grab parameters and other info for various objects. It uses a custom control I discovered on CodeProject to create a collapsible GroupBox control.

Say that I want to create a bunch of new controls based on the output of some SQL function or stored procedure. For example, the system stored procedure sp_databases returns a list of all databases on a given SQL server. 

In my collapsible GroupBox, I might want to create a CheckBox control for each database so that I can let the user choose which database to work on in the current session of the application.

To facilitate this, some pseudocode might be helpful:
  • Instantiate a new CheckBox object.
  • Does my GroupBox contain any other CheckBoxes?
    • NO: Set the CheckBox location so that it appears in the upper-left corner of the GroupBox
    • YES: Grab the X and Y coordinates of the last CheckBox contained in the GroupBox
  • Would the width of an additional CheckBox exceed that of the GroupBox?
    • YES: Set the new CheckBox X coordinate so that it aligns with the first CheckBox in the GroupBox, then bump the Y coordinate down by a set value.
    • NO: Place the new CheckBox to the right of the previous one.
  • Set the Text property of the newly-added CheckBox to a given input string value.

Along these lines, I created the method below, which takes a string as input representing the Text property of the newly-created CheckBox.

       
/// <summary>
        /// Create a new CheckBox control within a collapsible GroupBox, assigning input sName
        /// to the Text property.
        /// </summary>
        /// <param name="sName"></param>
        /// <returns></returns>
        private CheckBox CreateNewCheckBox(string sName)
        {
            int iExistingCheckBoxX = 0;
            int iExistingCheckBoxY = 0;

            int iIncrementX = 100;
            int iIncrementY = 20;

            CheckBox cbNew = new CheckBox();

            cbNew.Width = iIncrementX;

            if (cgbDatabases.Controls.Count == 0)
            {
                cbNew.Location = new Point(cgbDatabases.Location.X + 15, cgbDatabases.Location.Y - 50);
            }
            else
            {
                // Existing checkboxes, so get the Location of the last one.
                iExistingCheckBoxX = cgbDatabases.Controls[cgbDatabases.Controls.Count - 1].Location.X;
                iExistingCheckBoxY = cgbDatabases.Controls[cgbDatabases.Controls.Count - 1].Location.Y;

                // If the new control would overshoot the end of the container, bump it down.
                if ((iExistingCheckBoxX + iIncrementX) + cbNew.Width >= cgbDatabases.Width)
                {
                    iExistingCheckBoxX = cgbDatabases.Location.X + 15;
                    iExistingCheckBoxY = iExistingCheckBoxY + iIncrementY;

                    cbNew.Location = new Point(iExistingCheckBoxX, iExistingCheckBoxY);
                }
                else
                {
                    cbNew.Location = new Point(iExistingCheckBoxX + iIncrementX, iExistingCheckBoxY);
                }
            }

            // Set the Text property according to the input.
            cbNew.Text = sName;

            return cbNew;
        }


In the constructor of my WinForm app, as a test I used the collapsible GroupBox control's Add method in conjunction with the new method to insert a bunch of child CheckBox controls into parent GroupBox. 

        public frmMain()
        {
            InitializeComponent();

            cgbDatabases.Controls.Add(CreateNewCheckBox("CheckBox1"));
            cgbDatabases.Controls.Add(CreateNewCheckBox("CheckBox2"));
            cgbDatabases.Controls.Add(CreateNewCheckBox("CheckBox3"));
            cgbDatabases.Controls.Add(CreateNewCheckBox("CheckBox4"));
            cgbDatabases.Controls.Add(CreateNewCheckBox("Checkbox5"));
            cgbDatabases.Controls.Add(CreateNewCheckBox("Checkbox6"));
            cgbDatabases.Controls.Add(CreateNewCheckBox("Checkbox7"));
            cgbDatabases.Controls.Add(CreateNewCheckBox("Checkbox8"));
            cgbDatabases.Controls.Add(CreateNewCheckBox("Checkbox9"));
            cgbDatabases.Controls.Add(CreateNewCheckBox("Checkbox10"));
            cgbDatabases.Controls.Add(CreateNewCheckBox("Checkbox11"));
        }


The results are as I expect:




Advantages of this method include the ability to tweak the variables within the CreateNewCheckBox method to determine what the starting X and Y coordinates will be based on the Location of the GroupBox; if I move the GroupBox somewhere else in my form's design view, the code won't care, it will simply churn out the CheckBox objects based on the parent control's current position at runtime. Also, it prevents new objects from being created outside the visible width of the GroupBox.

Shortcomings include a lack of checking the height of the GroupBox; if a zillion controls are created within it, there's currently no logic to prevent them from being placed in the nonvisible area of the control. Also, I'm currently not analyzing the incoming string parameter to see whether I want to constrain the resulting object to a uniform height or width, which means a CheckBox with a huge label likely will not display its Text value correctly.

Speaking of dimensions, the method is currently not "smart" enough to roll with the punches in terms of variations in height or width of the resulting controls; it doesn't offer an easy way to make the child objects conform to specific patterns (e.g. the objects appear one after the next from left to right, not up to down or diagonally or some other order). These would need to be tweaked and tested manually to ensure the object labels remain intact, and the objects themselves remain visible and usable. A more elegant solution would be to have the method scrutinize the control to be created and its attributes and compensate.

Baby steps, but nonetheless I found this a helpful exercise in creating CheckBox controls at will within a parent GroupBox, and already am seeing ways in which I can improve this implementation. I wish to thank Jordan Hammond, creator of the CollapsibleGroupBox control from the article I cited above, and also Manoli's code formatter, which was used here to make the C# excerpts presentable.

Tuesday, March 23, 2010