Tuesday, December 21, 2004

XMLRPC-C 1.0 for Win32

If anyone is using (or trying to use) the XMLRPC-C library on Win32 and having issues with getting the examples (or anything) to compile and/or work with the WinInet transport, I have created an archive of changes to the 1.0 distribution that will probably solve your problems.

A few of the changes are likely not "clean" (but good enough for me to start on a quick prototype of something else). If/when I have some time later I will fix them up, confirm it doesn't break *NIX distributions, and send them in. In the meantime, if anyone would like the archive email me and I'll send it on - normal caveats apply.

Kudos to Bryan Henderson for recently resurrecting this library from an over 3 year grave. And thanks to Eric Kidd for the initial work!

Saturday, December 18, 2004

Abstractions in Design

Peter Torr from Microsoft blogged that Changing Abstractions in an Object Model is Hard. I think of system design as the "art" portion of software development. I consider a "programmer" as a person who is familiar with the technical (both normal and arcane) details of an operating system, language, or framework. The "software developer" needs to know the former, and branch out from there, thinking of object models, end-user usability, threat models, abstraction, and extensibility.

The italicized font portions below are quoted from the blog entry (some liberty with snipping was taken for readability out of context). If you are interested in learning more about solid design, this essay is a good high level overview. These are (IMHO) the salient points of the essay:

But to make a[n] object model easy to use, you want to simplify (abstract away) many of the details so that the developer has less concepts to understand and so that they can "discover" and use the features of the object model in a natural way.

...you don't want to make your system more complicated than it needs to be. Complications lead to bugs, and unnecessary complications lead to unnecessary bugs. Having lots of "pluggable" components (interfaces, abstract classes) makes it hard to reason about the system (and to threat model it!) because you can't be sure what any given extension will do.

Or, if she is just getting started with the object and starts to read the conceptual overview, there will be far too many things obscuring the basic design that she won't be able to see the forest for all the trees. This is a real concern, since documentation is incredibly hard to write and minimizing the number of concepts a developer must grasp to use an object model is crucial to making it usable.

Active Setup Registry Keys and their Purpose

I’ve been very busy lately, and I’m sorry about the blog being so quiet. I haven’t gotten around to completing the Custom Action DLL tutorial, but I’m pretty close. I need to find a server that would let me store some zip files. Any ideas?

Back in September, Aaron Stebner blogged about detecting what .NET 1.0 Service Pack is installed. One of the keys he mentions checking has the word "Active Setup" in it. Just what is that key for?

The Active Setup key in Local Machine is read when a user logs in to the system. Keys in the Local Machine registry hive are compared against keys in the Current User hive. If a key that exists in Local Machine does not exist in the Current User hive, the program the key points to is run. Effectively, this is one way to customize (or completely remove) an installation on a per-user basis, assuring that a program is executed exactly once per user.

There are several installations that use this key. Microsoft Net Meeting and Internet Explorer are a few examples. Lets say you update Internet Explorer to a new version – like IE 5 to IE 6 – and you have two active user profiles. When the computer is restarted and the user logs in, you see a dialog that shows IE is being configured. When the other user logs in, they see the same dialog. This is Active Setup at work! Net Meeting uses this mechanism to cleanup user profiles after Net Meeting is uninstalled. Sadly, virus and spyware packages also use this mechanism (changing the Local Machine key after each reboot), forcing the vigilant advanced user to check yet another key for items that run at startup. This is yet another reason to run as a restricted user!

The limitations of this mechanism are simple: The program that is run from Active Setup runs in the current user space. Therefore, to be completely safe, any Active Setup program should require read/write access of the most restricted user type – only modifying files and registry keys owned by an individual user. Additionally, this is run BEFORE the shell and other run keys.

There is no "official" documentation on these keys or this behavior, so the normal caveats apply to using this mechanism – it may not be present in newer versions of the OS or Service Packs.

Here is some detailed descriptions of the keys and their contents. A registry key called "HKEY_LOCAL_MACHINE\Software\Microsoft\Active Setup\Installed Components" has a bunch of subkeys under it that are GUIDs. Values contained under the GUID key that we are talking about are "Version" (string), and "StubPath" (Expand String).

Every time a user logs in, the contents of this key is compared against the SAME key in HKCU. If the HKCU key does not exist and/or the "version" value is less than that in HLKM, the appropriate "StubPath" command is run and the key copied to HKCU so it is not run again.

To see how this works for yourself, you can create a key in HKLM…Installed Components and call it "test". Then, add to it a String Value "StubPath" and set it to "notepad". Reboot. See that when you log in, notepad starts. Log in as a different user. See that notepad starts. Log in again with the first ID – notice notepad does not start – as now the "test" key has been copied into the HKCU branch after it ran the first time. Note that version is not necessary.

Since I am not sure what the other values of these keys mean and/or do, I’m going to end the discussion here. If anyone has more information on these keys, please add it to the comments for this post!

Monday, November 15, 2004

Insane security

Raymond Chen posted about some questions better left unanswered because the answer is unreliable. This quote cracked me up:

"You cannot reliably reason about the security of a system from within the system itself. It's like trying to prove to yourself that you aren't insane."

Although the quote is funny, he makes several points about security that developers should heed. Commenters have taken the post a bit out of context - but the side point he makes is valid. Since most of my audience is setup/installation developers, I'll direct my comments towards that community.

The next time you are given a design document for installing a service, please make sure to ask about the security level required for the service. Make sure to raise objections to installing a service with access to the desktop without good reason (read comments to this post for more information related to the shatter attack) . Ask if LocalSystem access for the service can be reduced to a more restrictive account (HINT: There is a sample for creating user accounts within an MSI in the Platform SDK). Most of all, insist that scoping of installation/deployment design is best when it is done with the scoping of the feature or application - not after. This way, the application developer can develop in an environment closest to that which will ultimately be deployed to customers in the installation with no surprises looming at the end of a development cycle.

In case you haven't noticed, Windows XP SP2 and Windows Server 2003 have tightened up security in these areas. Instead of blindly loosening up the default restrictions this brings, take the application to the next level by understanding why the restriction is now the default in the first place, and learn how to program taking into account this more secure environment. As a setup developer, your customer is the end user. Make sure you are on the side of the customer when it comes to security.

Thursday, November 11, 2004

.NET Threading

Although my blog states I'd be covering some C# stuff, I haven't posted anything related to it. Time for that to change:)

I've been fighting with threading in a C# .NET application. Perhaps my frustration was related to the poor coverage of threading in pretty much any .NET book. Perhaps it was because MSDN doesn't really have a decent overview or "related links" in any of the threading-related topics. Another guess is that .NET is dissimilar to Java in its threading mechanism. Most likely, I have been spoiled by Win32's richness and capability when it came to threading.

I uncovered an excellent link to "Multi-threading in .NET" It is currently in 2nd place on a Google search for that term. I am using my limited "Google karma" to help pump that resource to #1 - the article is that good. Newbies to the .NET framework will not be overwhelmed, and moderately experienced .Net'ers will not be bored by it. It simply takes everything about threading and puts it into one place with some great examples. There is a slight C# bent to it. The article contains some .NET 2.0 items, and appears to be regularly updated. One thing I would change - it would be nice to have a "what's new" section for repeat visitors. Jon has plenty of other .NET related articles from his C# page. I haven't read anything else by him yet, but I'm assuming the quality bar is nearly the same.

Some favorite quotes: "...writing thread-safe code is all about taking luck out of the equation." Relating to changing Form or Control elements from a different thread than what they were created on: "You may get away with it in some cases, but only by blind luck."

Friday, October 29, 2004

Joel's best articles reviewed

Even though I have a billion other things to do, I couldn't help but get sucked into reading every one of the 112 (as of this writing) articled submitted to Joel's best articles on Software Development. This is a deviation from my normal content, but there is value to be gained for most all readers in the Software industry - designers, marketers, and developers.

Here are my favorites (not necessarily the best of the bunch, but ones that got me to think), I will link to the nomination post, suggest the audience type, and give my .02c. :

