While I'd usually like to abstain from sharing personal or political viewpoints on a technical blog, this one was too funny. I take for granted everyone on the blogsphere knows about the Dan Rather / 60 Minutes II documents regarding Bush that appear highly likely to be forged. Well, there is a new product that can be used for document forgery. I can see Clippy saying, "So... It appears you are trying to forge a document..."
Sunday, September 19, 2004
Tuesday, September 14, 2004
Detecting .NET Framework 1.1 SP1
Aaron Stebner from Microsoft blogged about how to determine if the 1.1 version of the .NET framework is installed. Unfortunately, that is just to detect if 1.1 is installed.
One method of checking that Aaron quoted is described by a Microsoft Knowledge Base article. This approach is impossible to perform programmaticly. He also gives a registry key to check for the 1.1 .NET framework, but is a bit unclear if this key is for 1.1 or 1.1 with the service pack.
Other methods of checking I uncovered are difficult to do in code in a manner that makes sense to work in the generic case. The confusion that led to Aaron's post in the first place are questions (and the answers/lack of answers) posed in newsgroups, like this one.
The .NET framework version checking problem reminds me of old InstallShield code. I can't recall if this was built into InstallShield or if it was a bad code sample, but lots of installs failed on Windows 2000 simply because the install code checked to see if you were NT 4.0 SP3 or higher. The detection code was not based on proper logic, but rather if OS version >= 4 and SP version < 3, do not install. What a headache (at least until Windows 2000 SP3 came out)!
Fortunately, Aaron is going to research getting a code sample to detect the old versions and hopefully Microsoft can provide a future-proof method to determine this in upcoming revisions.
Saturday, September 11, 2004
Wrestling with the MSI setup editor in Visual Studio
Tim Anderson blogs about editing an MSI after it is built by Visual Studio .NET. There are various tips hidden in here about how to cope with duplicate assemblies being added to the MSI, marking a file so upgrades don't overwrite it, invoking the Custom Action editor, and MSI SQL.
For more detailed information related to editing an MSI after it is built, check my previous blog entry "MSI - Modifying the Installer Database at Runtime" and scroll down to Solution 1.
For more information as to how to launch a program after installation, see How do I use a custom action to launch an installed file at the end of the installation? from MSDN.
Tim also mentions as to why he stuck with using the Visual Studio MSI packager instead of a commercial editor. He gives 4 reasons, and I would add purity as a fifth. Regardless of the authoring tool he uses, his code will always work. Granted, pretty much any tool he uses will eliminate the need for his particular edits to the MSI, but the concept is still valid.
Monday, September 06, 2004
MSI - Modifying the Installer Database at Runtime
A recent question posted to the Microsoft.public.platformsdk.msi newsgroup:
I'm trying to prevent some NT services installation with a VBScript custom action (type 38) that removes the corresponding rows from the ServiceInstall table.The poster shows some example code that does not seem to work. Let's look at some approaches to try to solve this problem.
Solution 1
Here's some theoretical JScript to run in a Custom Action that modifies the installer Database:
oDatabase = Session.Database;
oView = oDatabase.OpenView("select * from ServiceInstall");
oView.Execute();
//now our view object has records we can "fetch"
oRecord = oView.Fetch(); //get the first record from the query
while (oRecord != null) //make sure we are not at the end.
{
//TODO: Either handle the logic on what to remove based
// on the oRecord here or in the query. Assumes all
// records matching the query are removed.
oView.Modify(6,oRecord); //6 = msiViewModifyDelete
//get the next record from the query
oRecord = oView.Fetch();
}
oView.Close(); //close the view
oDatabase.Commit(); //commit our changes
Looks pretty good, right? We are opening the database, fetching records, modifying the records, and committing the changes. Sad to say even though the API logic is correct, this won't solve the problem at hand! Why?
Custom actions can only add, modify, or remove temporary rows, columns, or tables from a database. Custom actions cannot modify persistent data in a database, such as data that is a part of the database stored on disk. (View.Modify documentation excerpt)As an aside, you could substitute the following 2 lines for the first one and modify the msi (permanently) outside a CA:
oInstaller = new ActiveXObject("WindowsInstaller.Installer");
oDatabase = oInstaller.OpenDatabase("myInstall,msi",2);
Solution 2
So, with this restriction, how do we solve your problem? The answer lies (buried) in the View.Modify documentation. The action type msiViewModifyInsertTemporary is our friend. Instead of solving the problem by deleting the services you don't want to install, we can add the services you DO want to install at runtime! Remember to delete the lines from the ServiceInstall table if you use this method
Lets look at an example of this:
oDatabase = Session.Database;
oView = oDatabase.OpenView("select * from ServiceInstall");
oView.Execute();
oRecord = Installer.CreateRecord(13);
oRecord.StringData(1)="MyService";
oRecord.StringData(2)="MyServiceName";
oRecord.StringData(3)="MyServiceDisplayName";
//SERVICE_WIN32_OWN_PROCESS
oRecord.IntegerData(4)=16;
//TODO: continue for the rest of the columns in this table. See
// the ServiceInstall table docs for more information
//msiViewModifyInsertTemporary the record.
oView.Modify(7,oRecord);
oView.Close(); //NOTE: no need to call commit..
You do need to deal with the uninstall issue using another CA. That is not covered here, but the concept is similar.
As an aside, I use the above technique to adjust the ReserveCost table when I need to create a large database. If the DB exists, I do nothing, otherwise, I add the size of the database to the table for proper costing.
Solution 3
Although Solution 2 will solve this problem, why would you want to go through that complexity when there is a much better (and simpler) method built into the MSI engine? In the ServiceInstall table, there is a Foreign Key into the Component table. If the component is to be installed, so is the service.
Simply create a hidden feature, assign the components necessary for the service to that feature, and conditionally install (or not install) that feature.
Saturday, August 28, 2004
New Look (aka the joys of CSS)
I changed the look of the blog today. I was getting tired of the "default" look and decided to customize it a bit. I started with the default Blogger "TicTac" theme, and then customized it.
I now have a banner image that randomly rotates on each page reload. The featured images (for now) are some excellent panoramic pictures of the City of Pittsburgh. Some are modern, some are from the early 1900's.
I also adjusted the feeds through FeedBurner. If anyone has problems subscribing, please let me know - it should be able to handle most RSS/Atom clients with a single subscribe link. If you are a blogger, give FeedBurner a try. See the sidebar for a link.
As for CSS, I think today is the longest amount of time I spent with it, and that was only a couple of hours. It was fairly easy to pick up. What is really interesting is how you can use CSS or HTML to define STYLES at design time, and at runtime you can change 99% of them. The :hover property of the 'A' object is not accessible from script.
One of the more interesting problems I had to solve was handling text coloring for the items that need to overlay the new banners. Of course, I need to specify certain things for each image - the color of the text/links, the color of hovers over the links, and the height of the image. At runtime, I randomly select an image, and the styles are changed to match. The title of the blog becomes a hyperlink to the main page if you are on an anchored article or in the archives. Since I couldn't change the :hover, I ended up cheating with handling onmouseover and onmouseleave events.
Anyway, if you want to check out the modifications to the modified CSS, you will have to view source, since I have no idea what the direct link to the CSS file is. If you want to use it, just send me a note or something!
