Taming DLL Hell

Windows File Protection, side-by-side components, and DLL redirection can help you douse the flames

Three problem-solving features in Windows 2000 might benefit you more than any other technology in the OS. Windows File Protection (WFP), side-by-side (S*S) components, and DLL redirection can help systems administrators fight DLL Hell, an annoying problem in Windows that prevents two or more applications that share components from operating independently of one another. Win2K's new features go a long way toward eliminating DLL Hell.

The Road to DLL Hell
DLLs and other shared components are modular, reusable code libraries that a calling application can load on the fly. However, DLLs, OLE custom controls (OCXs), and similar modules can create problems for enterprises that deploy many applications on Windows desktops. The problems' root is the shareable nature of DLLs and other Windows application components such as COM controls. When you upgrade a Win32-based DLL, an OCX, or a COM component, you expect the component to maintain backward compatibility. When you upgrade a component or library that multiple applications use, you expect all those applications to continue to operate.

This compatibility often doesn't happen. In some cases, when you upgrade a component, a dependent application fails the first time it tries to use the component's new version. The problem is exacerbated when software vendors, including Microsoft, ship new versions of shared components with their applications. When you install the new application, it upgrades the shared components. Then, other applications crash when they try to use the altered components. This situation is DLL Hell.

Keeping Track of Components
In Windows NT 4.0, DLL Hell is hard to prevent. The best strategy is to try to avoid the problem. In many cases, enterprises need to separate certain applications from one another so that the applications don't try to share .dll files and other components. However, this separation limits Windows' flexibility and adds to an NT-based infrastructure's total cost of ownership (TCO).

Win2K and NT 4.0 use reference counting (i.e., refcounts) to keep track of how many applications depend on a particular shared component. Reference counting is a fragile mechanism because it relies on each dependent application to correctly add to and subtract from a component's reference count when the application installs or uninstalls the component. Not all applications perform this operation or perform it correctly. Reference counts for shared DLLs and other components reside in the Registry under the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ Windows\CurrentVersion\SharedDLLs key. If the shared component is a COM component, the component uses its globally unique ID (GUID) to register its location in HKEY_CLASSES_ROOT\CLSID and HKEY_CLASSES_ROOT\Interface. The HKEY_CLASSES_ROOT\CLSID entry specifies the COM component's location in the file system (a path to the component typically resides in the InProcServer32 Registry value). An application that calls a particular COM component looks in the Registry to locate that component. The calling application uses the component's GUID, which refers to an interface within the component or the component's programmatic identifier (ProgID), to locate the key in HKEY_CLASSES_ROOT\CLSID that describes the component in the Registry. The application then locates the DLL, EXE, or OCX component that holds the functionality the application requires.

If the component isn't COM-based (e.g., a library of Win32 functions), an application uses a different process to locate and load the file that the application needs. In many cases, applications rely on a search process that includes looking first in system memory, then in \winnt\system32, then in the application's directory, and then in any defined path on the system. In NT, applications that you purchase or develop install most shared components by default in \winnt\system32.

For example, suppose you purchase and install application A, which installs version 1.0 of comctrl32.ocx to \winntsystem32 on all your workstations. Six months later, you install application B, which includes version 2.0 of comctrl32 .ocx. The setup program for application B notices version 1.0 of the OCX and upgrades it to version 2.0, overwriting the previous version. Although you expect application A to continue to work using version 2.0 of the new component, that compatibility often doesn't happen. Frequently, application A fails and displays a message about an entry point or ordinal that the program can't find or a method that it doesn't support. When this situation occurs, you have two choices. You can decide that applica-tions A and B won't exist on the same machine, or you can upgrade application A to support component version 2.0. Such an upgrade can be difficult if you've deployed application A on 50,000 desktops or the application's developer is no longer in business and you don't have the source code. Fortunately, Microsoft recognizes the difficulty that this problem causes for enterprises and tries to correct the problem in Win2K and Windows 98 Second Edition (Win98SE).

Windows File Protection
Win2K and Win98SE include WFP, a process that runs in the background on every Win2K server and workstation. WFP monitors the system files that Win2K installs in \winnt\system32. If a user or application replaces a protected file with a different version, WFP quietly restores the overwritten file with a copy of the original that the system has held in a cache (by default, the cache is in \winnt\system32\dllcache). The protected-file cache includes several Microsoft-provided shared components (e.g., Microsoft Foundation Class Library—mfc.dll, common controls library—comctrl32.dll). For more information about utilities that operate within WFP, see the sidebars "The System File Checker," and "File Signature Verification," page 84.

