Ken Kopczyk

Hurdles in .NET Development

UprightNJ.com

Posted by Ken on April 16, 2012

Just finished up on the first round of my freelance project, uprightnj.com.  I designed and implemented this from scratch with a little help from Fancybox for all the picture galleries and YUI Grids for the site’s wireframe.  It is hosted using Godaddy.  There are a couple pages that still need to be fleshed out, but I thought it had enough content to get launched.

Next steps:  SEO and Google Adwords.

Posted in Freelance Projects, Web Development | Leave a Comment »

Installing SVN on Windows is Easy!

Posted by Ken on December 21, 2010

I shouldn’t have to go into the importance of a good source control system. It allows you to do the following great things:

  • track code changes over time
  • rebuild old versions for technical support purposes
  • discover the original author of the software so you can playfully fingerpoint when it breaks
  • document features and bug fixes in a particular release
  • it’s a giant “undo” button!  (as described in The Pragmatic Programmer)

Installing an SVN server as a windows service is painless:

  1. Download and install the CollabNet SVN server and command-line client package on your server.
    • At the time of writing this, the latest stable version was 1.6.15.
    • Since we are installing this to run as a Windows Service, you can uncheck Apache in the “Choose Components” step. (Figure 1)
    • By default, SVN uses port 3690 and a default repository path of c:\svn_repository. (Figure 2)
  2. To set up a repository, run the following command:  
    svnadmin create "c:\svn_repository"
  3. Uncomment the following lines from c:\svn_repository\conf\svnserve.conf
    anon-access = read
    auth-access = write
    password-db = passwd
    
  4. Add users to c:\svn_repository\conf\passwd
    [users]
    # harry = harryssecret
    # sally = sallyssecret
    user = password
    
  5. Make sure port 3690 is open on your server if you have a firewall.
  6. Start the “CollabNet Subversion svnserve” windows service and you’re server is good to go!

Next, we need to install some client components that can access the server:

  1. Download and install TortoiseSVN on your client machine.
    • Allows you to access the SVN server and integrates into the windows shell.
    • At the time of writing, the latest version is 1.6.12, which is designed to work with SVN server 1.6.15
    • All default options are fine.
    • Requires restart.
  2. Download and install WinMerge on your client machine.
    • Default options are fine, but ensure that “Integrate with TortoiseSVN” is checked. (Figure 3)
    • The TortoiseSVN integration allows WinMerge to handle diff’ing of code history.

Figure 1

Figure 2

Figure 3

Posted in Source Control | Tagged: , | Leave a Comment »

MSI Upgrade Removes Necessary Registry Entries! Help!

Posted by Ken on December 13, 2010

Using a Visual Studio “Setup Project” is a quick and easy way to create a simple MSI installation package. Creating a basic installation package is as easy as selecting the items you want to package (exe/dll within your solution, Assemblies, miscellaneous files, etc), choosing their location (system directory, GAC, Desktop, etc) and flipping a couple of options within the Properties window. You can even have it automatically register assemblies if necessary. This is very helpful if you have to deal with older COM components.

Properties window for a Visual Studio Setup Project

After releasing an initial MSI version 1.0, I started running into issues with my 2.0 upgrade MSI. My intent was to have the new MSI remove all components/files installed by the 1.0 MSI, and then install the 2.0 components. I thought this would be accomplished by flipping the “RemovePreviousVersions” property to true, incrementing the “Version” property and changing the “ProductCode.”  However, I’ve experienced issues where registry entries were getting removed and files were not getting updated as they should. Turns out the MSI needed additional TLC which the “Setup Project” GUI within Visual Studio could not provide. Enter Orca.

