Click Here to Install Silverlight*
United StatesChange|All Microsoft Sites
MSDN
|Developer Centers|Library|Downloads|Code Center|Subscriptions|MSDN Worldwide
Search for


Advanced Search
MSDN Home > MSJ > May 1997
May 1997


Code for this article: activexcode0597.exe (3KB)
Don Box is a co-founder of DevelopMentor where he manages the COM curriculum. Don is currently breathing deep sighs of relief as his new book, Essential COM (Addison-Wesley), is finally complete. Don can be reached at http://www.develop.com/dbox/default.asp.

Q I am building a suite of COM objects that have little or no user interface and will be called from a variety of client languages, perhaps over a network. Should I build inproc servers or outofproc servers?

A As you might expect, there is no universally true answer. There are some basic guidelines that may help you decide which way to go, but both types of servers would work and each has its own relative strengths and weaknesses. This is why both approaches are available.
The basic trade-off between inproc and outofproc servers has been one of balancing performance with robustness. Inproc servers favor performance while outofproc servers favor robustness. A properly written inproc server imposes no COM-related overhead on each method call. The object’s data members and method implementations all reside in the address space of the client and, assuming the server supports the threading model of the client’s apartment, can use the client’s thread and stack to execute the method call. This yields the best possible performance.
Figure 1 shows the relative performance costs of making a null method call (no parameters, no actual work done in method) for a variety of client/object scenarios. Note that, to achieve good performance, the threading model of the DLL needs to be compatible with the threading model of the client apartment. If this is not the case, then the object will be created in a different apartment (although still in the client’s process) and the resulting pointer will be marshaled back to the apartment that calls CoCreateInstance, creating a proxy/stub connection. In some cases, accessing out-of-process objects is actually faster than accessing an inproc object in a different apartment. The implication of the results in Figure 1 is that, if you do not support the threading model of the client, you are losing one of the primary advantages of being inproc: performance.
Performance issues aside, when you deploy your object as an inproc server, your clients will be able to create instances of your class in their address spaces alongside all of the other client-side data structures used to build the client program. However, since your object runs in the execution context of the client, it is subject to all of the hazards that can result from raw client access. For example, if the client decides to pass one of your object’s interface pointers to the C runtime library’s memcpy routine, your object is in trouble:


 void NaughtyClient(IFoo *pFooYou) {
    memcpy(pFooYou, "Hello Toast!", 20);
 }
Your object is equally susceptible to array boundary overruns, dangling pointers, uninitialized pointers, or just about any other random pointer bug that the client may choose to throw your way.
If your anticipated client is Visual Basic
® or Java, you may think this isn’t much of an issue, as these languages tend to protect programmers from themselves more than C or C++. However, it is possible for a Java-based client to load other inproc objects that were written by C++ programmers less skilled than you. If these objects cause a fault, they will bring the client (and by association, your object) down with them.
In contrast, objects that run in outofproc servers are insulated from faults that occur in their clients or in other objects used by their clients. Conversely, clients are insulated from faults that happen in the outofproc servers they communicate with. Roughly speaking, outofproc servers inherit the robustness of the process model of the underlying operating system. Dead servers can be detected by checking the HRESULT returned by all remotable methods. If the facility code of the HRESULT is FACILTY_RPC or the HRESULT is an RPC-related FACILITY_WIN32 error, then the result is from the underlying communications infrastructure, not from the object.
Dead clients are detected automatically by the OXID Resolver (OR). The OR is a system component in COM and runs in the RPCSS service with the RPC endpoint mapper and the COM Service Control Manager. The OR is responsible for translating apartment identifiers (OXIDs) to network/IPC addresses. It is also responsible for managing the ping conversation between any host machines that have active COM-related connections. The client-side OR periodically sends a ping packet to the server-side OR, indicating which object references are still valid. If the server-side OR does not receive a periodic ping notification for some preestablished period of time, it assumes that the client machine has died and runs down all of its outstanding references in the appropriate server processes.
A somewhat more subtle problem related to inproc servers is leaked references. Consider a client that calls your AddRef method one too many times—or calls your Release method one too few times. Without a balanced AddRef/Release pattern in the client, your reference count will never decrement to zero and your object will never be properly destroyed. Any system-wide or network-wide resources your object had planned on releasing in its destructor may potentially be leaked. For example, consider the following code fragment:

 class CMyClass : public IMyInterface {
   HANDLE m_hmutex;  BOOL m_bLocked;
   STDMETHODIMP StartWork(void) {
     m_bLocked = WaitForSingleObject(m_hmutex, 
                                     INFINITE) 
                                    == WAIT_OBJECT_0;
     return S_OK;
   }
   virtual ~CMyClass(void) {
     if (m_bLocked)
       ReleaseMutex(m_hmutex);
   }
     :
 };
