Thursday, April 16, 2009

MSI Factory & Passing Command Line Arguments to a Custom Action

I created an installer using MSI Factory. Part of my setup logic is to launch an application at the end of the installation so that the user can complete the configuration process that requires them to deploy some customizations to their Team Foundation Server (TFS). I accomplish this by using Custom Actions and pass the executable as one of its parameters the installation folder or the value of INSTALLDIR, for example. The results, however, are not as expected.

In my installer I set the Command line arguments option in the Run Executable dialog of the Custom Actions editor to the following:

-p “[INSTALLDIR]”

One would expect that the property INSTALLDIR would be replaced by the actual value. At first I thought it did not since researching about it seemed to indicate that this was the case, as it was reported that way by many.

To test the case further I decided to “hard-code” the value of the expected path to pass as an argument in the installer Custom Action and things seem to work as expected.

So, I added logic in my executable code to write to the Event Log the possible causes of this issue (errors and the actual arguments passed). The following entry would happen when I execute my application as part of my installer through a Custom Action.

Event Type:    Warning
Event Source: Process Template Item Deployer
Event Category: None
Event ID: 0
Date: 4/12/2009
Time: 3:57:28 PM
User: N/A
Computer: TFSRTM
Description:
Timestamp: 4/12/2009 7:57:28 PM
Message: System.ArgumentException: Illegal characters in path.
at System.IO.Path.CheckInvalidPathChars(String path)
at System.IO.Path.NormalizePathFast(String path, Boolean fullCheck)
at System.IO.Path.NormalizePath(String path, Boolean fullCheck)
at System.IO.Path.GetFullPathInternal(String path)
at System.IO.DirectoryInfo.ctor(String path)
at InCycle.Tools.Forms.ImportProcessTemplateItem.GetSupportedFilesFromPath(String path)
Category: General
Priority: -1
EventId: 0
Severity: Warning
Title:
Machine: TFSRTM
Application Domain: ProcessTemplateItemDeployer.exe
Process Id: 3956
Process Name: ProcessTemplateItemDeployer.exe
Win32 Thread Id: 3580
Thread Name:
Extended Properties:

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

I then decided to inspect previous Event Log entries and found the following interesting entry.

Event Type:    Information
Event Source: Process Template Item Deployer
Event Category: None
Event ID: 0
Date: 4/12/2009
Time: 3:57:27 PM
User: N/A
Computer: LEOV-TFSRTM
Description:
Timestamp: 4/12/2009 7:57:27 PM
Message: Attempting to load files from "C:\Temp\InstallFolder""
Category: General
Priority: -1
EventId: 0
Severity: Verbose
Title:
Machine: LEOV-TFSRTM
Application Domain: ProcessTemplateItemDeployer.exe
Process Id: 3956
Process Name: C:\Program Files\InCycle Software\InCycle Add-ons\bin\ProcessTemplateItemDeployer.exe
Win32 Thread Id: 3580
Thread Name:
Extended Properties:

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

As you can see, for some reason “[INSTALLDIR]” is expanded correctly with one small problem, it seems to pass an extra quote at the end.

I then changed my code to add logic to fix the path that I expect as an argument before I use it elsewhere in my code. The following code snippet shows you the argument extraction with the added line that fixes the problem.

// Determine if a default path has been specified from which to load files.
if ("-P".Equals(args[0].ToUpperInvariant()))
{
// Assign it for later use.
loadFilesFromPath = args[1];
// When launched from a MSI there may be invalid characters. Therefore,
// make sure to remove all such characters before proceeding.
loadFilesFromPath = loadFilesFromPath.Trim(Path.GetInvalidPathChars());
// Display debugging details.
Logger.WriteDebug(string.Format(CultureInfo.InstalledUICulture, "Attempting to load files from \"{0}\"", loadFilesFromPath));
}

Using string.Trim with Path.GetInvalidPathChars() allowed me to clean the errors from the given path.

I don’t know whether the problem lies in MSI Factory or in any underlying component, but this fixed it for me. Unfortunately, I don’t really have a way to fix this for executables for which I don’t have the code. Perhaps, writing a bootstrap application may help. But that’s another post.

Read full post...

Monday, April 13, 2009

Team Foundation Server (TFS) Customizations in x64 Environments

While testing one of the Visual Studio Team System (VSTS) extensions I wrote I came across a scenario where the assembly was failing to load under the 64-bit version of Windows 7. It turns out that by default when one creates a project in Visual Studio 2008 the target is set to Any CPU. This in turn sets the Processor Architecture of the resulting assembly to MSIL, which essentially lets the Operating System (OS) decide which .NET Framework version and flavor to use (for all intense and purposes). This causes a problem when writing extensions for VSTS such as Work Item Tracking Custom Controls and other applications that leverage the Team Foundation Server SDK.

When loading such an assembly (of type MSIL, the default created when using the target Any CPU) in a x64 environment it will use the corresponding .NET Framework assemblies, which are in this case also 64-bit. The problem is that since all Team Foundation assemblies are 32-bit (or specifically of type x86) the mix causes the application to fail loading, in my case with the following error (as displayed in the Windows Event Log).