Church of XML. If you use XML and like sarcastic humor, this may interest you. At least, I hope it is meant to be sarcastic. XML is great for some things, but it is not the holy grail solution for everything data related. I remember in Y2k working for a company whose directors told the development staff that we must use XML. We quickly coined the phrase "buzzword compliance" to describe the requirement. There was no possible use for XML data in our application where XML was a viable alternative, much less a better one. At a later company, the value of XML as a method of integration with our customer's systems and as a communications mechanism (XML-RPC) was profoundly positive. Members of the Church of XML may want to defect after reading another few nominated articles: ant and XML, and Soapbox: Humans should not have to grok XML.

How many Microsoft Employees Does It Take to Change a Lightbulb. Any developer should enjoy it, but managers (especially ones driving deadlines) need to read it. I am a big fan of Eric Lippert's writing style. This one delves into the cost of fixing a bug. I prefer this one to the main article nominated since it is more to the point. Setup developers may want to read The anatomy of a bug as it deals with an uninstallation issue, and something you may want to think about. Both articles do not mention the cost of fixing a bug in terms of dollars (refer to your copy of Code Complete for more on this). Although you may disagree with Eric's line: "At Microsoft we try very, very hard to not release half-baked software," the point he is trying to make is if you do release bad code, it costs a bunch (in dollars and manpower) to fix it later. Almost on-topic is a 20 minute video of an actual bug triage meeting at Microsoft. Good stuff.

Plus ca change. Reading for anyone interested in how standards can be used to even the playing field for small software vendors. Or perhaps not. While I am not heavy into the web standards discussions today, at one point I was. To quote The Gunslinger, "The world has moved on." HTML's initial purpose (IMHO) was to allow me (the user) to render content as I (the user) wanted to see it. Netscape and Microsoft decided that their extensions to HTML would be the differentiators between their browser products. The content producers decided that web content should be shown to the user the way the content producer wants it to look, and with these extensions, they could. This seriously limits the accessibility of the content for people with disabilities, which was one characteristic HTML was supposed to give content producers (and users) for free. Flash and Shockwave further limited the control of content by the user. My thoughts on this today are for vendors to add their extensions whenever they want. If the extension provides enough value, other vendors will pick up on it and it will become a defacto standard. Today, implementing a standard in a web browser is not good enough to get you lined up at the starting gate. Look at Internet Explorer - massive share and not 100% compliant.

Lean Construction. Audience is general developers interested in planning. If you have read Code Complete, you will notice the use of metaphors throughout, one of which likens software development to a construction project. This article is the result of a software developer attending a class for construction foreman related to project planning. Some of the takeaways trumpet agile development methodologies. I especially like the quote: "master schedules are useless for planning work, contracting practices create islands of optimization, and there are large opportunities for productivity improvement." Is the author referring to current software development methodologies or old construction planning?

The 5 Pitfalls of Estimating a Software Project. Also see "Repeat After Me: I Am Not In the Software Business" in the same nomination. Everyone should read these. I have nothing else to add - they both have excellent points and are short enough to easily grasp it.

In the Spirit of C. Hardcore programmers. This one is fantastic. In the "managed" world of programming frameworks, it is easy to lose important roots in development. I am completely surprised by the number of programmers that do not know C or C++ well enough to code a real application. I've found that the best programmers are ones with roots in C. C developers tend to be able to learn new languages quickly, and pick the best language for any given programming task. Noteworthy quote: "In my experience there is almost no limit to the damage that a sufficiently ingenious fool can do with C++. But there is also almost no limit to the degree of complexity that a skillful library designer can hide behind a simple, safe, and elegant C++ interface. "

Ten Ways to Kill Design. Product managers are a good audience. This talks about new designs and pilots, and how/why they fail (unrelated to the design itself). For more information on the design, spec, and requirements, please check out another nominee On Reqs and Specs.

Hazards of Hiring. For anyone considering beefing up your workforce or looking for a job. It is an Eric Sink article that is very good, as usual. There are more worth reading that are related, such as programmers are not hackers.

Simple is Beautiful. All developers. Quote: "If you manage to write some code that a non-programmer can more or less understand, then you know you're there. The code should be simple and self-explanatory enough that should be a breeze to refactor in the future." Another nice quote is from Brian Kernighan (an inventor of C). I have this quote hanging up by my desk: "Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it."

We built it but no one came. Product Designers. Quote: "the thing that engineers love most about their technology is precisely what prevents them from making money." I looked at Steve Johnson's blog and he writes lots of great stuff in the development/marketing arena. Subscribed! I recommend anyone in the position of designing a product for the Average Joe to pick up a book Crossing The Chasm by Geoffrey Moore.

Growing, Pruning, spiking an architecture. Designers. Quote:"Good architectures are those in which it is considered easy to create the features desired."

What is a Web Service. Anyone. I agree with the other commentator. This is more of a definition, but summed up well. This allows me to segue into the topic of dynamic vs. loose coupling in typical service oriented architecture, although this latter link is more specific to architects.

Exceptions vs. Status Returns. Developers. The age old debate on the use of Exceptions. My position is contrary to the nominee. If you take the debate further, there is the use of exceptions as flow control. Exceptions are good when used appropriately - for the exceptional case. Too many times I have seen code that encourages or uses exceptions when simply returning a status value would be better. There are some good examples here. An OutOfMemoryException is a valid use of an exception. No matter how deep in my code I am, this is something that is highly unlikely to be handled appropriately or anticipated.

How to tell your personality from your code. Developers. Quote: The Obfuscator: "while(*a) *b++ = (islower(*a))? *a++:*a++ + ('a'-'A');" I have not only worked with each of these coding personality types, but I have been them. I wonder what my code personality type was today?

Choose your competition. Entrepreneurs. Quote: "The existence of a competitor indicates the existence of paying customers." As usual, good stuff from Eric Sink.

State of Affairs in Computer Science Education. Everyone. I was going to write a blog entry about this one. Since I'm trying to get a new sheepskin, one of the classes I am required to take uses a book "Object-Oriented Application Development using Visual Basic .NET" by E. Reed Doke. The book stinks. The fact that I must take and pay for this "capstone" course instead of one where I could actually learn something new is one problem with the system. In this book, the author uses a phone number as the primary key on the Customer table! I guess with phone number portability, this is an acceptable PK! We are teaching people this? The brilliant author creates a method in the Customer class called TellAboutSelf() that returns a string representation of the object. Wouldn't it be industry-standard and expected to override Object.toString()? WTF! This simply adds to the retraining requirement of new grads that enter the workplace. This is another segue into yet another nominee Eric Lippert's Cargo Cult Series. If you are a new or experienced developer, and especially if you are still held captive by academia, this series is a must-read.

Saturday, October 23, 2004

Joel on Software's Best Software Essays of 2004

I noticed in my referral logs that a previous post of mine about Excellence in Software Development was nominated for the "Joel On Software" best Software Essays of 2004. If you haven't read that previous post yet, please do so. I'm impressed that "schmoe" found that post enlightening enough to nominate. It is certainly not the best work on the subject that could be done, rather reflections that came to me at 1:40 AM that evening/morning. Perhaps this is what blogging is about - a slice of time or history that impacted the blogger in some way that he or she felt important enough to share with others.

The nominator mentioned that my post is similar to the "Personal Character" chapter in Code Complete 2nd Edition. Sadly, I have not completed reading that book (including chapter 33) - it keeps getting pushed aside for other books because I read the 1st edition long ago. I'll have to put the book back to the top of the queue to catch this reference. I'm sure that I'd agree with Steve McConnell's philosophy - I have in the past.

Joel publishing a collection of these essays is a good thing, especially since he is so well respected in the community. Hopefully, he will allow some editing if that post is chosen - without having link context some of the points don't make sense, which is typical of a blog entry. If you don't read Joel, you probably should.

I haven't read many of the other ones nominated. I have read Joel's excellent essay on how Microsoft lost the API war. I disagree with Joel on "the new API is HTML" conclusion, but the points that he makes are well done. Perhaps I will blog a review of some of these other nominated articles after I catch up on some now mandatory reading at the expense of Steve McConnell.

Which Keyboard?

