Showing posts with label Windows Internals. Show all posts
Showing posts with label Windows Internals. Show all posts

Sunday, July 31, 2005

Trouble ahead for time computations?

I was just reading an article about the new U.S. Energy Bill that intends to extend Daylight Savings Time an additional month (starting in 2007). I wonder what the impact of this is on the Computer Science front. There has not been any chatter on my normally read blogs, but perhaps there will be soon. I have no facts to back up my theory here, but lets play along with my conspiracy theory and possibly learn something about static and dynamic linking of DLLs.

Think about this from the Microsoft C/C++ developer perspective: the date functions are implemented in the C standard library, MSVCRT.DLL (or its post VC6 brethren MSVC*.dll). Lets assume (although I am guessing) that the "rules" for the current daylight savings time are implemented in this same DLL (NOTE: Please see comments). One of these functions is called mktime(). Let me quote from MSDN:

When specifying a tm structure time, set the tm_isdst field to 0 to indicate that standard time is in effect, or to a value greater than 0 to indicate that daylight savings time is in effect, or to a value less than zero to have the C run-time library code compute whether standard time or daylight savings time is in effect. (The C run-time library assumes the United States's rules for implementing the calculation of Daylight Saving Time).
So if we developers assumed that the runtime would take care of conversions for us, and we statically linked to the library, we may have to recompile against a newer link library (with the revised code) for our applications to continue working if this change actually happens. On the other hand, had the developer dynamically linked to this DLL and not copied the pre-dst-updated MSVCRT.DLL to the application folder or application current directory (Remember that in Windows Server 2003 and possibly Windows XP the rules have changed), the DLL can be updated once in the system32 directory (perhaps through Windows Update) and our preexisting applications will be none the wiser and work just fine with the change.

This is more food for thought on the debate of static vs. dynamic linking that could help sway some developers into a different line of thought - I prefer dynamic linking, but in some cases for some applications I do not practice what I preach. For the installation folks, please read the previous link (and the comments - good stuff there on merge modules) - this is why you need to be sure that you are following proper version replacement and marking shared DLL's as shared - otherwise you can cause grief for other applications or your own.

[UPDATE: 10/4/2005 - fix formatting and errant character]

Saturday, March 12, 2005

Checking or Changing System Settings

Raymond Chen reminded me of a story I wanted to share. In non-MSI setups (and I am only using setups as an example of a larger problem in application programming), installing a service was a bit strange and usually flaky. Some installation engines would simply write the service registry keys directly into the "CurrentControlSet" section of the registry and require a reboot for the service to work. The reason was the Service Control Manager only looks at that portion of the registry when the machine is starting. Some engines decided to write values to the HKLM\System\ControlSet001 key - an even worse idea. Recall that "CurrentControlSet" does not really exist, it is mapped to the "real" ControlSet001 or ControlSet002 based on how the machine was booted. This is sort of like the HKEY_CLASSES_ROOT virtual key I blogged about before. If you need to query information about services, install them, etc. use the API's intended for this purpose - not the registry keys. Not only is this good advice for services, but in general. If you are using a registry key to check or change a setting, check for an API to set/check for the setting first. This generally isolates you from potential incompatibilities across future OS versions.

Raymond's post relates to User Interface settings, and describes an application that crashed under Windows 2000 because of the reliance on registry keys and not API's. Rephrasing my earlier point - if there is an API that reads or writes registry key data, it should be considered "Private".

One commenter to Raymond's post responded "sometimes its extremely hard to find the right api, with so many apis hanging around" - and he is 100% correct. This is where having experienced people around your development shop helps. If you don't have the former, this is a case where reading portions of the MSDN library (and blogs like Raymond's and Larry's) helps. If you are short a photographic memory, you won't retain everything - but you may remember reading something in the past and a few keywords that you can Google for later. Microsoft can help programmers avoid this trap somewhat by adding the keys that are read from or changed by API's in the MSDN documentation with a disclaimer - "The following key stores data related to this API but should not be relied upon as an implementation detail in future OS releases."

Sometimes it is hard to follow these "rules." Take for instance the Distributed Transaction Coordinator, or DTC for short. If you wanted to get or change any particular setting related to DTC you can't do it the same way across OS versions. In Windows 2003, there is the IDtcNetworkAccessConfig interface you can use. To change the same settings on Windows XP, the registry appears the only option. Furthermore, the upcoming Windows 2003 SP1 DTC changes (which mirror the XP SP2 changes) has not updated the API to reflect the change (at least in the SP1 RC2 version of the SDK documentation). Specifically, there is no updated method of allowing or disallowing specific transaction permissions such as inbound or outbound, so the API is useless - back to registry keys! Checking the DTC authentication level never had an API - just some registry keys documented in a KB article. I hope they fix this oversight for the final SP1 release - but in the meantime, what is a developer expected to do?

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.

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