I've been using Windows 8 for awhile and I like it a lot. I can't say I liked the new Start Screen at first but it's grown on me. There is room for improvement though.
Anyways, back to installs Here's a short list I have for the moment and I'll come back and grow it as more things occur to me.
1) The Change UAC Settings dialog doesn't really disable UAC. You'll find your command windows aren't elevated and that you get UAC confirmation when updating files in the Windows directory. To completely disable UAC you have to go into the registry and change the EnableLUA policy registry value from 1 to 0 and reboot. I'm not saying you should do this, I'm just saying that if you *STILL* aren't creating installers that follow best practices with regard to custom action scheduling and execution context you really, really need to get on board. (Ahem Visual Studio Deployment Project users ).
2) .NET 2.0/3.0/3.5 is not installed by default. If you are using managed code such as custom actions that don't have an activation policy manifest that allows the use of newer versions of the framework they will blow up. (Ahem Visual Studio Installer Class Custom actions users)
3) Installing .NET 2.0/3.0/3.5 on Windows 8 is a pain in the back side. You can't use the EXE redist, you have to use a dism command to enable the feature. However, Microsoft didn't include the bits in the install.wim so it has to be able to go out to Windows Update and download source. In other words it's web/wsus enabled (via the OS) install only. There is no standalone installer (MSU) that I'm aware of. This is a sore spot and is best cured by moving your code forward to .NET 4.0 or beyond.
4) The new start screen shows all icons by default. The user can create groups (think groupbox ) but they can not create subfolders and zoom into to see the icons. In other words, if your installer creates a bunch of icons and you thought that was ok because you were putting them into subfolders to hide them, they will all appear and the user will have to unpin a bunch of shortcuts to clean up his desktop. (InstallShield, Visual Studio... lots of icons ) To prevent your installer from doing this, set the System.AppUserModel.StartPinOption to 1 using the MsiShortCutProperty table.
Those are my three biggies so far. As I discover more, I'll keep you posted.
Thursday, November 8, 2012
Saturday, November 3, 2012
Help Wanted - Austin, TX or Atlanta, GA
Calling all available Hard Core Deployment Engineers!
I'm looking for a setup developer with experience using three or more of the following:
WiX, MSI, InstallShield, C#, C++, Microsoft TFS (Source Control and Team Build ), Build / Release Automation
This is for a contract position in Atlanta, GA or Austin, TX supporting The Home Depot's Open Systems Engineering - Windows Automation team.
Feel free to reach out to me to discuss further.
No Third Parties.
I'm looking for a setup developer with experience using three or more of the following:
WiX, MSI, InstallShield, C#, C++, Microsoft TFS (Source Control and Team Build ), Build / Release Automation
This is for a contract position in Atlanta, GA or Austin, TX supporting The Home Depot's Open Systems Engineering - Windows Automation team.
Feel free to reach out to me to discuss further.
No Third Parties.
Tuesday, September 11, 2012
Visual Studio Deployment Projects Migration Assistance Available
With Visual Studio 2012 shipped, the news from a year and a half ago that Visual Studio Deployment Projects would no long be supported is finally hitting home with developers.
They generally aren't happy.
FWIW, I've converted hundreds of these projects to other project types using tools such as Windows Installer XML and InstallShield. I have this process down to a science and I'd love to have an opportunity to work with anyone needing assistance in moving forward.
You'll find that I have tools and methodologies that keep my costs very low while greatly improving the quality of your product's installer.
They generally aren't happy.
FWIW, I've converted hundreds of these projects to other project types using tools such as Windows Installer XML and InstallShield. I have this process down to a science and I'd love to have an opportunity to work with anyone needing assistance in moving forward.
You'll find that I have tools and methodologies that keep my costs very low while greatly improving the quality of your product's installer.
Thursday, April 19, 2012
Patriotic Opener
![]() |
| Credit: United States Marines / Released |
Back when I was in the Corps, I used to travel all over the world performing for audiences. One of our show formats was the Patriotic Opener. This was basically where we would march onto a stage and do a stand still performance. Over the years we performed at some impressive venues for some high up big wigs but none as cool or as high as this recent performance. You can watch the entire ceremony over at C-SPAN.
Saturday, December 24, 2011
IsWiX 1.5 Released
Release 1.5.11358.1 of Industrial Strength Windows Installer is now available for download over at CodePlex.
This release completes the "Component Rules" radio buttons on the Files and Folders designer. You can read about it in the release notes at CodePlex.
This release completes the "Component Rules" radio buttons on the Files and Folders designer. You can read about it in the release notes at CodePlex.
Monday, November 21, 2011
The Curious Case of the Corrupted Characters
Recently I was shown a machine where the PowerShell cmdlet Get-WmiObject was failing to query the Win32_Product class. The strange thing was that WMIC path Win32_Process was working.
I almost wanted to blow this off as yet another example of how Win32_Product is broken but there was a catch: I could fix and break the behavior at will by uninstalling and installing the MSI I was working on.
I started to wonder how this could be. I could only assume that there was some bad data causing casting problems in PowerShell ( COM interop is a pain ) so I decided to run MSI validation to see if it warned me of anything.
Sure enough, it did. A big ICE24 error alerted me to a problem in the ProductVersion property. Well, at least it tried to. 2.0.1600 sure looked like a valid ProductVersion to me. For the life of me I couldn't get it to stop complaining about that value so I decided to export the Property table to an IDT file. Sure enough notepad showed me a special character and a Hex editor showed me that there were actually two special characters corrupting my property.
I kept digging and found the root of the problem in the build automation NAnt file. A text file was being created and loaded into a NAnt property using the loadfile elemnt. Those two extra characters were coming along for the ride.
Let's see all the ways this could have been caught:
1) NAnt could have trimmed the property automatically.
2) ISCmdBld could have trimmed the property.
3) MSI Validation could have been turned on in the build.
4) MsiPublishProduct could actually enforce the required datatype of the property
5) The MSI WMI provider could trim the property.
6) The Get-WmiObject could trip the property or report a better error.
And of course, this post wouldn't be complete without me mentioning that there is a much better alternative to Win32_Product called the ProductInstallation class as found in WiX DTF's Microsoft.Deployment.WindowsInstaller interop. It's a very nice encapsulation of the unmanaged MsiEnumProducts function and doesn't have the horrible performance problems that Win32_Product has.
I almost wanted to blow this off as yet another example of how Win32_Product is broken but there was a catch: I could fix and break the behavior at will by uninstalling and installing the MSI I was working on.
I started to wonder how this could be. I could only assume that there was some bad data causing casting problems in PowerShell ( COM interop is a pain ) so I decided to run MSI validation to see if it warned me of anything.
Sure enough, it did. A big ICE24 error alerted me to a problem in the ProductVersion property. Well, at least it tried to. 2.0.1600 sure looked like a valid ProductVersion to me. For the life of me I couldn't get it to stop complaining about that value so I decided to export the Property table to an IDT file. Sure enough notepad showed me a special character and a Hex editor showed me that there were actually two special characters corrupting my property.
I kept digging and found the root of the problem in the build automation NAnt file. A text file was being created and loaded into a NAnt property using the loadfile elemnt. Those two extra characters were coming along for the ride.
Let's see all the ways this could have been caught:
1) NAnt could have trimmed the property automatically.
2) ISCmdBld could have trimmed the property.
3) MSI Validation could have been turned on in the build.
4) MsiPublishProduct could actually enforce the required datatype of the property
5) The MSI WMI provider could trim the property.
6) The Get-WmiObject could trip the property or report a better error.
And of course, this post wouldn't be complete without me mentioning that there is a much better alternative to Win32_Product called the ProductInstallation class as found in WiX DTF's Microsoft.Deployment.WindowsInstaller interop. It's a very nice encapsulation of the unmanaged MsiEnumProducts function and doesn't have the horrible performance problems that Win32_Product has.
Friday, October 28, 2011
Beam Me Up: Using JSON to serialize CustomActionData
Data driven custom actions require two custom actions. The first custom action queries custom tables and evaluates business rules and then passes CustomActionData to the second custom action which then performs the operations needed. Windows Installer calls this script generation and script execution but I call it "brains" and "brawn". That is the first action has access to all the information needed and can think about what needs to be done yet it doesn't have the access rights to do the work. The second action has all the rights to do the work but doesn't have enough access to teh MSI handle to be able to decide what needs to be done.
So just how does the brain communicate with the muscle? Through the CustomActionData property.
Today I want to demonstrate a lightweight technique of transporting complex instructions through this property using JavaScript Object Notation aka JSON.
Let's say I wanted a custom action that could create local user groups and I wanted it to be data driven. First I'd start off with defining custom tables that can express the
requirements:
This table basically says let's create a group called Rockers when SomeComponent is being installed. Now let's write some code. The brain needs to be able to communicate with the bronze so let's start with creating something they can both understand. A POCO class ( Plain Old C# Object ) that describes a Group:
Now let's write the brains:
We query the Groups table and then iterate through the rows. During this we evaluate if the component is being installed and if it is we generate a Group class and add it to the Groups list. Finally we serialize the List of Groups to JSON and put it in the CustomActionData collection as the Groups KeyValuePair.
So what does this CustomActionData string end up looking like? Let's look at the Windows Installer log:
MSI (s) (70!E4) [09:47:15:735]: PROPERTY CHANGE:
Adding CreateGroups property. Its value is:
Groups=[{"Name":"Rockers","Description":"People who rock!"}]
Now it's time for the brawn to flex some muscle:
We grab the value of the Groups KeyValuePair from the CustomActionData property and then deserialize it back into a List of Group. Then we iterate through the list and call the API needed to create the group(s).
So there you have it. Beam me up! A simple contextual example of how to use JSON in your installer. It turns out JSON isn't just for the web guys.
Disclaimer: This code was thrown together in about 30 minutes and has not been thoroughly tested. I'll be improving it over time and if you'd like a copy you can contact me via email.
So just how does the brain communicate with the muscle? Through the CustomActionData property.
Today I want to demonstrate a lightweight technique of transporting complex instructions through this property using JavaScript Object Notation aka JSON.
Let's say I wanted a custom action that could create local user groups and I wanted it to be data driven. First I'd start off with defining custom tables that can express the
requirements:
<CustomTable Id="Groups">
<Column Id="Group" Type="string" PrimaryKey="yes" Category="Identifier" Description="Unique Identifier of Group" />
<Column Id="Component_" Type="string" Width="72" Modularize="Column" KeyTable="Component" KeyColumn="1"/>
<Column Id="Name" Type="string" Description="Name of Group"/>
<Column Id="Description" Type="string" Width="255" Description="Description of Group" />
<Row>
<Data Column="Group">groupRockers</Data>
<Data Column="Component_">SomeComponent</Data>
<Data Column="Name">Rockers</Data>
<Data Column="Description">People who rock!</Data>
</Row>
</CustomTable>
This table basically says let's create a group called Rockers when SomeComponent is being installed. Now let's write some code. The brain needs to be able to communicate with the bronze so let's start with creating something they can both understand. A POCO class ( Plain Old C# Object ) that describes a Group:
class Group
{
public string Name { get; set; }
public string Description { get; set; }
}
Now let's write the brains:
[CustomAction]
public static ActionResult CostGroups(Session session)
{
var cad = new CustomActionData();
var groups = new List<Group>();
using (View view = session.Database.OpenView("SELECT `Component_`, `Name`, `Description` FROM `Groups`"))
{
view.Execute();
foreach (var record in view)
using (record)
if (session.Components[record.GetString(1)].RequestState.Equals(InstallState.Local))
groups.Add(new Group() { Name = record.GetString(2), Description = record.GetString(3) });
}
cad.Add("Groups", JsonConvert.SerializeObject(groups));
session["CreateGroups"] = cad.ToString();
return ActionResult.Success;
We query the Groups table and then iterate through the rows. During this we evaluate if the component is being installed and if it is we generate a Group class and add it to the Groups list. Finally we serialize the List of Groups to JSON and put it in the CustomActionData collection as the Groups KeyValuePair.
So what does this CustomActionData string end up looking like? Let's look at the Windows Installer log:
MSI (s) (70!E4) [09:47:15:735]: PROPERTY CHANGE:
Adding CreateGroups property. Its value is:
Groups=[{"Name":"Rockers","Description":"People who rock!"}]
Now it's time for the brawn to flex some muscle:
[CustomAction]
public static ActionResult CreateGroups(Session session)
{
try
{
var groups = JsonConvert.DeserializeObject<List<Group>>(session.CustomActionData["Groups"]);
foreach (var group in groups)
{
var ad = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer");
DirectoryEntry newGroup = ad.Children.Add(group.Name, "group");
newGroup.Invoke("Put", new object[] { "Description", group.Description });
newGroup.CommitChanges();
}
}
catch (Exception ex)
{
session.Log(ex.Message);
}
return ActionResult.Success;
}
We grab the value of the Groups KeyValuePair from the CustomActionData property and then deserialize it back into a List of Group. Then we iterate through the list and call the API needed to create the group(s).
So there you have it. Beam me up! A simple contextual example of how to use JSON in your installer. It turns out JSON isn't just for the web guys.
Disclaimer: This code was thrown together in about 30 minutes and has not been thoroughly tested. I'll be improving it over time and if you'd like a copy you can contact me via email.
Subscribe to:
Posts (Atom)