Faulting application ProcessTemplateItemDeployer.exe, version 1.0.0.0, time stamp 0x49de2a19, faulting module KERNEL32.dll, version 6.0.6001.18000, time stamp 0x4791ada5, exception code 0xe0434f4d, fault offset 0x000000000002649d, process id 0x%9, application start time 0x%10.

As you can see this is not very helpful. More details can be retrieved, however, from the error dialog itself that shows up when the application falters. In my case, the following snippet is displayed (extras removed for brevity).

See the end of this message for details on invoking just-in-time (JIT) debugging instead of this dialog box.
************** Exception Text **************
System.InvalidProgramException: Common Language Runtime detected an invalid program.
at InCycle.Tools.Forms.ImportProcessTemplateItem.ImportProcessTemplateItem_Load(Object sender, EventArgs e)
… (extra text removed …)
************** Loaded Assemblies **************
mscorlib Assembly Version: 2.0.0.0 Win32 Version: 2.0.50727.3521 (NetFXspW7.050727-3500)
CodeBase: file:///C:/Windows/Microsoft.NET/Framework64/v2.0.50727/mscorlib.dll

What is interesting here is the last line. Notice that the mscorlib.dll that my application is referencing comes from the Framework64 folder of the .NET 2.0 Framework.

The solution I found, after quite a bit of research and trial and error, is to force the extensions to specifically target x86. Doing so forces the OS to load the 32-bit framework assemblies at run time, even in a 64-bit OS.

Refer to the article How to: Optimize an Application for a Specific CPU Type on MSDN for specific details on how to change the Processor Architecture.

Read full post...

Monday, April 6, 2009

Sys is undefined when registering script via RegisterClientScriptBlock

Writing web applications using ASP.NET, most often than not, requires you to implement JavaScript code. If you are using AJAX and embed the script code as part of your page rendering you may run into the vague Sys is undefined error.

I have found that the error Sys is undefined is often caused when registering script using RegisterClientScriptBlock. The reason is that the AJAX Script Manager has yet to initialize. When using RegisterClientScriptBlock the script is placed immediately after the start of the form tag. In contrast, RegisterStartupScript places the script just before the closing form tag.

Therefore, the solution for this issue is to use RegisterStartupScript instead of RegisterClientScriptBlock.

Read full post...

Wednesday, March 18, 2009

Code Snippet plugin for Windows Live Writer v2.0.0

It's been a while since I looked at this plugin, just as it's been a while since I blogged (but that's another story). I have spent the last day or two implementing most of the features listed below in the current code base. This release is fully functional and I am looking for some of you folks to help me determine its stability in other environments.

Here's a list of what is included in this release:

Screenshots

Please take a look at some of the screenshots I posted in Code Snippet plugin for Windows Live Writer section of the the Plugin Collection for Windows Live Writer project at CodePlex.

New Languages

The following formats have been added to the list of supported languages:
  • AutoIt (never heard of it, saw someone looking for such support elsewhere and decided to add it, why not!)
  • ColdFusion
  • Java
  • PHP
  • Regular Expression

New Options Dialog

One of the things I wanted to do for a long time was expose some of the configuration parameters for the plugin. In previous versions, some of these configuration parameters were available in the configuration file. But now, you can edit them through a dialog and more of the options have been exposed.

New Features

Customize Styles
In previous releases the styles used to apply the syntax highlighting were exposed in a very limited fashion. You could only get the cascading style sheet to include as a reference in your website. However, this works great only if your audience is viewing your posts in your website. If you want to make sure your RSS feeds, for example, have the proper formatting, you need to embed the styles.
Now, you can change the look of your code snippets by modifying these styles through the Options dialog. The changes are stored in the configuration file and will be used to apply to any future code snippets you insert in your posts.
Edit Code Snippets
In the original implementation, I did not want to use the in-place editing features of the Windows Live Writer API. So, I opted for a different implementation where you can highlight existing snippets of code, whether or not these were inserted using this plugin. Basically, any highlighted text can be converted to a code snippet. This is a great feature, if I may say so myself, for re-applying style changes, if you previously embedded the styles in your post.
Convert Any Text to a Code Snippet
Using the same feature for editing snippets of code, you can now select any text in your post and convert it to a code snippet.

Miscellaneous Enhancements

Here's a list of additional enhancements to the plugin. These enhancements are not visible to the end user but in my opinion make the package more robust and complete.
  • Refactored style logic to allow exposure through Options dialog.
  • Migrated solution to Visual Studio 2008 SP1.
  • Verified / Updated all source code documentation for completeness and to ensure it is as accurate as possible.
  • Localized all string resources, including those used in the Options dialog using some custom classes, such as Attribute classes, for use in the PropertyGrid control. (Anyone interested in translating these resources?)
  • Added stand-alone application to launch the Code Snippet plugin outside of the context of the Windows Live Writer editor. This is helpful for debugging, or when you just want to get some formatted HTML to post in a forum or something. (This is not pat of the deployment - let me know if you think it should be.)
  • Analyzed the source code through the Code Analysis feature of Visual Studio 2008.
  • Fixed the source code based on ReSharper 4.1 and Code Analysis recommendations.

Please refer to my original post for details on some of the other features the plugin offers.

Read full post...