Despite the fact that the mutex handle will be closed when the client process exits or is terminated, any threads waiting on the mutex in other processes will awaken with an abandoned mutex and probably need to reinitialize any shared resources protected by the mutex. This is not the sign of a well-crafted system.
Had the server been implemented as an out-of-process server, the leaked client reference would only leak the client-side proxy, not the object itself. While the leaked proxy would in fact keep your object alive, the proxy would be run down by COM when the client process exits, in effect implicitly releasing the reference on your object. When the final reference is run down, either explicitly via a client’s call to Release on the proxy or implicitly via client process termination, your object will be released by COM, triggering its destructor. Note that your protection from client-side leaks kicks in only when the client process exits or when the client host machine dies. If the client process and host machine keep running indefinitely, then any leaked object references in the client will seem active to the OR. This means that the Stub will never release your object.
A classic solution to this problem is to build an idle timer into the object to detect extended periods of inactivity. After some predetermined idle period, the object could call CoDisconnectObject, terminating all outstanding client connections to the object. While somewhat brutish, this technique would allow the server to reclaim any resources held on behalf of its clients. For this technique to work, the object requires the level of indirection afforded by the proxy/stub connection. Had your object loaded in the apartment of the client, calling CoDisconnectObject would have no effect because the client would still have a raw pointer into your object state.
The problem of leaked references is related to the more general problem of process versus object lifetime. By their nature, all objects live in a process. For an out-of-process server, this process is created dynamically by the Service Control Manager (SCM), based on the server’s implementation of main/WinMain. For outofproc servers, the server implementor is in complete control of when the process shuts down. The standard implementation of a server’s WinMain is to have the main thread of the process wait around until no objects have outstanding clients to service. This guarantees that the object’s "home" will remain alive as long as it is needed.
For inproc servers, the object lives in the client’s process. Unfortunately, the client’s process lifetime is not tied to the lifetime of the inproc objects it creates. At first glance, this may not seem like a problem. A properly implemented client would never dream of shutting down until all interface pointers it had acquired have been released. However, assume the following client-side code:

 void f(IMoniker *pmk, IBindCtx *pbc) {
   IFoo *pfoo = 0;
   pmk->BindToObject(pbc, 0, IID_IFoo, (void**)&pfoo);
 // pfoo points to an existing outofproc object
   IBar *pbar = 0;
   CoCreateInstance(CLSID_Bar,CLSCTX_INPROC_SERVER, 
                    0, IID_IBar, (void**)&pbar);
 // pbar points to a new inproc object
   pfoo->UseThisObject(pbar);
 // pbar is marshaled to pfoo’s apartment, creating
 // stub in client’s apartment to receive incoming 
 // calls
   pfoo->Release(); // release client’s ref
   pbar->Release(); // release client’s ref
 }
As shown in Figure 2, the client passes a pointer to an inproc object (pbar) as a method parameter to a remote object (pfoo). This causes the COM remoting layer to build a stub in the client’s process to receive packets from the proxy created in the remote object’s process for the duration of the UseThisObject method call. This is required to allow the inproc object to receive calls from the remote object. If the remote object does not AddRef the proxy it receives as the [in] parameter to UseThisObject, all is well because the stub routine for UseThisObject will automatically release the proxy to pbar, tearing down the Bar stub once the method call has completed. If, on the other hand, the remote object were to AddRef the Bar proxy it received in UseThisObject, the client process should remain running as long as the proxy is outstanding. Unfortunately, there is no generic mechanism for the inproc object to communicate its lifetime to the client. Because of this, the client is apt to shut down prematurely, leaving the remote object with a dangling proxy. While the remote object will not crash, it will receive an HRESULT from the next method call indicating that the client-side object has gone away.
Figure 2 Sharing an In-process Object
Figure 2 Sharing an In-process Object