Last weekend, I spilled a little bit of coffee in my keyboard. Hey, accidents happen. I had a Microsoft Natural Keyboard Pro. It is a keyboard where the left and right side were split and a bit raised. I loved this keyboard. The feel was more "natural" than a flat keyboard, and I never had wrist fatigue. The keypresses felt perfect. Post coffee, the keyboard began to spit out more characters than asked to. After drying it out, tearing it apart, and putting it back together, I realized how sensitive these things are to manual reassembly. I also realized how much "flat" keyboards suck in comparison by how my wrist is feeling.

Today, I bought a Microsoft "Wireless Optical Desktop Comfort Edition." at Costco. The price was right at $60. I still like the feel of the keys, but this curved thing is making me crazy. I have never hit "backspace" as much as I have in the last two posts. Plus, after typing a bit, my wrist still feels strange, like using a flat keyboard. The "comfort edition" is curved, but not split or raised in the center. This may be the problem. Also, the function keys are shared shortcut keys. I won't get used to that.

Additionally, this duo has a nice "pleather" wrist-rest that I like. The mouse is plain plastic. I'd think they would both be pleather. I was wrong. The scroll wheel moves easily, but almost too easily. I don't get the feedback I am used to. Perhaps I will get used to it. The mouse "feels" right.

I think I am going to return this one and go with the "Wireless Optical Desktop Pro," which is most similar to what I used to have. Best Buy wanted $100 for it. I can get it cheaper online. I really don't care about wireless for the keyboard, but for the mouse it is useful.

Does anyone have other suggestions? I considered Logitech's similar offering, but I just don't know. Microsoft's hardware has always been top-notch.

Friday, October 22, 2004

Raymond is on a roll... (shared files, versioning and setup)

Fellow blogger Raymond Chen has posted yet another blog of note to setup developers. I'm going to use a different example than he did, plus use it as a springboard for yet another rant. If you remember back to Windows 95, especially when configuring network settings, it would prompt for the CD (bad) and then prompt to if you wanted to overwrite a newer version of a file with an older version (even worse). As an added bonus, it asked this question more than once (the absolute worst).

Why did it do this? Read Raymond's answer. For this specific example, most people installed updates to the original TCP/IP stack. Windows 95 wanted to restore the initial version of all files in all cases. This could leave the user hosed if they didn't answer the questions consistently. Microsoft learned from this example. We should, too.

Lets look at how the MSI engine handles patching. OK, maybe that would be too long of a rant:) The essential thing to take away is that a MSI patch (msp file) will not overwrite a newer version of a file with an older version. In the usual case, this is not a problem, since all identically named DLL's are always backwards compatible (in Utopian software development), right? This is a decision the Windows Installer engine designers had to make up-front. And as long as we are in Utopia, it works.

If you recall, many older InstallShield installations also prompted the user on uninstall if they wanted to remove a DLL that is marked as shared, since no other programs claim they are using it. The more modern versions of InstallShield don't. Why? They expect us (setup developers) to get it right. Make sure you are using the appropriate method of installing files that may be shared by other installations. In the MSI world, Merge Modules accomplish this. But especially for non-MSI installations, make sure you are setting the "shared" flag, so the uninstallation of you application does not impact other installed applications by removing files critical to their operation.

As a side note, Raymond links to an older article related to asking useless questions that is quite humorous, yet further proves this important point: If you ask the user a technical question, odds are they they're just going to stare at it blankly for a while, then try to cancel out of it.

Don't make these mistakes.

Thursday, October 21, 2004

Control Panel Items

Since I'm on a roll with configuring stuff, there is a nice article on MSDN about configuring Control Panel Items. If you are a setup developer, this link will take you to the section that you should read.

Some things to note:

  • "...the DLL file must have a .cpl file name extension" - These control panel items ARE DLL's.
  • on "Windows 2000 and later systems, new Control Panel items should be installed in the associated application's folder." - Yep, no installation to SYSTEM like the old days!
  • The placement of a registry key dictates what shows up in the Control Panel. Remember to respect per-user installations! (Yes! it is a bug, a security one, and a bad one)
  • If you must support the "old" pre-Windows 2000 method, only do so on the pre-2000 OS.
  • Yes. you can extend System Control Panel Items.
  • You can also specify a category for the item in Windows XP or better, for users using the default control panel view.
  • For MSI folks, there is nothing in any of the Standard Actions that will help you.

Larry talks HKCR

If you recall, I recently blogged about the fact that HKEY_CLASSES_ROOT key is not a real registry hive. Larry Osterman from Microsoft recently posted a "What is wrong with this code?" series and covers the HKCR issue extremely well in the answer section. If you develop services, this one is worth a read.

Raymond talks about registering shell extensions

As you can tell, I'm catching up on reading some of my favorite blogs... Raymond Chen from Microsoft wrote a really nice article about registering shell extensions. Setup developers should take note of how Windows deals with what you put in those keys.

What is a shell extension (also known as Verbs)? This is what Windows uses to determine what to do (or what right-click options to provide) when clicking on a file with a specific extension. Read this link to find our more. I'll wait...

In the setup world, I believe when you run a program from a setup most vendors use the ShellExecute() API. If this is the case, when you launch a readme at the end of an installation by simply specifying "readme.txt" the default "open" verb (on a fresh Windows installation for a text file this will be Notepad) will be run. If the method of running an application in your authoring tool uses CreateProcess() , it will likely result in an error if you simply give it "readme.txt". For MSI based installations, many people may use the tutorial method described in the Platform SDK. I leave it as an exercise to the reader to see what that one uses! Any guesses based on the specifications for the tutorial mentioned in this document?

If you read the MSDN documentation for these functions - which are linked to above - you will understand this quote from the article: "Consequently, the shell mistakenly believes that the program name is "C:\Program", which it cannot find." This can be a security issue if a malicious program installs itself as "C:\program.exe". Please make sure you are registering extensions correctly - even if the installation design document you are given dictates otherwise.

Saturday, October 16, 2004

Custom Action Tutorial coming soon to a blog near you...

Since I didn't get any responses for suggestions for a C++ custom action, I took a look at the newsgroups and found this post where the author wants to detect if an application is running and prompt the user, close the application, etc. Developing a custom action to handle this would be a good thing for the community, as it is a fairly common request.

This also meets the bill for covering almost every aspect of a robust custom action - user interaction, running in silent mode, custom table, validation, etc. The one thing it does not implicitly have is a rollback action. I will "fake" a rollback action for demonstration purposes.

I also decided on a multi-part series as the best format for this.

  • Part one - describe the specifications and set up the development environment.
  • Part two - create a "Hello MSI" custom action and insert it into a sample MSI.
  • Part three - debugging the "Hello MSI" custom action.
  • Part four - add tables and other required entries to the MSI.
  • Part five - add code to read the tables.
  • Part six - add code to close the application. I'll likely gloss over this pretty fast, as killing an application is not the true focus here.
  • Part seven - testing the custom action.
  • Part eight - creating documentation for other developers.
  • Part eight - Creating validations for the action and tables.
In the process of developing this, I will loosely cover internationalization concerns, Unicode support, MSI Custom Action fundamentals, and more. I will "try" to follow the format of MSDN documentation in the process.

My intention is to complete the series first and post one new part every couple of days to keep the writing style similar and assure completeness. With my current workload, I'd guess part one would be published in late October.

Does anyone have specific feature requests for this Custom Action? Topics not mentioned here that should be covered?

Monday, October 11, 2004

.NET Deployment

I came across this blog entry that talks about this blog entry. So this blog entry is talking about a blog entry that is talking about a blog entry - whew!

The crux of the story is how an application based on the .NET Framework should be deployed. The post goes into adding the MSM for the framework, etc. The killer quote from the first blog entry by St├ęphane Rodriguez is "What should I, as a developer, think about a development framework which by design involves hacks only for my app to be deployed?"

Amen, sister!

I blogged previously about how difficult it is to determine the .NET Framework and again when Aaron Stebner produced some sample code to check this. St├ęphane hit exactly on what was in the back of my mind: Why is this so hard?

