Build Your Skills: How to optimize apps to run in Terminal Services

Learn why some applications run better than others in a Terminal Services environment and how to optimize applications for Terminal Services

Although Terminal Services have been around for quite some time, they have been getting a lot of attention lately because in many cases, larger organizations find that a Terminal Services environment is more secure and more cost-effective than a traditional LAN. Although just about any 32-bit Windows application will run in a Terminal Services environment, some applications run much better than others. To effectively use Terminal Services, you need to know why some applications run better than others in a Terminal Services environment and how to optimize applications for Terminal Services.

What makes Terminal Services tick?
In many ways, Terminal Services work like a next-generation mainframe. The biggest difference between a mainframe environment and a Terminal Services environment is that while mainframes run purely text applications, Terminal Services are graphical. All of the applications are run on a central computer, and only the video output is transmitted to the end user's machine.

At the same time, when a user logs on to a Terminal Services client, the desktop is not that of the client machine. Instead, the entire Windows session is being hosted by the terminal server. All applications are running on the terminal server as well. This means the server must be able to track who is running what applications and must be able to keep each user's sessions separate.

The biggest challenge caused by this is contention for resources. Each user's session is competing for server resources such as CPU time, disk time, disk space, memory, and network bandwidth. Normally, Windows does a fairly good job with multitasking and therefore has no trouble assigning resources to sessions and applications in a way that ensures the system has enough resources for everyone.

The problem is that a poorly written application can be too demanding of resources. Since all sessions are competing for system resources, if a user is running a CPU-intensive application, it means that less CPU time will be available for other users, and those users may notice a system slowdown. Worse yet, multiple users can run a CPU-intensive application simultaneously and completely drain the server of its CPU resources.

The easy fix to the problem is to keep spending money on additional server components. Certainly, adding more and faster CPUs, hard disks, and RAM will increase the server's performance. But it's important not to overlook the software aspect. There are certain types of software that should not be run in a Terminal Services environment. Likewise, if your company has an in-house development staff, it should adhere to some programming guidelines that I'll discuss later.

16-bit applications can be a problem
I'm not going to tell you not to run 16-bit applications on a terminal server. There are plenty of 16-bit applications that run fine in a Terminal Services environment. However, there are some specific issues with 16-bit applications that you need to be aware of.

For starters, a 16-bit program will generally use more RAM than a comparable 32-bit program. You may never notice this on an individual PC, but keep in mind that in a Terminal Services environment, all of the users are competing for a limited amount of memory. If one user runs a 16-bit application that requires a little extra memory, you'll probably never notice. However, if 100 users ran that same 16-bit application at the same time, and your server didn't have lots of extra memory to spare, you could have a big problem on your hands.

I recommend avoiding DOS-based applications in a Terminal Services environment. Many of these applications use a function that constantly polls the keyboard for a key press. If you're familiar with the old BASIC programming routines, you'll recall the INKEY$ function. This particular function checks the keyboard for input so frequently that it can place a tremendous drain on the CPU.

Some other DOS applications to avoid are those that require printing. If a DOS application has its own built-in print driver, the application should be avoided. However, 16-bit Windows applications that require printing capabilities are okay because they use the Windows printing API.

You should also avoid programs that are internally coded to map a drive letter to a volume on a NetWare server. Drive letter mapping typically doesn't work well unless the mapping is performed using a universal naming convention (\\servername\share). I also recommend avoiding DOS-based applications that link to FoxPro databases. Such applications tend to be way too CPU intensive to run effectively in a Terminal Services environment.

Finally, I suggest avoiding any older Windows application that directly manipulates an INI file. Although Terminal Services attempt to give each user his or her own copy of INI files, old applications that access INI files directly tend to cause problems within a Terminal Services environment. The problems are caused because the INI file's contents may get changed back and forth as different users access the application.

Beware of software versions
When a new version of an application is released in an organization, it's often standard practice to make both versions available to users during the transition period. However, this practice tends to cause problems in a Terminal Services environment.

The problem occurs because of the way DLL files are used. For example, Internet Explorer 3 and 4 use some DLL files that have the same name but are completely different. If you tried to make Internet Explorer 3 and 4 both available to your users through Terminal Services, it's likely that neither version—or only one version—would run because the DLL files would be invalid for one version or the other. The same problem can occur anytime you have multiple versions of a software package and each version includes its own version of a DLL file whose name was used in a previous version.