Orca is a database table editor that gives you full access to all the settings within the MSI.  It provides much more granule control than what Visual Studio gives you.  It is available as part of the Windows SDK Components for Windows Installer Developers.  To fix all my upgrading issues, I used the following steps:

  1. In the Properties table, I’ve added the key/value pair: REINSTALLMODE = amus.  See MSDN for more info on this property.
  2. In the InstallExecuteSequence table, I modified the Sequence of the RemoveExistingProducts Action to come just before the InstallValidate Action. This is per the advice of Adrian Accinelli from the following thread: MSDN MSI Forum
  3. Finally, I checked to make sure the rows in the Upgrade table were correct. I modified it so that the row with ActionProperty = PREVIOUSVERSIONSINSTALLED had the version min set to 1.0.0.0, version max set to the version number of the previous version, and the Attributes set to 768. I modified the row with ActionProperty = NEWERPRODUCTFOUND to have version min set to the version number of your upgrade, version max set to blank, and Attributes set to 258. See here for details of the Attributes column.

Step 3: The upgrade table of an MSI

One thing to note:  Any of those three steps can be automated using Post-Build events, Cscript (built into Windows) and WiRunSQL.vbs, a VBscript included in the Windows SDK Components for Windows Installer Developers.  Here is an example using step #1 from above:

Cscript WiRunSQL.vbs foo.msi “INSERT INTO Property (Property.Property, Property.Value) VALUES (‘REINSTALLMODE’,’amus’)”

The moral of this story is:  A Visual Studio Setup Project is a quick and dirty way to create an MSI.  However, better tools are required if your installation becomes slightly more complicated.  Orca, WiRunSQL, and WiX are all helpful tools for building and maintaining MSIs.

Posted in Installer | Leave a Comment »

Debugging Javascript in Visual Studio 2005

Posted by Ken on March 12, 2010

A feature of Visual Studio that is not enabled by default is the ability to debug javascript.

To enable this feature, you need to uncheck “Disable script debugging (Internet Explorer)” in the “Advanced” tab of the “Internet Options” dialog in IE:

Debugging is now enabled:

Doing this will also give you access to Visual Studio’s Script Explorer which allows you to view and trace through the stack of scripts that are running within your ASP.NET application behind the scenes:

Warning: Client-side script debugging does slow down the application quite a bit during development. It would be wise to only have this feature enabled when you need to debug javascript.

Note: This information assumes that you are using IE as your default development browser. If you would like to change default browsers, select your website project in the Solution Explorer, and then select “Browse With…” from the File menu:

Alternatively, while the application is running in Debug mode, you can attach the process to another browser and run the two browsers in tandem. Select “Attach to Process…” from the Debug menu to accomplish this:

Posted in Visual Studio | Tagged: , , | 2 Comments »

Persisting User Settings Using Serialization

Posted by Ken on January 31, 2010

Recently, I was faced with the task of implementing custom user settings that were to be saved between sessions. My solution was to save these settings to a file during application tear down and to load the file as part of the application’s startup. To accomplish this, I used the following steps:

  1. Create an XML schema definition file (xsd) to define the data being stored.
  2. Create a Serializable class based on the xsd so the data may be instantiated/accessed in the code.
  3. Use serialization and deserialization to load and unload the user settings.

Using xsd.exe to generate a serializable class.
The first step I took was to create an XML schema definition file to represent my user settings data.  Once the schema was finished, I used the XML Schema Definition Tool (xsd.exe) to generate a serializable class from the schema.  This executable is part of the .NET SDK and is a real time saver. Use the following command line parameters to generate a serializable class based on an xsd file:

xsd.exe -c -l:c# -n:[namespace for the generated class] [XML Schema Definition File]

Example:

xsd.exe -c -l:c# -n:MyProgram.Utilities MyUserSettings.xsd

This example will create a serializable class called MyUserSettings.cs.