In theory, the .NET runtimes are mostly backwards compatible. The version detection scheme is not future compatible - I have no idea how to detect the .NET 2.0 RTM runtime today as a setup developer. Even if I can detect this, there are no guarantees that "Compatibility and Breaking Changes" haven't affected me. At least if I could detect the version, I could intelligently deal with it. I may alert the user that the framework version installed has not been tested with my application and let the installation continue. Then again, I may choose to have the old framework downloaded and installed. Or really anything.

The problem that exists is (as a commenter to the quoted blog points out) very similar to Java's problems. Microsoft should have learned from this and done a better job. Looks like they haven't - and mostly from a documentation perspective from what I can tell.

Lastly, there is a lack of tools and guidance to setup developers and how they should handle deployment. In an MSI, why can't I simply tell the engine/tools that my application targets one or more versions, and let the engine handle the detection job along with a warning if installed framework version is greater than what is targeted?

Why must stuff that should be so easy be so difficult?

Tuesday, September 28, 2004

Debugging DLL Registration Issues

Did you know that the HKEY_CLASSES_ROOT registry hive (in Windows 2000 and above) is a figment of your imagination?

I was helping a coworker troubleshoot an application that runs as a service that accesses another homebrewed COM component. Of course, it worked on his machine, but not when deployed on a test machine. Running the normal Filemon/Regmon tag team, we traced it down to an issue instantiating the COM component. Regmon reported a registry key in HKEY_CLASSES_ROOT could not be found. Double clicking the line in Regmon (I really love that feature) opens up Regedit to the key that couldn't be found. I immediately guessed the root cause of the problem, and asked the developer the question above: Did you know that the HKEY_CLASSES_ROOT registry hive (in Windows 2000 and above) is a figment of your imagination?

He responded, "Huh? Its right there," as he pointed to the topmost hive in Regedit. A quick pointer to MSDN followed by browsing to HKEY_CURRENT_USER\Software\Classes and HKEY_LOCAL_MACHINE\Software\Classes showed the obvious - the key that Regmon showed as "not found" was really in the HKEY_CURRENT_USER section. As you may know, an application that runs as a service is run under the LocalSystem account (assuming no impersonation is done in the service, a user is not specified in the Service Control Manager, etc.). This account does not share the HKEY_CURRENT_USER hive with the user that installed the software, nor the currently logged in user, and was therefore unable to read the registry key necessary to instantiate the COM component since it did not exist for LocalSystem.

So what caused the problem? In an MSI created by most authoring tools, an option is given to the user to install the application for the "current user" or "all users". This ultimately sets the ALLUSERS property. In addition, the Windows Installer engine will add the registration information to either the HKCU or HKLM sections as defined by the ALLUSERS setting. The only documentation related to this behavior is buried in the SelfReg table, but not in the documentation for the property, tables, or actions that are truly affected by this. Bear in mind that for items in the SelfReg table, the DllRegisterServer() function is called in the context of the current user if ALLUSERS is set to a "this user only" setting, and "elevated" if it is a per-machine installation.

It is tempting to skip the bland and usually useless "User Information" dialog when creating your installations so that your installation doesn't get on people's nerves, but remember that while setting this property is important, you surely do not want to violate rule #20 while trying to appease rule #10.

Oh, and if you are interested in how I actually fixed this issue: since the per-user installation makes no sense for this application, and the "User Information" dialog was never shown, a custom action of type 51 came in handy to set ALLUSERS to 1 at the start of both the UI and InstallExecute sequences. Some ICE type validators may flag the installation if ALLUSERS is set in the property table. If you forcibly set ALLUSERS to 1, make sure that the LaunchCondition table checks that the user is Privileged and that Version9X is not set.

Friday, September 24, 2004

Wix video presentation

Rob Mensching published a video of a presentation he gave back in June regarding the Open Source Wix MSI toolset.

The video is a 64 MB download. My recommendation - do a right-click and SaveAs on the link.

Wednesday, September 22, 2004

Running as Non-administrator

Along with the recent security frenzy, many people have suggested running as a non-administrator on Windows 2000/XP. I attended an MSDN event for developers dealing with ASP.net several months ago, and was surprised that the presenter was running as non-admin. He also gave some great tips on how to make it easier.

First off, you should read Aaron Margosis' blog in its entirety. If you need convinced that you should run as a non-admin, start with this post. Then read all the others. They start back in June. If you already read them, skip the rest of this paragraph, as I will summarize the info you can get there. I like the "easiest way" for non-techie people. Its rather simple, but effective. Pay attention to the "update" mentioned, where he suggests making the admin desktop easily distinguished from the non-admin desktops. For the more tech-savvy, I prefer the shortcut with RunAs setup. I change the color of windows such as explorer, when running them as administrator, and suggest you do the same. Alternatively, PrivBar can be used, but I have not installed or used it myself.

Ok, so it should be about 15-20 minutes later since you read all the above stuff, right? Normal people can go off and read their next blog now after they made the necessary changes to their system - but I have a special message for you developers...

I blogged about pet peeves in installation programs and applications. Refresh your memory regarding items #20, 19, and 18. (Do you see a trend? I already covered #9 in a previous post). Hopefully you are starting to see another connection forming between non-admin users and where installations and applications put and use their files. To reinforce this connection read Larry Osterman's post from today talking about running as non-admin. This stuff is applicable to us.

Make sure your testing staff is running as non-admin to catch these issues. Not mentioned in the article is that you should make sure you are using XP Professional with NTFS disks so you can catch the strange permissions problems that can add.

Other test paths include installing the application as administrator for "all users" and logging in as a different, unprivileged user and make sure everything operates correctly. MSI Developers - don't ignore the ICE warning about mixing per-user and per-machine entries in a single component. Lastly, make sure the settings in your application are user profile aware. I recall trying to figure out how to make a media player work on my Uncle's PC with different profiles. For good reason, my uncle didn't want to share a playlist with his son - but that wasn't possible. Bye-bye old software vendor, hello new software vendor...

If you are a developer that doesn't work for Microsoft, you can go onto the next blog now, unless you are a real glutton for punishment:)

At my company, the IT department is pretty lenient to the developers. They give us admin privs to our own machines. The problem is I don't want admin privileges all the time, just when I'm doing stuff that requires it. One solution is to give me a local account admin account to use when I need to admin my box, and only normal rights at the domain level. The problem with this approach is I can't access network shares using my domain account when I am running as local admin for tasks like installing software. Having two domain accounts is a nightmare. I don't know what the solution is - perhaps a flag to make the account have normal user privs, and when administrative privileges are required have the security subsystem prompt me for explicit permission to elevate that process. I guess I'd call it the "Admin Masquerade" group for lack of a better term.

Tuesday, September 21, 2004

GDI Plus Vunerability Information for Setup Developers

Stefan Krueger from InstallSite.org has posted a very well-written article about the GDI+ vunerability and how setup developers should respond to it. Please read it ASAP, it is a great consolidation of all the available information, and should be acted upon quickly.

If you haven't subscribed to Stefan's RSS feed, here is the link. If you don't know what RSS is, try this link.

Monday, September 20, 2004

Making Progress Bars behave nicely

After my previous blog entry about installation pet-peeves, including how writing progress bar code is hard, I figured I would try to help a bit by providing a simple sample for MSI authors. At the same time, a question related to progress bars appears on the newsgroups. Coincidence?

If any of you have some stylesheet stuff I can add to make posting/reading code snippets on a blog less painful for both you and me, please send it in.

The usage in a Custom Action is pretty simple: you parse CustomActionData if you are running deferred and determine how many things you have to do. Then call setupProgress("MyCA","Transmorgifying files",10); if there are 10 things to do. When performing each of the 10 things, shoot off a call to tickProgress("this filename"); and the bar should behave nicely. It's pretty easy to convert to the Custom Action language of your choice. Most of the information needed to create this sample is in the Session.Message() documentation.

var msiMessageTypeProgress = 0x0A000000;
var msiMessageTypeActionStart = 0x08000000;
var msiMessageTypeActionData = 0x09000000;

