Compiling Components into Servers

[Previous] [Next]

A COM server must expose one or more components to be useful to a client application. In this sense, you can simply define a server as a binary file that packages a set of components. As you know, each component is defined as a coclass in the server's type library. In addition to defining coclasses, a server's type library can define other COM data types such as interfaces, enumerations, and UDTs.

COM supports two server types. COM DLLs are in-process servers (also known as in-proc servers), and COM EXEs are out-of-process servers. You must decide whether to serve up your components through an in-process DLL or an out-of-process EXE.

The Visual Basic integrated development environment offers three Project Type settings for building COM servers. You specify this setting when you create a new project, and you can change it afterward in the Project Properties dialog box. You use an ActiveX DLL project to create an in-process server, and you use an ActiveX Control project to create a more specialized type of visual in-process server. (This book doesn't cover ActiveX Control projects.) You use an ActiveX EXE project to create an out-of-process server.

In-Process Servers

An in-process server such as an ActiveX DLL is typically the most efficient choice for packaging components. Your code gets loaded into the address space of the client application. An object from an in-process server can usually be directly bound to the client without any need for a proxy/stub layer (as described in Chapter 3). Direct binding allows the client and the object to share the same thread, the same call stack, and the same set of memory addresses. Once the object is activated, the communication between the two is as fast as it would be if the class were defined within the client application. Better performance is often the primary advantage of using an in-process server.

An in-process server also imposes a few significant limitations. There is no easy way to share data between two different client applications using the same ActiveX DLL because all variables are process-specific. An in-process server is also not as robust as an out-of-process server because it's tightly coupled to the client application process in which it's loaded. A defective object can crash the client application, and if the client application crashes on its own, the object also crashes.

An in-process server is also somewhat inflexible when it comes to security. An object activated from an in-process server usually runs under the same security context as the client application. For instance, if two users, Bob and Sally, use your ActiveX DLL, some of your objects will run under Bob's identity and others will run under Sally's identity. This isn't always a problem, but sometimes you'll want all the objects activated from a particular server to run under a dedicated user account rather than taking on the identity of the client application. When client applications activate Visual Basic objects from in-process DLLs, you don't have the option of specifying a dedicated user account. This security issue becomes particularly important when you want to deploy middle-tier applications. We'll revisit this topic in greater depth in Chapter 11.

Out-of-Process Servers

An out-of-process server is implemented in Visual Basic as an ActiveX EXE. The executable file that Visual Basic builds when you compile an ActiveX EXE can launch and control its own Win32 process. When a client activates an object from an out-of-process server, COM's Service Control Manager (SCM) finds or loads the server process and negotiates the creation of a new object. Once the object and the client have been bound together, a proxy/stub layer exists between them. As you know, this layer adds significant overhead. Calls to objects in out-of-process servers are much slower than calls to objects loaded from in-process servers.

A local server runs on the same computer as the client application, while a remote server runs on a different computer. In terms of COM servers, it doesn't really matter whether your ActiveX EXE runs locally or on a computer across the network. An ActiveX EXE is always configured to run as a local server. Once the server has been configured to run as a local server, you can add a few configuration changes to the Registry to allow remote clients to use it as well. You don't need to do anything special to an ActiveX EXE project to differentiate between these deployment options.

Out-of-process servers are more robust than in-process servers. They're at least as robust as the operating system on which they're running. With an in-process server, either the client or the object can potentially crash the other. An out-of-process relationship has a built-in level of fault tolerance. A client can detect that an object has died by inspecting the HRESULT returned by any method. You'll see how this is done later in the chapter. The infrastructure of COM can also detect a dead client and notify the object that the connection is no longer valid. With an out-of-process server, either the client or the object can continue to live a productive life after the other has passed on.

So how do you decide between an ActiveX DLL and an ActiveX EXE? You should think about performance first. If your objects can be used exclusively by a single client application, it makes sense to package your components in an ActiveX DLL. This is the best approach when your code calculates a value such as sales tax or an interest rate. You can install your DLL on the user's desktop computer along with the client application and provide the best possible performance.

But what if you need to run your objects from across the network? One seemingly intuitive solution is to create an ActiveX EXE project because an ActiveX EXE can serve up objects to remote clients. However, another option gives you far more flexibility: serving up distributed objects with a surrogate process provided by COM+, as described in the next section.

Packaging Components for COM+

Much of Microsoft's strategy for creating distributed applications is based on the concept of surrogate processes. A surrogate process is a system-supplied runtime environment that acts as a host for your objects. For example, COM+ provides a container application named DLLHOST.EXE. Each COM+ server application runs in its own separate instance of DLLHOST.EXE. When you want to run your objects in this runtime environment, you can't package your components in an ActiveX EXE. Your only choice is to package them in an ActiveX DLL.

Once you compile your components into an ActiveX DLL and properly install them in a COM+ server application, clients can activate objects from across the network. Although you must package your components in an in-process server, COM+ can still provide the benefits of an out-of-process server. You have added fault tolerance, the ability to share memory, and the ability to have all objects run under a dedicated user account.

The difference between an out-of-process server that will be used by one or two client applications and an out-of-process server that will run objects for hundreds of client applications is significant. With an out-of-process server that will be accessed by a large user base, you must deal with more scalability issues, such as thread pooling, security, and database connection management. A COM+ server application provides a scalable architecture, while an ActiveX EXE does not. For this reason, I'll spend much more time in this book discussing ActiveX DLLs than ActiveX EXEs. Your time will be much better spent concentrating on how to create components for COM+ applications.

The idea of a universal runtime environment such as the one supplied by COM+ is appealing for a few reasons. First, it makes things easier for developers because it provides much of the infrastructure code that's required to scale a middle-tier application. Second, it allows Microsoft to maintain all this infrastructure in a single code base and, consequently, share it across many different languages. Finally, it allows COM objects to run and behave in a consistent manner, even if they were created with different languages and tools. Chapter 6 will introduce configured components and describe how to use all these built-in system services. The main point I want to emphasize is that you'll almost always ship your components in ActiveX DLLs.

Building a Server

Visual Basic makes some things incredibly easy. Even a four-year-old can build an ActiveX DLL or an ActiveX EXE in the Visual Basic IDE using the Make command on the File menu. This command opens a dialog box with various options and an OK button. You simply click OK to build the current project into a binary server.

Several important things happen behind the scenes when you choose the Make command. Figure 4-1 depicts what Visual Basic builds into the server. First, Visual Basic automatically publishes information in a type library and bundles it into your server's binary image. The type library is important for use by other development tools that need to create vTable bindings for your objects at compile time. The universal marshaler also uses this type library to build proxies and stubs at runtime.

click to view at full size.

Figure 4-1 When you build a server using the Make command on the File menu, Visual Basic does lots of work for you.

Second, for every component Visual Basic generates an implementation of each interface that's supported as well as an implementation of both IUnknown and IDispatch. Third, Visual Basic adds class factory support for each component. Finally, Visual Basic adds the code to your server to support self-registration. The self-registration code adds entries to the Registry for each CLSID and IID. It also adds a set of entries for the type library.

After Visual Basic finishes building the server, it registers it on your development workstation. When you build an ActiveX DLL, Visual Basic uses the REGSVR32.EXE utility to register your server. When you build an ActiveX EXE, it uses the appropriate command-line parameters to register it as an out-of-process server.

Dealing with Registry madness

It's safe to say that, of all the developers in the industry today, Visual Basic programmers don't have the reputation of being the most disciplined. However, the discipline of properly registering and unregistering servers on your development workstation at the proper times is worth striving for.

How can you tell if you aren't as disciplined as you should be? Here's a good test. Bring up the References dialog box from the Project menu. If you see two or more type libraries with exactly the same description, you know that you should pay more attention to what's being written to your Registry. If you see 10 or more type libraries with the same description, you're one of the programmers who've earned the Visual Basic community an unjust reputation for lacking discipline.

As you know, Visual Basic builds in self-registration code and then registers the server when you build an ActiveX DLL. What if you create a second build of the server and overwrite the first build? The Visual Basic IDE unregisters the first build before deleting it. There's not much that you have to be concerned with. However, if you create the second build of the DLL in a different location and don't overwrite the first build, you might leave entries for the first DLL in the Registry. That's probably not what you want. To make things worse, if you delete a registered DLL without unregistering it first, you delete the only code that knows how to remove those DLL entries from the Registry.

The point is that there are two situations in which you should always unregister a DLL: when you've built a newer version in a different location and before you delete the DLL. Adhering to these two rules keeps the Registry on your development workstation young and vital. Unregistering a DLL that isn't registered doesn't hurt anything; forgetting to unregister a DLL pollutes your Registry. Most experienced COM developers know that there's only one sure-fire way to restore the Registry once it's been abused by undisciplined programmers, and as you've guessed, it starts with the following command:

 FORMAT C: 

Generating a stand-alone type library

As you know, the universal marshaler needs the type library for building proxies and stubs at runtime. But what if the server runs on one computer while all the client applications run on other computers? On the computer that holds the server, the universal marshaler can simply use the type library bundled into the server's image. However, the client computer also needs a copy of the type library. The universal marshaler can't build a proxy without it. You could copy the server file to each client computer, but that would be inefficient. The server file includes all the component implementations, and the client computers need only the interface definitions.

Visual Basic lets you create a stand-alone type library by selecting the Remote Server Files check box on the Component tab of the Project Properties dialog box. When you select this check box for an ActiveX DLL or an ActiveX EXE project, the Make command creates a separate type library file with the .TLB extension in addition to the type library it builds into the server. The primary motivation for selecting the Remote Server Files check box is to create a stand-alone type library for distribution to the client computers in a network environment. This type library allows the universal marshaler on a client machine to build proxies as needed.

Miscellaneous compilation options

Before getting into server design issues, I want to quickly cover a few of the options you have when compiling a server. The Project Properties dialog box has a check box for Unattended Execution that tells the Visual Basic compiler to avoid anything that involves user interaction. For example, when you select this check box, all MsgBox statements are converted into application event logs.

When you invoke a MsgBox statement from your component, you hang the current thread, which can be a terrible thing to do in a COM+ application that's running in production. So you should consider the Unattended Execution option as a safety precaution. It is recommended for production builds of your servers. However, if you like to bring up message boxes during debugging, you should compile debug builds without this option.

If you select the Unattended Execution check box, the Retained In Memory check box also becomes available. Selecting this check box prevents the hosting application from unloading the code in your DLL. The DLL stays loaded for the lifetime of the hosting process. This option is recommended for servers that will be used in middle-tier environments such as COM+ and Microsoft Internet Information Services (IIS). You want to reduce the number of times that the code from a DLL is loaded into an application. If the DLL refuses to unload, it doesn't need to be reloaded later. If you select this option, you should be able to assume that the hosting application will continue to use the code from your server throughout its lifetime. This is usually a safe assumption in a COM+ application.



Programming Distributed Applications with COM+ and Microsoft Visual Basic 6.0
Programming Distributed Applications with Com and Microsoft Visual Basic 6.0 (Programming/Visual Basic)
ISBN: 1572319615
EAN: 2147483647
Year: 2000
Pages: 70
Authors: Ted Pattison

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net