Serialization and Deserialization
Simply stated, serialization is the process of converting an object instance into a stream of bytes, text, XML or other formats so that it can be persisted into storage medium or transfered across a network. Deserialization is the opposite process which converts the persisted data back into an object instance. The following are sample methods for each process:

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace MyProgram.Utilities
{
    public static class Serialization
    {
        public enum SerializationMethod { Binary, XML };

        public static void Serialize(string sFilePath, Type type, object objectToSerialize, SerializationMethod serializationMethod)
        {
            if (!File.Exists(sFilePath))
            {
                return;
            }

            Stream writeStream = null;
            XmlTextWriter xtw = null;
            try
            {
                writeStream = File.Open(sFilePath, FileMode.Truncate);
                if (serializationMethod == SerializationMethod.XML)
                {
                    XmlSerializer xs = new XmlSerializer(type);
                    xtw = new XmlTextWriter(writeStream, null);
                    xs.Serialize(xtw, objectToSerialize);
                }
                else if (serializationMethod == SerializationMethod.Binary)
                {
                    new BinaryFormatter().Serialize(writeStream, objectToSerialize);
                }
            }
            finally
            {
                if (xtw != null)
                {
                    xtw.Close();
                }
                if (writeStream != null)
                {
                    writeStream.Close();
                    writeStream.Dispose();
                }
            }
        }

        public static object Deserialize(string sFilePath, Type type, SerializationMethod serializationMethod)
        {
            if (!File.Exists(sFilePath))
            {
                return Activator.CreateInstance(type);
            }

            Stream stream = null;
            object deserializedObject = null;
            try
            {
                stream = File.OpenRead(sFilePath);
                if (serializationMethod == SerializationMethod.XML)
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(type);
                    deserializedObject = xmlSerializer.Deserialize(stream);
                }
                else if (serializationMethod == SerializationMethod.Binary)
                {
                    deserializedObject = new BinaryFormatter().Deserialize(stream);
                }
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                    stream.Dispose();
                }
            }
            return deserializedObject;
        }
    }
}

Note that these methods support both XML (human readable) and binary (gibberish) de/serialization. This comes in handy if you want to prevent the settings files from being tampered with in a production environment, but you’d prefer them easier to work with during development. Using preprocessor directives, you can do something like the following to make the files human readable at development time, but garbled when they are released into the wild:

MyUserSettings userSettings = GetMySettings();
#if DEBUG
Serialization.SerializationMethod serializationMethod = Serialization.SerializationMethod.XML;
#else
Serialization.SerializationMethod serializationMethod = Serialization.SerializationMethod.Binary;
#endif
Serialization.Serialize(sFullSettingsFileName, typeof(MyUserSettings), userSettings, serializationMethod);

Posted in .NET SDK, Serialization | Tagged: , | 1 Comment »

Disabling and Enabling the Close Button in .NET 2.0 WinForms

Posted by Ken on January 23, 2010

In .NET 2.0 WinForms, the user is given the following control over the minimize/maximize/close buttons (otherwise known as the ControlBox):

  • Disable/enable the Minimize button via the Form.MinimizeBox property
  • Disable/enable the Maximize button via the Form.MaximizeBox property
  • Show/hide the entire ControlBox via the Form.ControlBox property

However, you may want to allow maximizing and minimizing, but remove the user’s ability to close the application using the close button for any number of reasons. For example:

  • You want to run some process that cannot be cancelled midway through.
  • You want to ensure a user completes some number of tasks before being able to exit.

Unfortunately, this functionality is not supported by default. One must leverage the unmanaged code of the Windows API to achieve this behavior:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public class SuperForm : Form
{ 
    private const int MF_BYPOSITION = 0x400;

    protected void DisableCloseButton()
    {
        IntPtr hMenu = GetSystemMenu(this.Handle, false);
        int menuItemCount = GetMenuItemCount(hMenu);
        RemoveMenu(hMenu, menuItemCount - 1, MF_BYPOSITION);
        DrawMenuBar((int)this.Handle);
    }
    protected void EnableCloseButton()
    {
        GetSystemMenu(this.Handle, true);
        DrawMenuBar((int)this.Handle);
    }

    // Win32 API declarations
    [DllImport("User32")]
    private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
    [DllImport("User32")]
    private static extern int GetMenuItemCount(IntPtr hWnd);
    [DllImport("User32")]
    private static extern int RemoveMenu(IntPtr hMenu, int nPosition, int wFlags);
    [DllImport("User32")]
    private static extern IntPtr DrawMenuBar(int hwnd);
}

Notice that I incorporated this functionality into a base class that all your forms can inherit from to gain this ability.

Posted in WinForms Tips | Tagged: , | 6 Comments »