If the Bar object in the previous example was implemented as an outofproc server, the client could safely pass the reference to the remote Foo object, because Bar has its own process, distinct from the client. As shown in Figure 3, when the Bar object is implemented as an outofproc server, the lifetime of the client process is irrelevant to the Bar object’s lifetime.
Figure 3 Sharing an Out-of-process Object
Figure 3 Sharing an Out-of-process Object

You might infer from this example that clients should not pass inproc object references as parameters to methods. While this seems reasonable in theory, in practice it is impossible to know which object references refer to inproc objects. Additionally, many methods accept interface pointers as [in] parameters but do not AddRef them, which is perfectly safe. In practice, it is the job of the client, the object implementor, and the interface designer to ensure that this problem does not arise. One common solution is for the interface designer to document which method calls are likely to AddRef their [in] parameters. The Advise method on IConnectionPoint is a great example of this strategy.
One additional robustness-related problem is that inproc servers do not have their own security context. Like all DLLs, inproc servers load into the client’s address space. The code for the inproc object’s methods executes using the client’s access token. This means that if a trusted user (such as Administrator) loads an inproc object, any methods that the object executes will have access to whatever resources the trusted user has access to. Even worse, the inproc object could use the COSERVERINFO/COAUTHINFO structures used by CoCreateInstanceEx to create objects on other machines, granting impersonation rights to remote objects that the client has never even dreamed of. While most programmers dislike the whole notion of security and may think that this seems like a neat feature to exploit, many users would be bothered by a control that launched arbitrarily dangerous objects on an unlimited number of machines with the user’s credentials.
This security problem is often addressed by using code signing and authentication, which works great when the code you want to use is signed by an authority that you trust. However, there may be unsigned code that you really want to experiment with and yet keep it from accessing critical resources without detection. As is shown in Figure 4, if the server is implemented as an outofproc server, you can easily use DCOMCNFG.EXE to configure the server to run as a distinguished security principal. For untrusted code, you could create a new user account (such as UntrustedGuest) and give it virtually no permissions on the system. Additionally, you could configure the system to create audit entries in the security event log whenever the object accesses system resources. For security-sensitive users, this is a critical requirement.
Figure 4 Sandboxing with DCOMCNFG
Figure 4 Sandboxing with DCOMCNFG

Given the robustness issues related to inproc servers, you might wonder if there are any compelling reasons to implement inproc servers beyond an unbridled desire for raw speed and performance; there are several. Occasionally, you’ll need to design a COM interface that cannot remote. IViewObject is one such interface. IViewObject is often implemented by rendering handlers and by controls. It is a critical interface for the ActiveX Control and ActiveX Document architectures, and for efficiency it needs to run in-process. While it is usually better to design remotable interfaces to allow greater flexibility in terms of object location, you’ll sometimes need to break down and design for in-process access.
In addition, when porting large amounts of legacy C++ software to COM, you may need to avoid IDL for expediency and instead define interfaces in header files. This allows you to use intrinsic C++ elements such as classes, templates, and so on. Most of these C++ features prohibit remoting and require the object to run in-process. This strategy breaks down in the long run and should be avoided if at all possible. Perhaps the most compelling reason to implement inproc servers is to integrate with Microsoft Transaction Server (MTS). MTS allows inproc COM objects to be assimilated into a scalable, robust, transaction-based runtime environment. When running inside MTS, your inproc objects run in an MTS-provided surrogate process that affords them the same robust features as an outofproc server. However, for MTS to perform its magic, your object must be implemented as an inproc server.
As of Windows NT
® 4.0 Service Pack 2, COM provides built-in support for surrogates outside the scope of MTS. The idea behind COM surrogates is to decouple packaging from activation. Surrogate-based objects are packaged as DLLs, but can be activated as outofproc servers. Enabling a DLL to be activated inside a surrogate is a simple matter of adding some additional registry entries that the SCM consults when servicing an activation request. These entries can be added by client programmers to sandbox untrusted code or to activate legacy DLLs from other hosts.
To let your inproc CLSID be activated in a surrogate process, the CLSID must have an AppID named value. AppIDs are used in COM to group server-level security and remoting configuration information in the registry. AppIDs typically identify a single server process, which can contain one or more CLSIDs. CLSIDs note their AppID by using the AppID named value:

 [HKCR\CLSID\{XXX}]
   @=Circle Class
   AppID={ZZZ}
 [HKCR\CLSID\{XXX}\InprocServer32]
   @="shape.dll"
   ThreadingModel=Both
 [HKCR\CLSID\{YYY}]
   @=Square Class
   AppID={ZZZ}
 [HKCR\CLSID\{YYY}\InprocServer32]
   @="shape.dll"
   ThreadingModel=Both