To qualify for Win2K logo certification, an application must not include a protected component. This requirement prevents application developers from distributing different versions of Microsoft-provided components in a way that could break other dependent applications. However, if you have an application that relies on shared components other than those that Microsoft provides—or an application that relies on a different version of a Microsoft shared component from the one that Win2K includes—WFP doesn't provide much, if any, relief for you. You might find that S*S components and DLL redirection are helpful alternatives.

S*S Components
In Win2K, you can take several approaches to solving the DLL Hell problem. One approach involves a programming change. If you or someone in your company has written COM or Win32-based applications for your environment and you plan to deploy Win2K Professional, consider using S*S components. If you have existing applications that you need to get working immediately, DLL redirection might be the way to go.

You can use S*S components, which isolate components to a particular application, if you're a developer who is writing a new application or a set of shared components for Win2K. The isolation that S*S offers gives you the benefits of DLLs and the advantages of statically linked libraries. S*S components can exist on a Win2K system in which different versions of the component might also exist. A shared component can run at the same time that a different version of the component runs elsewhere on the same machine.

When you write Win32 or COM components that are S*S compatible, you need to recognize that some applications will require different versions of your component. All of a component's state information (i.e., information related to the component's data structures and file or other memory resources) must be private. To accomplish this privatization, you need to take specific steps with your code when you write and install it.

When you write a new S*S component, you first give it a name that is different from earlier component versions that aren't S*S compatible. This renaming ensures that backward compatibility with non-S*S components doesn't become a problem if you install the new component on an NT 4.0 machine or other Windows OS that isn't capable of supporting S*S components.

You also need to separate memory sections and data structures. Win2K supports memory sharing between multiple processes. Memory sections are memory management objects that make shared code from a file on-disk or the pagefile accessible to multiple applications. For example, your application might call the Win32 API CreateFileMapping(), which creates a memory section that maps to a certain file or pagefile area in which the system stores a shared component. In S*S mode, multiple versions of a component can run at the same time, so, to map shared components into memory, avoid using memory sections that more than one process might use. Similarly, your component must not create any shared data structures in memory; different versions of the component could access these data structures and then might not function properly.

In your new S*S component, you also need to separate user and application data. If your component keeps user-specific or application-specific data, don't store that data in a place where a different version of the component could overwrite the data. For example, suppose you write a COM component or Win32 DLL that lets an application send email through Microsoft Exchange Server. To accomplish that task, your DLL stores configuration information about the mailbox name through which the system will send email messages. Suppose the system stored this configuration information in a file in a global location (e.g., C:\winnt). Now, suppose you create a new version of your mail component, and the new version changes the format of the file that stores configuration information. The new version will overwrite the old version's file. As a result, applications that depend on the old version will stop working.

Similarly, if your component uses the Registry to store user or application data, ensure that you store the user or application data in Registry keys specific to the component version. For example, I would need to store configuration information about the email component in a key such as HKEY_ LOCAL_MACHINE\SOFTWARE\ABCSoftware\MailComponent1.5. This type of storage in the Registry separates configuration information that is specific to each component version.

COM components offer a different challenge from simple Win32-based components. COM components register by their GUIDs in HKEY_CLASSES_ ROOT. To ensure privatization, it's important for you to register S*S COM components correctly. You can generally complete the registration when you package the application for installation. The path to the component file (e.g., DLL, OCX) must be relative. COM components usually store their location under the GUID in the Registry's InProcServer32 value, and many components that self-register during installation or startup register their absolute path in the InProcServer32 value. For example, if I install mywidget.dll into C:\program files\applicationA and mywidget.dll registers itself, my widget.dll is likely to put C:\winnt\system32\mywidget.dll into the InProcServer32 value. This registration defeats privatization because mywidget.dll might install into multiple directories as each application that depends on it is installed. By entering a relative path, you ensure that when the dependent application looks in the Registry under the InProcServer32 value to find mywidget.dll, the application isn't directed to a particular copy of that DLL that one particular application installed. When you package an application that deploys S*S COM components, you need to ensure that the application package uses a relative path to register the S*S COM components in the Registry. Such a path would resemble HKEY_CLASSES_ROOT\CLSID\ \{01c6b350-12c7-11ce-bd31-00aa004bbb1f\} InProcServer32=mywidget.dll.

