A server is a module that provides a service, or component. In other words, the server is the module—EXE or DLL—in which a component resides, and that server is responsible for exposing that component to the outside world. Now, OLE itself can be considered the "server" for many components, such as type library management, which we saw in Chapter 3, or Compound Files, which we'll see in Chapter 7. Because OLE itself is part of the operating system, it can provide uniquely named API functions, such as CreateTypeLib, in order to provide access to its own components. But a custom component server does not have this luxury; any exported API function for creating an instance of that component is not part of the system API, so there is no efficient way to publish such a function for clients at run time. This is a problem especially when you consider that an end user might install a component on a machine after a client is installed. For that component to be useful at all, it must become immediately available to all existing clients, as well as those clients installed later on.
The solution to this problem is to implement both client and server to a particular central standard, which is part of COM. Given a CLSID of a component, the client asks COM, through standard API functions, to create an instance of that component; COM, in turn, asks the server, through standard functions and interfaces, to create that instance and return an interface pointer that COM gives to the client (creating marshaling support as necessary). Given such a standard mechanism, a client is free from specific knowledge about servers, and servers are free from specific knowledge about clients. The simple abstraction layer of COM provides this freedom. The following sections examine the responsibilities and roles of the server, the client, and COM in implementing this standard.
One point, however, applies to both clients and servers alike when those agents are executables themselves. When any executable is run, the operating system creates a new task—a new process space—for that executable. As we saw in Chapter 2, any executable that is using OLE must call CoInitialize on startup and CoUninitialize on shutdown (or OleInitialize and OleUninitialize, as necessary). This applies not only to clients, as we've seen so far, but also to local and remote component servers, because those servers have their own processes as well. It is easy to confuse task initialization as a client-only concern, so remember that it always applies to any executable, even components.