These registry entries indicate that Square and Circle share the same AppID. This implies that they will share a common set of security and remoting settings under HKCR\AppID. To indicate that the AppID corresponds to a DLL surrogate, you need to add the DllSurrogate setting to the registry:

 [HKCR\AppID\{ZZZ}]
   @="Shape Server"
   DllSurrogate=
When a non-inproc activation request first occurs for a CLSID, the SCM uses the following precedence rules to decide what to launch:
  1. If the AppID has a LocalService named value, COM uses the OpenSCManager/StartService APIs to start the server.
  2. If the CLSID has a LocalServer32 entry, COM starts the designated process using CreateProcess or CreateProcessAsUser.
  3. If the AppID’s DllSurrogate named value is present yet empty, a system-provided surrogate process (dllhost.exe) is started.
  4. If the AppID’s DllSurrogate named value is present and contains a file name, the designated custom surrogate is started.
  5. If the AppID’s RemoteServerName named value is present, the request is forwarded to the SCM on the designated host.

If the AppID of a surrogate is configured to run as the interactive user or as a distinguished user account, one instance of the process will be started when the AppID’s first CLSID is activated. All subsequent activation requests for any CLSIDs that belong to the same AppID will be sent to the already running surrogate. If the AppID is configured to run as the activating user, then a new instance of the dllhost.exe process must be started for each unique client security principal that tries to activate one of the AppID’s classes.
Much like MTS packages, surrogate AppIDs form a unit of trust. As shown in Figure 5, all CLSIDs with the same AppID will be loaded into a single surrogate process and will have raw access to each other’s objects. CLSIDs that are configured to use the default surrogate but with a different AppID will load in a different instance of dllhost.exe. For many DLLs, the system-provided default surrogate is sufficient. The default surrogate is capable of loading both Multithreaded Apartment (MTA) and Single-threaded Apartment (STA)-compliant DLLs. It also recognizes the Identity and Security AppID settings used by the SCM and DCOMCNFG.EXE.
Figure 5 CLSIDs, AppIDs, and Surrogates
Figure 5 CLSIDs, AppIDs, and Surrogates

In general, the default surrogate does a more than adequate job for a large class of objects. It is possible, however, that your needs may not fit the behavior of the default surrogate. These are some possible reasons to implement a custom surrogate:
  • To expose nonremotable interfaces, a custom surrogate can inject a wrapper between the client and inproc object that can custom marshal into the client’s apartment.
  • To implement nonstandard process/object lifetime semantics (such as shutting down idle servers or objects).
  • To spread the instances of an STA-only CLSID across multiple STA threads to increase concurrency for SMP machines. For an example of this, please see http://www.develop.com/dbox/com/surrogates/aptsur.htm.
  • To make the surrogate use hardwired security settings independent of what the administrator has configured using DCOMCNFG.EXE.
  • To play tricks on the client and swap in a different CLSID (or perhaps host machine) based on some dynamic state that your surrogate can monitor.
In general, all of these techniques could be implemented in the context of an outofproc server. However, if you are dealing with legacy DLLs, custom surrogates play a critical role in enabling remote, scaleable, and securable access.
To support user-implemented surrogates, it is possible to designate a custom surrogate for a given AppID. This is accomplished by indicating the file name of the custom surrogate as the value of the DllSurrogate AppID entry:

 [HKCR\AppID\{ZZZ}]
   @="Shape Server"
   DllSurrogate=C:\shapeSurrogate.exe