function setupProgress(ActionName, ActionDescription, numTicksExpected)
//set the UI Status ACTIONSTART
var statusRecordObj = Installer.CreateRecord(3);
statusRecordObj.StringData(1) = ActionName;
statusRecordObj.StringData(2) = ActionDescription;
//below line defines a template for display on each message tick
statusRecordObj.StringData(3) = "[1]";
Session.Message(msiMessageTypeActionStart, statusRecordObj);

//resets the progress bar.
var progressRecordObj = Installer.CreateRecord(4);
progressRecordObj.IntegerData(1) = 0; //0 = reset bar
progressRecordObj.IntegerData(2) = numTicksExpected+1;
progressRecordObj.IntegerData(3) = 0; //0 = left-to-right, 1= R-to-L
progressRecordObj.IntegerData(4) = 0; //0 = calc time remaining
Session.Message(msiMessageTypeProgress, progressRecordObj);

//tell the progress bar to increment for each ActionData message
progressRecordObj.IntegerData(1) = 1; //Action Info
progressRecordObj.IntegerData(2) = 1; //numTicks each ActionData
progressRecordObj.IntegerData(3) = 1; //increment bar for each ActionData
Session.Message(msiMessageTypeProgress, progressRecordObj);

function tickProgress(message)
//The ActionData record follows the template defined in the msiMessageTypeActionStart
//item #3. Refer to the MSI documentation related to templates. If you add more fields
//to the template, make sure to add the additional fields here as well.
var progressObject = Installer.CreateRecord(1);
progressObject.StringData(1) = message;
Session.Message(msiMessageTypeActionData, progressObject);

Sunday, September 19, 2004

Top 20 Installation Pet Peeves and How to Avoid Making Them

This Top-20 list of pet-peeves/annoyances is presented in no specific order and pertains to all installations, however, some items and examples directly relate to MSI based installations.

Am I missing your top pet-peeves? Are you guilty of any of the below and care to admit it? Please leave feedback in the comments!

20. Security and profile unaware installations. Can the application be installed or used by a non-administrator? Use the principle of least-privilege to ascertain what the real limitations are and make installations compliant with it. Respect user profile settings. Is the installation storing sensitive information unencrypted (such as passwords) in your log files, registry/INI files, or MSI files?

19. Installing to a location other than "Program Files" by default. This also applies to installations that assume "Program Files" is located on the C:\ drive. I recommend testing installations and applications on systems that have Windows installed to a drive other than the default C: drive with a Windows directory name other than Windows or WINNT to catch these assumptions.

18. Non-application file storing directories sharing the application directory. No application should store configuration information or user data (by default) to a directory other than the current user's documents and settings directory. Similarly, if the installation is creating a shared folder, virtual directory, or a directory that will have security implications/working data in it (such as file-sharing or database), the user should be able to select this directory separately (and it should be defaulted to a non-Program Files directory). The rationale for this is simple: Applications are restorable by reinstalling the software. User data isn't. Make it easy for the user to back up these items by default.

17. Not providing a merge module if this software will be redistributed by other vendors. I absolutely despise running an executable from my installation to install a 3rd party component. Not only do I have no idea if the sub-installation succeeded or failed, but automatic reference counting is impossible. If I repackage it, other vendors could do the same and we have out of whack component codes and keyfiles causing endless automatic repair cycles, maintenance, and removal nightmares. This also adds an easy way to assure a common method of distributing patches if the third-party software is found to have a security vulnerability.

16. Full screen installations are evil. Why does the entire screen need to be used for an install whose UI is 640x480 or less? Can't I do anything else while the install is in progress? Conversely, if the install requires a screen resolution greater than 800 x 600, my parents can't install it (or worse yet your help desk will be overwhelmed by my parents and their friends).

15. Product Serial Numbers are usually a pain. If the installation is using the Microsoft style dash separated product keys, don't also suffer from same problem that prevents a simple one step cut and paste from a registration email into the install UI. Also, try to prevent the use of commonly confused digits in the registration code, as it is font and eye-strength dependent when trying to figure out if the code is a capital O or a 0, a lower case l or a 1. If millions of codes are not needed, consider axing the U and V as well.

14. Multiple level start menu shortcuts. If a folder in the start menu only has one option in it, please do not put it into a folder. Also, make sure the folder name makes sense to the majority of end users. If I'm looking for a program "Ink Blot" am I going to think to look in the folder "Joe Schmo Software" first?

13. Unintelligent replacement of system or other redistributable files. Are you using a merge module for redistributables if one exists? Does the install check file versions/dates before replacing them? Is reference counting (shared dlls for legacy installations) being set properly? Are all required redistributables included?

12. Unnecessary Reboots. The converse is also true, where reboots are required but not asked for. Also in this category is detection of running software that makes a reboot necessary prior to running the guts of the install. Please let the user know if they can do something that will prevent a reboot at the end. If it is an older version of the installed application that is running that will require the reboot, do what is required to prevent the reboot (as long as you tell the user first). See Pet-Peeve #3.

11. Upgrades that require an uninstallation of the old version first. If this is truly required, is it being handled automatically?

10. Asking useless questions. If the installation behaves identically regardless of the install type (minimum/typical/custom) why is that dialog there? If there is a difference, is the text on that dialog accurate and/or does it make sense in the context of your application? Also in this category are error messages such as "Unable to do something important. Would you like to continue?" when "something important" is essential to the functionality of the application. This can also extend to the age-old useless "shared file no longer in use, should I delete" question.

9. Status bars that are not accurate (or present). I realize that writing status bar code is incredibly difficult. If we are talking InstallScript, VBScript, or JScript installations/actions threading is impossible, so the author can't give a moving status bar when performing a synchronous operation (like creating a database). If an alternate method is not possible, another strategy is to put a message up saying "Busy doing [something long] which may take up to 10 minutes, started at xx:xx". If you are using a C++ custom action that can be threaded to perform the synchronous action, determine the maximum time on the slowest PC meeting your system requirements and provide a time-based incrementing progress bar.

8. Allowing free-text user inputs when choices can be given. As an example, let's say the installation upgrades a database. Presenting a free-text input box for the old database name is a recipe for disaster. Instead, the install can perform a SQL query of the database server to get a list of databases. If the upgrade is applicable only to a specific type or version of database, it can then query each existing database to determine which ones the upgrade can be performed on and populate a selection list. In silent mode, any UI level checks will not be run, so validation should be performed on any command line parameters as well. See pet-peeve #1 and #6.

7. Requests for source media. If I am on my laptop (and more people are each day), and especially if I am out of the office (or a remote employee) it is practically impossible to get the source media at a moment's notice. This is especially true for installations of patches where a security vulnerability is in the wild and users need to patch immediately. Make sure nothing is being done that would prompt for source media if possible, and try to assure that the MSI and support files is going to be available by caching locally by default. Office 2003 made significant headway in this department.

6. Non-working silent installations. Make sure silent installations work properly. This includes checking properties set on the command line as you would user input in the UI, and failing with some meaningful error posted to a log if required parameters are bad or not present. Are all user interface elements able to be passed in via the command line? Failure to provide a silent installation means IT departments will grumble heavily or look for an alternative if they need to deploy your application.

5. Repair/Maintenance/Uninstallation/Rollback doesn't work. If the install is not going to provide anything useful for maintenance mode, get rid of the option. Make sure all custom setup properties are cached somewhere and pulled back in for the repair operation to succeed. Does auto-repair work correctly (delete the install directory, run from the shortcut, and see if it works)? Does an uninstallation remove everything, including cached copies of the installation? Does an error near or at the end of the installation properly rollback the changes (Throw in a deferred custom action that simply fails prior to InstallFinalize)? If altering a DB, is it being backed up before running the portions of the installation that alter it so the previous version can be restored in a failure case?

4. Assumptions that are not checked. If an installation adds a database to SQL Server 2000, is the system checked for the presence of a running SQL server before it allows the user to start the installation? Think about a system that has SQL Server installed, but the service set to disabled or is currently stopped. Similar comparisons can be made to installations that rely on web servers, messenger service, BITS, etc. If the service is not started or set to disabled, is the installation violating pet-peeve #3?