In this example, any application using mywidget.dll will first find the version of the component stored in the application's directory, before the application finds other component versions in the system. Microsoft recommends that you reference-count the GUID of any S*S COM object that registers. If you reference count an S*S COM component's GUID, you can keep track of how many applications use that GUID; when all the applications uninstall, the last one can remove the component's GUID from the Registry.

If you build and use type libraries with your component, Microsoft recommends that you compile the type libraries within your DLL, rather than as a separate .tlb file. Type libraries are lists of interfaces that a component supports; applications and developers use type libraries to determine which functionality a component supports. S*S components don't use external type library files, so S*S components don't register those files in the Registry, as current components do (in HKEY_CLASSES_ ROOT\TypeLib).

Remember that you also need to privatize new S*S components that you install in the Win2K file system. If a component ships within a particular application, you need to package the component's installation to that application's directory. Similarly, if several applications use the same component, each application needs to install the same version of the component file to that application's directory. This installation is the only way to ensure robust privatization of components.

DLL Redirection
As a systems administrator, you might not be in a position to use the S*S component model to rewrite existing applications. However, DLL redirection lets you retrofit existing applications to support some isolation of shared components and doesn't require any changes to your application code, regardless of whether you're using DLL redirection on a third-party application or on an application you've written. You can activate DLL redirection in two steps.

Step 1. Repackage your applications so that all dependent components are in the application's directory. The DLL-redirected application contains shared components that other applications might use, so keep those components with the application, instead of installing the components in \winnt\system32. This arrangement ensures that a subsequently installed version of the component doesn't overwrite the version that the redirected application uses. The arrangement also ensures that the new component version won't overwrite previously installed versions in \winntsystem32.

Step 2. This step is the key step in activating DLL redirection. In the application directory, create an empty file that uses the isolated application file's name with a .local extension. For example, suppose you want to isolate an application executable file called myapp.exe. In the directory in which the system stores the application, create a zero-byte file called myapp.exe.local. You can use Notepad or the DOS command copy con to create the file. The .local file's presence forces Win2K to look only in the application's directory for shared components when the application starts. For COM components registered in HKEY_ CLASSES_ROOT\CLSID, the .local file also overrides any path information in the InProcServer32 value and loads the component from only the local application directory.

DLL redirection doesn't work on all shared components. Similarly to S*S components, redirected components need to behave well in isolation. For example, if a component uses shared memory or can't run multiple versions of itself at the same time, you might not be able to redirect the application. The easiest way to learn whether DLL redirection works with a certain application is to test the application in redirected mode while the system runs other redirected applications that use versions of the same shared component.

Finding Dependent Components
Identifying an application's dependent components is one challenge of enabling DLL redirection. A tool that can help you perform this task is the Dependency Walker (i.e., depends.exe) utility, which comes with Windows 2000 Support Tools (you can find setup for Support Tools in the \support\tools folder on the Win2K Pro or Win2K Server CD-ROM) or with Microsoft Visual Studio (VS). Figure 1 shows the Dependency Walker running against an executable file, scan32.exe, which is part of Network Associates' VirusScan. The top left pane shows all dependent DLLs that scan32.exe uses. When you're repackaging your application for DLL redirection, you can use this list to ensure that dependent components install in the application's directory. However, you need to ensure that the components you package with the application are specific to that application and aren't Microsoft shared components. For example, WFP protects core system files such as mcf40.dll, so you need to leave core system files in \winnt\system32 where Win2K installed them. (For information about other resources to help with DLL Hell, see the sidebar "Resources for Exorcising DLL Demons.")

The Ultimate Solution
WFP, S*S components, and DLL redirection are great solutions to your DLL Hell woes. They help buy you time as you integrate multiple applications on a Win2K desktop. However, they're only workarounds to a problem that plagues the Windows environment. By running multiple isolated versions of the same shared components simultaneously, you're using a large amount of system resources. This situation might not be a problem in the short run, but it can become serious if you have five or six versions of a component running at the same time. Ultimately, S*S components and DLL redirection can't substitute for upgrading applications to work with the most recent version of those components.

Hide comments


  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.