Custom surrogates allow you to write your own WinMain and do more or less whatever you want, provided you properly register your process at runtime as a custom surrogate. As a custom surrogate, you have complete control over the security context and lifetime of the surrogate. Additionally, you are given a chance to wrap any interface pointers that are handed out with your own custom object implementations, which may or may not actually load the designated inproc object. In fact, if a CLSID maps to an AppID with a custom surrogate, the surrogate will start even if no InprocServer32 entry is present for the designated CLSID. Provided that the custom surrogate never tries to access the InprocServer version of the CLSID, this won’t be a problem.
The primary requirement for custom surrogates is that they must register an object at startup that correctly implements the ISurrogate interface:

 interface ISurrogate : IUnknown {
   HRESULT LoadDllServer([in] REFCLSID rclsid);
   HRESULT FreeSurrogate(void);
 }
The LoadDllServer method is called by the SCM to force your process to register a class object for the designated CLSID. The FreeSurrogate is called by OLE32.DLL when the last outstanding connection to your process is terminated, informing you to terminate your process. Your LoadDllServer implementation needs to register a class object with COM to allow the SCM to reach into your process and make class-specific requests.
When implementing a surrogate, you do not want to load and register the inproc class object directly. Instead, the surrogate must create a new surrogate-defined class object wrapper that exposes IUnknown, IClassFactory, and (optionally) IMarshal. This wrapper class must be registered with COM using CoRegisterClassObject:

 STDMETHODIMP 
 CSurrogate::LoadDllServer(REFCLSID rclsid) {
   CWrapper *pcf = new CWrapper(rclsid);
   if (!pcf) return E_OUTOFMEMORY;
 // register with COM using REGCLS_SURROGATE
   HRESULT hr = CoRegisterClassObject(rclsid,            
               (IClassFactory*)pcf, CLSCTX_LOCAL_SERVER,
                REGCLS_SURROGATE, &pcf->m_dwReg);
 // keep track of class object to revoke later on
   if (SUCCEEDED(hr))
     m_rgClassObjects.push_back(pcf);
   else
     pcf->Release();
   return hr;
 }
By requiring the surrogate to register a wrapper instead of the actual inproc class object, COM allows the surrogate to intercept the initial activation or marshaling request from the client’s CoCreateInstance/CoGetClassObject calls. The surrogate is free to either forward the requests directly to the inproc class object or return a custom object that can perform any number of miracles, one of which might include translating the client’s wishes into real method calls on an inproc object.
Figure 6 Custom Surrogate Architecture
Figure 6 Custom Surrogate Architecture

As shown in Figure 6, the client’s CoCreateInstance request is forwarded directly to your wrapper’s CreateInstance implementation. The expected behavior of a surrogate’s wrapper is to use the real inproc class object to implement the method:

 STDMETHODIMP 
 CWrapper::CreateInstance(IUnknown *pUnkOuter, 
                          REFIID riid,void **ppv){
 // forward the call to the "real" inproc factory 
     return CoCreateInstance(m_clsid,pUnkOuter,
                             CLSCTX_INPROC_SERVER,
                             riid,ppv);
 }
If you intend to return a custom wrapper object instead, this is where you do it:

 STDMETHODIMP 
 CWrapper::CreateInstance(IUnknown *pUnkOuter, 
                          REFIID riid,void **ppv){
 // create a new instance wrapper that might
 // (or might not) create the real inproc object
     CInstanceWrap *p = new CInstanceWrap(m_clsid);
     p->AddRef();
     HRESULT hr = p->QueryInterface(riid, ppv);
     p->Release();
     return hr;
 }