Look for exception with 32-bit applications
Generally, there are no problems running 32-bit applications in a Terminal Services environment. However, there are exceptions to every rule. Some types of applications just won't run very well (or at all) if more than one instance of the application is running on the server. Typically, though, such problems are limited to resource monitoring software. There are other issues that can affect a 32-bit application's ability to run on a terminal server. Following are some tips that your development staff can use to ensure that the applications they develop are compatible with Terminal Services.

Application installation
Whether you want to run a commercial application or one that was developed in-house, the application should have an installation program that allows it to be installed or removed through the Control Panel's Add/Remove Programs applet. Earlier, I mentioned that older Windows programs that directly call INI files can cause problems. This is related to the way the applications are installed.

In order for the Terminal Service to share a Windows program among multiple users, each user must have a unique copy of the registry entries and the INI files associated with the application. When an application is properly installed, the Windows installer replicates the application's registry entries and INI files to each user's individual profile. If the application is not installed using the Windows installer, the application's registry keys and INI files cannot be replicated, and the application usually won't run properly.

On a similar note, applications should be designed so that they never store local data in global locations. For example, I've seen countless applications that store application data in the HKEY_LOCAL_MACHINE registry hive. This works great as long as only one user is using the machine at a time. However, if the application is being used by multiple users, user-specific registry data should be stored in the HKEY_CURRENT_USER registry hive.

Likewise, the application should be coded in a way that avoids placing any user-specific configuration files or preference files in a central location. Such files should be placed in the user's home directory and never in the WINDOWS directory or the Program Files directory.

Dangerous assumptions
Developers sometimes make assumptions when designing Windows applications. Some assumptions that are perfectly normal and safe to make in single-user environments simply can't be made in a Terminal Services environment. One such assumption is the existence of the Windows shell. It may sound weird to tell you that you can't assume the Windows shell exists, but think about it. In Terminal Services environments, it's very common to configure a session to go directly to a specific application at login and to close the session when the user exits that application. After all, if a user needs only a single application to do a job, then why give the user access to the Windows shell? Never assume the Windows shell is available when developing Terminal Service applications.

Another assumption you can't make is that a computer name or an IP address is the equivalent of a specific user. On systems in which only one user accesses a computer at a time, the computer name or the computer's IP address is often used to identify the user. We've all seen systems in which activity on an intranet is logged by IP address. Keep in mind, though, that in a Terminal Services environment, all applications are running on the same computer. Therefore, everyone could potentially have the same IP address and computer name from an application's point of view.

Still another thing you can't assume is persistence of time. Often, applications are designed to place a file into a temporary directory and to access that file the next time the user accesses the application. For example, my personal Web site uses cookies to retain a user's settings from one session to the next. However, in a Terminal Services environment, you can't assume that a temporary file (cookie or any other type) will be available once the session ends.

The reason for this is that most administrators configure Terminal Services to delete all temporary files once a session ends. This is done to preserve hard disk space. If your application must use a temp file that must be retained over time, the application should be designed to place the temporary file into the user's home directory or into some other user-specific location.

System files
System files deserve special attention. Never, I repeat, never design an application to replace system files. The reason is that Terminal Services also modify system files. If you're running Windows NT 4.0, many of the system files differ between the normal version and the Terminal Service Edition. Likewise, when you install Terminal Services in Windows 2000, some of the currently existing system files are replaced with versions specifically designed for Terminal Services. If you modify a system file, you run a very real chance of destroying Windows.

Client/server negotiations
Another issue you must consider is the use of front-end/back-end applications. Some applications require a server component that exists in the form of a background service. The foreground application must then be able to communicate with that service. While this type of software can be difficult to design to function in a Terminal Services environment, it's possible to do so.

The trick is that your back-end service must be able to tell the difference between each unique instance of the front-end application. Remember that multiple users mean multiple instances of the front-end application. At the same time, the server supports only a single set of services, so there will be only one instance of the back-end service. Therefore, a single back-end service must be able to tell the difference between multiple instances of a front-end application.

The easiest way to pull this off is to design the application so that it negotiates a communications session between the front-end and back-end components. Typically, you would do this through Remote Procedure Calls (RPCs) or named pipes.

Applications and development practices
If you have an in-house development staff, I recommend using these development practices whether you're currently using Terminal Services or not. Writing applications in the way I've recommended won't cause any harm in non-Terminal Services environments. If anything, such development practices will help your applications run more efficiently across the existing network.

Editor's Picks

Free Newsletters, In your Inbox