3. Modifying a system globally without asking or telling the user. This is a huge category with security implications. If the installation adds a network share or internet accessible directory, is the user being told this up-front? Take as an example the .NET framework installation. Is the user asked/told if I want ASP.NET installed to the IIS server? The answer is no. A mention of this or other types of global system changes in the documentation alone (especially since most documentation is installed with the product) is unacceptable. Note that most modern media player installations ask the user if he or she wants to associate file types with it. Installations were not always this nice to your system.

2. Not checking return codes. When running a command that is supposed to do something essential for a successful installation, does the installation check the return code and handle the error? A great example of this problem is found in many database installations, where the result of a SQL script is not checked. The application does not work after a reported "successful" installation, and it takes a tremendous amount of time and effort to determine why.

1. Not validating user input. The install allows the user to free-text something in where choices can not be predetermined (see pet-peeve #8); let's say a timeout value or something that is essential for the installation to succeed or the application to function. The user types in "10h" by accident. The install doesn't verify the input is a number or a number within a valid range. Installation fails (bad) or says it is successful, yet the application does not work (infinitely worse).

Aside from this list, a very useful document for installation (and application development)authors to follow is the "Designed for Microsoft Windows XP" Application Specifications. Many of the items listed there are also listed above.

Detecting .NET Framework Version (Update)

Aaron Stebner from Microsoft comes through with some source code (in C++) to determine the installed version of the .NET framework, including service packs. Great job, Aaron!

Too good to pass up

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..."

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");
//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");
oRecord = Installer.CreateRecord(13);

//TODO: continue for the rest of the columns in this table. See
// the ServiceInstall table docs for more information

//msiViewModifyInsertTemporary the record.

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!

Monday, August 23, 2004

COM+ in an MSI - Of Trials and Tribulations

I recently answered a question on installing COM+ applications in an MSI on one of the newsgroups. I promised that I would go into a bit more depth on the subject here, specifically on the different methods of installation I have tried and why they didn't work (and of course, what did work).

I had an application that consisted of about 200 COM+ components. These components were to be installed into three different directories, essentially separated by their tier (DB, Business, Common). The naming convention was related to the purpose of the DLL. As an example the Business component was "CurrentCustomerListBus.dll" and the DB component was "CurrentCustomerListDB.dll." The build output mimicked the actual desired installation directory structure. Several of these DLL's required configuration by means of adjusting the activation string and timeouts. These settings were different for each installation.

The first approach tried is the Microsoft suggested approach of exporting the COM+ application as an MSI. This pretty much populates an MSI with the files and TypeLibraries, plus adds a blob of undocumented stuff to the ExpType column of the Complus table of the MSI. When this is done, the output was less than desirable:

  1. MSI Validation fails due to short file name (SFN) clashes. Not really a big deal, but worth mentioning. This is due to point 2 below.
  2. The destination directory is a single directory. When exporting the application, there were no SFN clashes since they were in separate directories. Note the similarity of the first several filenames above. When the package is deployed they are going to the same directory thanks to the export process, so these clashes now exist. Although the single directory was not according to specs, it was a livable limitation.
  3. The installation specific activation strings could not be configured in the MSI. A Custom Action would still need to be written to programmatically change them after installation in the host MSI. Again, this is a problem we could live with.
  4. Deployment via a nested installation, meaning this exported COM+ MSI would need to be installed as part of another MSI. Nested applications are extremely difficult (if not impossible) to patch (exact words from the above link are "Patching and upgrading may not work with nested installations"). Additionally, to properly assure the application (which in my case was a Web application) would not be accessed until the application was fully installed, the nested approach would not be ideal. This one was a deal-breaker, but as it turned out, not the only one.
  5. Type Library clashes. Although this is not a problem with the export, it does indicate a problem with the construction (programming) of the COM+ application. While the application still functioned OK with these issues, redeployment through the exported MSI was just not possible. Plus, this was not discovered until much later - so I did not know the exported MSI really didn't work.

Approach #2 built upon the first approach. We were using Wise for Windows Installer 3.x which did not have COM+ deployment, so this limited other options. I took the exported MSI and turned it into a merge module using the Windows Installer API's and some tricky scripting. This approach solved problems 1 and 4 above. That is when we found out about problem #5 above, which completely killed this option.

Approach #3 was upgrading to Wise for Windows Installer 4.x Pro (Pro edition allowed for COM+ deployment plus build scripting). This approach failed due to all but reason #4 above, which was the only thing the Wise method solved. To blatantly rip-off Wise's new saying that they stole from my former coworkers (yes, you know who you are) "...but it worked on my machine." Yeah, right!

Approach #4 was the final, shipping method that I found to be 100% reliable, extensible, and portable. We started using Wise and moved to InstallShield with very little work (and none related to COM+) because of this method. For the tool migration benefits alone, this is why I prefer not to use vendor or tool-specific MSI extensions (InstallScript especially). But that is a subject for another future blog entry...

One last bit about approach #4 before I give away my secrets. I implemented it not exactly in the manner I am suggesting here. The main reason I didn't is to hit the testing deadlines and be sure what I released would work in the field. My approach is not perfect, and is difficult to move to another install. In other words, it is not a well-written series of CustomActions and tables that are portable enough to move to another installation without some modification. After this release, time became an issue so I couldn't reimplement it in a more extensible and portable manner. Please don't make the same mistake with your implementation.

To use approach #4, you should be familiar with interacting with Windows Installer via custom actions (VB Script if you want to use the many examples floating around - I prefer JScript). Secondly, you should not be afraid of programmatic access to COM+.

  • Create a feature and mark as hidden from the UI. Assure that this feature is always installed via using a Level or a custom action that sets it to be installed based on whatever conditions you require. Note that this feature can not be advertised.
  • Add your COM+ component dlls, and only your COM+ component dlls to this feature. Assure that you do not have them self-register or extract the COM information at install build time.
  • Use the COM+ APIs to construct some CustomAction(s):

    • At uninstall, call ShutdownApplication() on the COM+ Application and remove the COM+ application before the files are being removed. If your COM+ components are used by a service (homebrew, IIS, etc.), make sure you stop the means of access to the COM+ application. I generally place this action after the ServiceControl action in the standard sequence. This will prevent in-use problems at uninstall time.
    • During repair/reinstall/maintenance, you want to restrict access to creating the COM+ application and call ShutdownApplication() on it prior to any file actions.
    • During installation (and I do it as part of repair), delete and recreate the COM+ application and install each component. Essentially, you need to create a COMAdminCatalog, and on it call GetCollection("Applications"). You are returned a COMAdminCatalogCollection object consisting of Applications. Populate it by calling Populate() on it. Iterate the collection, remove/edit/create as necessary a new COMAdminCatalogObject to represent your COM+ application. Set the Name, Description, and ID values appropriately (refer to this) and then call SaveChanges() on the collection. This gets you the configured COM+ Application. To add components to it, call InstallComponent() for each COM+ dll. I make sure to create progress bar information as well so it does not appear to make the installation hang. I would use the information in the FeatureComponents, File, and Component tables to create a query that will ultimately allow you to get the path to each COM+ dll included in the hidden feature to build this list, then pass it as CustomActionData to the deferred action that actually does the work described above. Most of the COM+ stuff can be seen in action at the link I gave earlier. The MSI interaction stuff I expect you to should already know or be able to figure out from the inline links above - if you have specific questions, just ask!
    • Optional - configure component properties as required, such as the activation string, timeouts, etc. on a per component basis. I chose to hard-code this instead of creating a new table in the MSI to store these customizations, but in the future I would use a custom MSI table based approach.

That's all there was to it. It actually took less time to complete approach #4 than approach #2. I am not looking at the source code of my implementation to be sure I'm not missing anything, but this should be complete enough for you to use as a starting point at the very least.

Friday, August 20, 2004

The Case of the Disappearing DLL

Although this is not quite the Sherlock Holmes and Dr. Watson type of mystery, this bug was an interesting one to troubleshoot. After reading the bug report, I suspected what the problem was, but could not reproduce it on purpose in the lab without some hassle.

The bug report stated that some of the DLL's required by our product were deleting themselves. After installation, everything was fine. Sometime weeks later, a DLL or two mysteriously vanished.

The culprit? On an uninstall of the old version of the software, a DLL was in use, and its removal slated for post reboot. After the uninstall ran, the user promptly ignores the reboot request and installs the new version of the product. The dll is no longer in use at this point, so the installation succeeds and no reboot is prompted for. The next time the user rebooted - blammo - bye, bye DLLs!

I'm not going to give away my solution for this one (other than it involved fixing the uninstall process going forward), but one possible way to prevent this is to disallow installations if there are pending file removals or replacements. You can check the registry at HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations to see if there is some activity scheduled for after reboot. There is a tool that dumps this information and another one that captures that plus some other post-reboot activities. One of these tools should be part of every setup developer's toolbox.

Thursday, August 19, 2004

Why can't I use WScript in my MSI Custom action?

This is a fairly frequent question, and one that deserves a decent answer. VBScript and JScript custom actions run inside of the Windows Installer engine - in technical terms, the MSI engine is the scripting host for all scripts running as a custom action.

The scripting host program can make available objects to the scripts if it so desires. I've written applications that host the scripting engine, so this is familiar to me. In the case of MSI custom actions, the Session object is an example of this. When you run a script from the command line or by double-clicking it, it is hosted by the "Windows Scripting Host" (WSH), and it kindly supplies the WScript object. The main reason these objects are provided is so the script can interact with the host environment. In the case of WSH, you usually need to interact with the user since it is being run from Explorer or the console, hence the "Echo" method. In the MSI world, you are encouraged to interact with the MSI engine and let the MSI engine interact with the user based on the appropriate UILevel settings - this is all handled for you if you use the Session.Message API's to interact with the user or to write log files.

To summarize, if you have a script that contains WScript.something, it will not work in an MSI, as the WScript object does not exist and you cannot create it.

An unrelated issue (but people think it is related...) is the "WScript.Shell" object. You can create the WScript.Shell object (in JScript foo = new ActiveXObject("WScript.Shell")) and use it. In this case, the full name of the object you are creating is merely WScript.Shell by coincidence, and you are not calling a method on the WScript object, as you are in a line similar to WScript.Echo("foo"). An interesting related aside here is an article detailing the differences between CreateObject and WScript.CreateObject.

Danger, Will Robinson! Some antivirus programs and other script blockers will either silently block or loudly proclaim attempts to instantiate the WScript.Shell and/or the Scripting.FileSystemObject. These are commonly used by malware since they can access the registry and filesystem. Write a C++ dll custom action if you need to interact with the system in this way.

A fairly common question posted to the Windows Installer boards is "How do I sleep?" or otherwise delay the script. While I don't have an answer you want to hear, there is an excellent article about the subject here. BTW, the answer you don't want to hear is to write a DLL custom action that merely sleeps, add it to the CustomAction table, and in your CA script call Session.DoAction()

Monday, August 16, 2004

Averatec C3500 Review - Part I

[UPDATE 2/26/05: Since this is my most popular article, I have updated the driver download versions and links, corrected a previous mistake in the CPU speed of the unit, and made several updates to points where Averatec has now provided downloads for items not previously available. Look for these UPDATE tags throughout to see the changes.]

This review pertains to the Averatec C3500 Convertible Tablet PC model as sold at Costco. This Tablet PC has generated a bit of buzz because of its price point and its features. The Costco model differs from the specifications as listed on the C3500's homepage in three ways:

  • 80 GB Hard Drive (Instead of 60 GB)
  • Carry Case
  • $1279.99 Price (no rebate)

This review (Part 1 of at least 2) focuses solely on the hardware and software that can be discovered in one night of ownership. Issues such as heat, usability, battery life, and weight are not something that is easily determined or expressed after a few hours of use, so they will be covered in part II.

To put the review in perspective, I made this purchase on behalf of a student straight out of high-school going to college. Thus, the typical use would be for writing papers, with the bonus feature of note taking in the classroom. After showing a Tablet PC to this student, the features offered by the form factor and the OneNote application made it stand out from the crowd of traditional notebooks. However, my background is that of a software developer and general PC hardware enthusiast. Thus, the first two parts of the review will be mine and highly technical. The last part (assuming the student is willing) will be written from the typical non heavy-duty computer user college student.

Taking the unit out of the box, I noticed the case first, since the early message board reviews have not mentioned it much. It is a medium thickness, medium hard plastic, that is essentially a glove fit of the tablet. There are no pockets, and no storage space for anything but the tablet itself. It is also a purplish blue color. As this is not mentioned in the Averatec literature, it may be Costco model specific.

There are several images of the unit online, so I won't waste your time with putting more up here. I can confirm that there is a S-Video connector and not a composite video out that was pictured in some of the prototype models.

Plugging it in and turning it on began the typical Windows XP "finalize the setup" type stuff. I did specify an administrator password, which has a potential issue after rebooting the unit there is a logon box with the administrator name and a password error dialog that you must click OK to. Sometimes, this dialog is hidden behind the login screen, and selecting the user to login appears to hang. Use the ALT+TAB key combination to pop that window to the front, click the OK button, and continue the login process. Averatec does have an update on their webpage called "LogonFix" to correct this issue.

After booting for the first time, BEFORE going online/connecting to a network, I enabled the XP Firewall. If you do not know how to do this, I suggest Googling for it. I then grabbed Windows XP SP2 and installed it.

Once Windows was updated to SP2, I made a quick trip to Windows Update to see if there was anything else new. Turns out there are new drivers for the MSI 802.11g wireless card (released June 10, even though it is a Ralink card, see below) and the onboard SiS 900PCI network adapter (released July 11). Looking at the Windows Update history, looks like the person making the image for this tablet likely installed most if not all updates from Windows Update as of June 25th. I should have checked the installed updates previous to this point to have been sure. I installed these driver updates before checking the initial versions.

Hardware and Drivers
I always assure the latest drivers are installed before evaluating anything, so I checked with the hardware component manufacturers web sites to be sure. I hope this hardware and driver version list answers some questions related to the Averatec hardware components I have seen asked and not answered. Links are provided for the listed hardware for the geeks reading this (* means after the updates described above). [Update: Check Averatec's C3500 download site for potential updates to the below list.]

  • SiS Northbridge chipset M741
  • SiS Southbridge Chipset 963/963L (multiple chipsets listed at this link, make sure you are at the correct section)
  • SiS Video - Mirage - Driver version 3.60a [UPDATE: 3.65 is available from SiS dated 1/11/2005 - also, a version (which might be older than the SiS one previously mentioned) is on the Averatecs web site and purports to fix some issues. If updating to SiS's latest version doesn't do the job, try the version at Averatec's download site.] The on board video uses 32 MB of the total system RAM.
  • SiS AGP version (SiS's 1.17e), 7/18/2003 [UPDATE: 1.19a is available from SiS dated 1/25/2005]
  • SiS 5513 IDE The Microsoft SiS IDE driver comes installed [UPDATE: (v2.04a is available from SiS, dated 11/18/2003) CAUTION: Be prepared to roll back the driver, as an earlier SiS version prevented a successful resume from hibernate mode.]
  • *SiS 900 Fast Ethernet Controller v1.16.0.8, 7/11/2003 [UPDATE: v1.18 is available from Sis, dated 12/6/2004) ]
  • UC-Logic Serial Pen Tablet to HID Driver v5.1.3.2, 5/4/2004. This means the digitizer is likely this model. The pen is likely similar to this one. The drivers on UC-Logic's website appears to be for their tablets, not for these modules. [Update: Check Averatec's C3500 download site for an update to v. which may fix the disappearing floating input panel bug. One poster also claimed that a fix for this was posted on Windows Update.]
  • LCD Screen - 12.1" LCD that is identified as a Generic type ("Default Monitor"). The max resolution is 1024 X 768. I remember seeing a post where someone states they could take it to 1280 X 1024, but the only thing that does is make the screen scroll.
  • Hard Drive - Hitachi TravelStar 80GN (ATA-6, 4200 RPM, 12.0MS, 8MB Cache)
  • DVD/CD-RW Slimtype COMBO SOSC-2483K, likely manufactured by Lite-On
  • Alps Pointing device (12/5/2003, v5.5.1.2) [UPDATE: Version 6.0.305.5 is available from Toshiba.] Even though the driver is provided by another vendor, the control panel is much improved.
  • Modem - Motorola SM56 Data/Fax modem (v6.07.06, 4/27/2004) [UPDATE: v6.08.01 is available from Motorola's website.]
  • *Wireless - Ralink Technology 11g-RT2500 MiniPCI (6/10/2004 v [UPDATE: v3.0.1.0 (1/20/2005) is available from Ralink's website]
  • PCMCIA is identified as a generic cardbus controller
  • Processor - Mobile AMD Athlon XP-M 2200+, runs at speeds between 400 Mhz to 1.667 GHz depending on AC vs. battery and system load. [UPDATE: Thanks to the anonymous poster for correcting my mistake here.]
  • Sound - Realtek 650 chipset AC97 Audio (v5.10.0.5580 5/14/2004) [UPDATE: Version A3.69 dated 2/1/2005 is available at Realtek's website]
  • USB - SiS 7001 PCI to USB Controller using Microsoft drivers
  • RAM - 512 MB physical, 480 MB available to Windows (remember that 32 MB is set aside for the video RAM)
  • built-in microphone which is located towards the base of the screen below the pen holder. This feature is not mentioned in the Averatec specifications.

Sadly, none of these drivers are posted on Averatec's website, nor provided on a disc with the Tablet. Although most of the drivers are available from the manufacturer of the component, some of them (ALPS touchpad and UCLogic's screen/pen) are not available from the component manufacturer. This makes restoration or a "from scratch" installation of Windows XP impossible. This same issue exists with the included applications, which I will cover later. [Update: Check Averatec's C3500 download site - the digitizer and touchpad drivers are now available there - Kudos to Avertec for posting most of them. See above for an update to the touchpad driver]

As for included software, the Averatec has a basic configuration. It does not include the Office suite - just the OneNote application which is a part of the Office 2003 product suite. A complete listing of software is below:

  • OneNote 2003 (w/o SP1)
  • 1stMenuApplet (by FIC) 3.0 Alpha8 (Can't find this application or updates anywhere) [UPDATE: Check Averatec's C3500 download site - this is possibly what is in the Tablet Buttons download]
  • Roxio Easy CD/DVD Creator 6 Basic [UPDATE: Check Roxio's site for newer patches]
  • Adobe Acrobat Reader 6.0.0 [UPDATE: Please update this application, as 6.0.0 has some security issues. The latest version is 7.0. I highly recommend grabbing the PDF SpeedUp program after updating to reduce load times.]
  • Cyberlink Power DVD 5.0 (Update available to build 1203 7/26/2004)
  • Norton Antivirus 2004 (6 month trial edition)
  • Windows XP SP1 Tablet PC Edition
I upgraded the Antivirus software prior to applying XP SP2. This update included Symantec's WMI fix, which is supposed to make Norton Antivirus integrate with the security center feature of the latest Microsoft Operating System upgrade. Sadly, this did not seem to work correctly. I would advise people to install SP2 then run LiveUpdate to upgrade Norton. This may make the two play nicer together. I would be careful on sending this to college with the 6 month Antivirus subscription. Look at some sale flyers to find a deal on an antivirus program - usually you can find Norton or similar in free-after-rebate deals. Alternatively, you can grab a free copy of AVG or Avast. [UPDATE: I also highly recommend an antispyware application, such as Microsoft's free solution. To prevent spyware in the first place (although as the popularity grows, the spyware will follow) try Firefox as your new default browser.]

It goes without saying you should update Windows to SP2 ASAP. This upgrades your Tablet OS to the "Lonestar" or 2005 version, in addition to the security and reliability enhancements in Windows XP SP2. At the time of this posting, this should be available to all users via Windows Update.

Likewise, Microsoft has made tons of enhancements to OneNote. Going to the Office Update site should allow you to easily update this to the latest version. However, Averatec left us with a problem - early models of this Tablet do not include the CAB files this update requires, nor is there a stand alone installation CD. Since I am a MSDN subscriber, and have access to this application, I was able to update it. However, those of you that do not have the same luck as myself will need to try downloading the full file version of the service pack (larger of the two downloads at the bottom of the page). Please leave some feedback as to if this worked for you.

The 1stMenuApplet program is what is used to control the hardware buttons. Unfortunately, it resides in the startup folder. Some people like to remove applications from there, but don't remove this one if you want the HW buttons to work! Also, don't uninstall it by accident, as this is not provided separately. This application is completely unusable due to its horrendous UI, and the readme found in the program directory indicates it is written by FIC and is an alpha version. I'm hoping that a newer and more usable version of this software is provided at a later time. [UPDATE: Check Averatec's C3500 download site - this is possibly what is in the Tablet Buttons download, although it states it is the initial shipping version, I am not 100% certain that this is the case.]

Lastly, the BIOS (Yes, I'm calling it software) is made by Insyde. This is the most plain-looking BIOS I have ever seen. Virtually no settings can be altered aside from boot device order. Some people have speculated the video is capable of 64 MB shared memory, and the chipset specs appear to confirm this. However, the BIOS does not allow you to configure this amount. Perhaps future revisions of the BIOS will allow this.

I'm not a fan of benchmarks, but they can be a rough way to make comparisons. The methodology is simple: Two runs of the benchmark are done. One run with the AC power plugged in, one with the battery power. The software used is the PCMark 2004 Free edition. Drivers, applications, and OS are updated to the latest version as described above.
  • AC power: 2013 PCMarks
  • Battery: 2019 PCMarks
General Construction and layout
I found the unit to have a solid feel. The hinge also feels solid. The keyboard feels like most notebook keyboards, and I was able to type on it w/o missing keystrokes (My hand would be classified as "large" bordering on "extra-large" if you go by the manufacturer of most types of gloves).

There is nothing horrible about the layout, like most notebooks, the special keys are fairly inconsistent and reside in different places.

Wireless Networking
The wireless networking component can be turned on or off by using a switch. I believe this is to reduce the battery consumption of the receiver continuously scanning for an Access Point. One thing I noticed is even when the C3500 is near the AP (Excellent signal strength) Windows continuously pops up the "...is now connected" message. I am not sure if this is because the connection is constantly being broken and re-established or because Windows likes to annoy us with useless messages (ala Clippy). I will run a socket test application I wrote eons ago to determine what the problem is (if any) and report back in part II.

Overall initial impressions
I am quite disappointed that Averatec has not made the drivers and applications that are part of the restore image available as stand-alone install executables. This limits the end user's ability to repair themselves in the situation where inadvertent circumstances end up destroying the installed drivers or applications. Thus, the only way to recover from this is a fresh recovery image of the tablet with the user losing installed applications and data. [UPDATE: These are now available on Averatec's C3500 download site]

Secondly, although this is related to the first point, the Windows CAB files and OneNote cab files (installation files for reinstallation or patching) are extremely important, yet they are not provided. Averatec would do well by providing CD's containing these missing bits to the customers who bought early models without these installed. If the student were a web designer, IIS (the web server that ships with the OS) could not be installed with the CDs and computer as provided by Averatec.

On the plus side, the Averatec customer support rep I spoke with appeared to speak English as his primary language (contrary to some other posts I have read), and was able to answer the OneNote update question regarding the missing CAB files after a short delay. He did not point me to the full file download, although I am not sure if this will work or is an acceptable workaround (please let me know if this is the case). 24 hour tech support is provided.

In Part II of this review (expect it inside a week), I will cover more specifics of the performance of the hardware. Factors such as heat, boot time, battery life, included microphone performance, wireless networking distance and reliability, digitizer/pen sensitivity, and weight will be addressed. If there are any further questions as to the hardware or software not addressed in this article, please leave a comment as to what you want addressed and I will update Part I and add it to the Part II review. As a side question, what software should I add to this in addition to Microsoft Office student edition that would be useful for a college student?