There are relatively few limits on what you can do in this function. Note that if you end up returning a proxy to an outofproc object, your surrogate may not remain running after you return from CreateInstance because your surrogate will not have any outstanding references (the outofproc server will instead). The lack of outstanding references causes COM to call your FreeSurrogate method to shut down your process.
When clients call CoGetClassObject, the SCM reaches into your process and marshals the class object you registered in the CoRegisterClassObject call. Your class object wrapper must implement IMarshal so that your class object wrapper will generically forward the CoGetClassObject request to the inproc class object for arbitrary interfaces. You will then receive the marshaling request via your wrapper’s MarshalInterface method. To simply forward the request to the real inproc implementation, the code shown in Figure 7 is sufficient.
If you are implementing a custom wrapper around the DLL’s objects, you may want to simply forgo any activation interfaces beyond IClassFactory unless you have a priori knowledge of them and can implement them manually on your class object wrapper. To support wrapping arbitrary interfaces on a class object requires not only an in-depth understanding of type information or MIDL format strings, it also requires that you understand the semantics of the methods, which is not possible in the general case.
To allow COM to call your LoadDLLServer and FreeSurrogate methods, your surrogate process must register its implementation of the ISurrogate interface by calling CoRegisterSurrogate at startup:

 HRESULT CoRegisterSurrogate(ISurrogate *ps);
CoRegisterSurrogate must be called after calling CoInitializeEx and (optionally) CoInitializeSecurity. Failure to call CoInitializeSecurity will cause your surrogate to run using the default access permissions for your machine. If you want to get your surrogate’s AppID-specific settings from the registry instead, use the new EOAC_APPID capabilities flag, passing your AppID as a GUID to CoInitializeSecurity:

 hr = CoInitializeSecurity((void*)&APPID_Custom, 0, 0,
                           0, 0, 0, 0, EOAC_APPID, 0);
Beyond the call to CoRegisterSurrogate, your surrogate process is basically another COM server. You can spawn additional apartments or threads, change process priority, make outbound COM method calls, and so on.
Figure 8 shows a complete implementation of a generic custom surrogate that terminates itself after 15 seconds of inactivity. This surrogate does not attempt to wrap the pointers to the inproc objects, but instead simply provides direct access using the techniques discussed in this article.
So, given all of the issues discussed here, how do you decide which type of server to provide? Here are some situations that may motivate you to implement an outofproc server:
  • If you expect instances of your object to be shared by more than one client.
  • If you expect to advertise your object in the running object table.
  • If you need isolation from client faults.
  • If you need each instance of your object to be isolated in its own separate process using REGCLS_SINGLEUSE.
  • If you need to do your own idle-time object termination/garbage collection.
  • If you need your own security context.
  • If your object has a nontrivial user interface.
  • If your object requires a particular threading model (for example, it cannot be ThreadingModel=Both when inproc) and will not be able to run in the anticipated client’s apartment.
In the following situations, you might want to implement an inproc server:
  • If raw, same-machine performance is critical.
  • If you need to expose interfaces that cannot remote (such as IViewObject).
  • If you need to provide an object’s internal data structures directly to the client.
  • If you need your object to integrate with MTS.
The availability of surrogates blurs the distinction somewhat. If you implement an inproc server and designate either a custom or system-provided surrogate, you are really delegating the choice to the client. Since many clients (including Java and Visual Basic) use the CLSCTX_ALL flag when calling CoCreateInstance, most clients on the local host will get the inproc version, and remote-host clients will get the server loaded in a surrogate. This means that local-host clients get the robustness semantics of an inproc server, while remote-host clients get the superior robustness semantics of an outofproc server. Because of this, surrogates are really best suited for allowing legacy inproc servers to be accessed across host boundaries or for sandboxing untrusted DLLs.
If you are building distributed objects for arbitrary clients, you are probably better off implementing outofproc servers, or perhaps hosting your inproc server in MTS. Fortunately, provided you first define all interfaces in IDL to ensure that all of your methods are remote-capable, it is fairly painless to port from one type of server to another because most of the changes happen in the registration and server-lifetime portions of the code.

Have a question about programming with ActiveX or COM? Send your questions via email to Don Box at dbox@develop.com or http://www.develop.com/dbox/default.asp

From the May 1997 issue of Microsoft Systems Journal. Get it at your local newsstand, or better yet, subscribe .

© 1997 Microsoft Corporation. All rights reserved.
Terms of Use
.

© 2015 Microsoft Corporation. All rights reserved. Contact Us |Terms of Use |Trademarks |Privacy & Cookies
Microsoft