A COM server must expose one or more coclasses in order to be useful to a client application. The server can be described as a binary file that packages a collection of coclasses. COM supports two kinds of component packaging. 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 classes through a DLL or an EXE.
The Visual Basic IDE (integrated development environment) offers three different Project Type settings for building COM servers. You select this setting when you create a new project, and you can change it afterwards in the Project Properties dialog box. You use an ActiveX DLL project to create an in-process server. 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.
An in-process server is very efficient. It's 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. This means that the client and the object share the same thread 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 one of the biggest benefits of using an in-process server.
An in-process server also imposes a few significant limitations. For instance, each client application gets its own private set of the DLLs variables. This makes it impractical to share data among multiple client applications. An in-process server is also not as robust as an out-of-process server because it's tightly coupled to the client application's 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 from an in-process server (an in-process object) must run 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 while 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 single user account. When you load objects into client applications from in-process DLLs, this isn't an option.
An out-of-process server is implemented in Visual Basic as an ActiveX EXE. The executable file that Visual Basic builds when you use the Make command can launch and control its own Win32 process. When a client activates an object from an out-of-process server, the Service Control Manager (SCM) finds or loads the server process and negotiates the creation of a new object. The object and the client are then bound together with a proxy/stub layer between them. As you know, this layer adds significant overhead. Calls to out-of-process servers are always much slower than calls to 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 packaging, 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 more configuration changes to the Registry to allow remote clients to use it as well. You don't need to do anything special in the Visual Basic IDE 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 Distributed 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 coclasses in a DLL. This is the best approach when your code calculates something such as sales tax or an interest rate. You can install your DLL on the user's desktop computer along with the client application. This gives you the best possible performance.
If you need to share a server process among several client applications, you should deploy an ActiveX EXE server on a user's desktop to allow several client applications to connect at once. This means that you can share data among objects owned by different client applications. This also prevents one of the client applications from crashing the server process and all the objects in it.
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, you have another option that will give you far more flexibility. The next section describes another way to serve up distributed objects based on the concept of a surrogate process.
A surrogate process is a container application that acts as a host for objects served up through a COM-style DLL. Microsoft initially created a generic container application named DllHost.exe for the purpose of deploying out-of-process objects from legacy DLLs. You can modify the Registry entries for a coclass in a DLL in such a way that the SCM activates its objects inside an instance of DllHost.exe. Although deployment using the original version of DllHost.exe is no longer considered strategic, the idea of using a surrogate process is very much alive at Microsoft.
Microsoft Transaction Server (MTS) provides a container application named MTX.EXE. This application provides a surrogate process that can host the objects you create with Visual Basic. This means that you'll usually package your coclasses in an ActiveX DLL when you want to deploy them in an MTS application. Once you build a DLL, you must properly install and configure it on the remote machine running the surrogate process. However, once the DLL is set up properly, clients can activate your objects from across the network.
MTS is made up of much more than just the container application MTX.EXE. A large part of the code behind MTS is maintained in a DLL named MTXEX.DLL. These two components and a few others work together to provide a very sophisticated run-time environment. You can see MTS as an advanced out-of-process server that's capable of running your objects.
There's a significant 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. An out-of-process server that will be accessed by a large user base involves more scalability issues, such as connection management, thread pooling, and security. When you build an ActiveX EXE, the code built into your server must address all of these issues.
Over the last few years, several teams at Microsoft have independently built infrastructure support code for scalable out-of-process servers. The Visual Basic team has built this type of infrastructure support into ActiveX EXEs. In a separate effort, the ActiveX Template Library (ATL) team has added code to ATL's framework to help programmers create multithreaded servers. Many other C++ programmers have written code for connection management and thread pooling by hand. As you can see, quite a few people have written redundant code to solve the same problem.
As a result, Microsoft has decided to provide the core of this essential infrastructure support code in a generic run-time environment. MTX.EXE and MTXEX.DLL work together to provide connection management, thread pooling, and security features that are far more sophisticated than those supplied by an ActiveX EXE.
The idea of a generic run-time environment is appealing for a few reasons. First, it makes things easier for developers because it provides most of the infrastructure code required by a high-volume server. Second, it allows Microsoft to maintain all this infrastructure code in a single code base and share it across many different languages. Finally, it allows COM objects to run and behave in a consistent manner, even if they have been created with different languages and tools.