InProc Server

An InProc server implements IClassFactory, DllGetClassObject() and DllCanUnloadNow(). The boiler plate .def file Visual Studio generates in an ATL project looks like this:

LIBRARY

EXPORTS
	DllGetClassObject		PRIVATE
	DllCanUnloadNow			PRIVATE
	DllRegisterServer		PRIVATE
	DllUnregisterServer		PRIVATE

A client application will typically instantiate a COM object with CoCreateInstance() if it needs a single instance.

Note that DllGetClassObject() does not directly create objects, as the name would imply. Instead, is called with IID_IClassFactory to get a factory object. pFactory->CreateInstance() is then called to create the object. This all happens behind the scenes when you call CoCreateInstance.

int
main ()
{
    ODS0 ("[HelloClient]\tentering main\n");

    // initialize COM libraries
    HRESULT hr = CoInitialize ((LPVOID) 0);

    // convert ProgID into corresponding CLSID
    CLSID CLSID_HelloWorldServer;
    hr = CLSIDFromProgID (
        L"HandsOnCOM.HelloWorld.1",
        & CLSID_HelloWorldServer
    );

    // create a HelloWorld object
    IUnknown * pIUnknown;
    hr = CoCreateInstance (
        CLSID_HelloWorldServer,  // CLSID of object
        (IUnknown *) 0, // object is not part of an aggregate

#if ! defined (OUTPROC_SERVER)
        CLSCTX_INPROC_SERVER, // see CLSCTX enumeration
#else
        CLSCTX_LOCAL_SERVER,  // see CLSCTX enumeration
#endif

        IID_IUnknown,  // requested interface identifier
        (void **) & pIUnknown  // return value
    );

    // request ISayHello interface
    ISayHello * pISayHello;
    hr = pIUnknown -> QueryInterface (
        IID_ISayHello,
        (void **) & pISayHello
    );

    // allocate a BSTR
    OLECHAR szTemp[] = L"Hello World!";
    BSTR szHello = SysAllocString (szTemp);

    // invoke method SayHello
#if defined (TEST_PERFORMANCE)
    long dwMilliSeconds = GetTickCount ();
    long dwRetries = TEST_ITERATIONEN;

    for (int i = 0; i < dwRetries; i ++)
        hr = pISayHello -> SayHello (szHello);

    ODS2 ("[HelloClient]\t%ld calls: %ld milliseconds\n", dwRetries, GetTickCount () - dwMilliSeconds);
#else
    ODS0 ("[HelloClient]\tvor SayHello\n");
    hr = pISayHello -> SayHello (szHello);
    ODS0 ("[HelloClient]\tnach SayHello\n");

#endif

    // release interface pointer
    hr = pISayHello -> Release ();
    hr = pIUnknown -> Release ();

    // free BSTR
    SysFreeString (szHello);

    // release COM libraries
    CoUninitialize ();

    MessageBox ((HWND) 0, "Good bye", "Client", MB_OK);
    ODS0 ("[HelloClient]\tleaving main\n");
    return 0;
}

If multiple objects are needed, it will call CoGetClassObject() to get an IClassFactory and call CreateInstance() multiple times.

int
main ()
{
    // initialize COM libraries
    HRESULT hr = CoInitialize ((LPVOID) 0);

    // convert ProgID into corresponding CLSID
    CLSID CLSID_HelloWorldServer;
    hr = CLSIDFromProgID (
        L"HandsOnCOM.HelloWorld.1",
        & CLSID_HelloWorldServer
    );

    // retrieve class factory interface pointer
    IClassFactory * pIClassFactory;
    hr = CoGetClassObject (
        CLSID_HelloWorldServer, // CLSID of class object

#if ! defined (OUTPROC_SERVER)
        CLSCTX_INPROC_SERVER,   // see CLSCTX enumeration
#else
        CLSCTX_LOCAL_SERVER,    // see CLSCTX enumeration
#endif

        (COSERVERINFO *) 0,     // no remote machine info
        IID_IClassFactory,      // requested interface identifier
        (void **) & pIClassFactory  // return value
    );

    // create two HelloWorld objects
    ISayHello * pISayHello1;
    hr = pIClassFactory -> CreateInstance (
        (IUnknown *) 0, // object is not part of an aggregate
        IID_ISayHello,  // requested interface identifier
        (void **) & pISayHello1 // return value
    );

    ISayHello * pISayHello2;
    hr = pIClassFactory -> CreateInstance (
        (IUnknown *) 0, // object is not part of an aggregate
        IID_ISayHello,  // requested interface identifier
        (void **) & pISayHello2 // return value
    );

    // allocate two BSTRs
    OLECHAR szTemp1[] = L"Say hello to our first object!";
    OLECHAR szTemp2[] = L"Say hello to our second object!";
    BSTR szHello1 = SysAllocString (szTemp1);
    BSTR szHello2 = SysAllocString (szTemp2);

    // invoke method SayHello from first object
    hr = pISayHello1 -> SayHello (szHello1);

    // invoke method SayHello from second object
    hr = pISayHello2 -> SayHello (szHello2);

    // release interface pointer of objects
    hr = pISayHello1 -> Release ();
    hr = pISayHello2 -> Release ();

    // free BSTRs
    SysFreeString (szHello1);
    SysFreeString (szHello2);

    // release class factory
    hr = pIClassFactory -> Release ();

    // release COM libraries
    CoUninitialize ();

    MessageBox ((HWND) 0, "Good bye", "Client", MB_OK);
    return 0;
}

Occasionally, an InProc server may have multiple apartments. See Apartment Models.

Not recommended, but just to complete the thought - it is possible to load an inproc server without using the registry or SCM.

IUnknown* pUnknown;
IClassFactory* pClassFactory;

// Load the DLL.
HINSTANCE myDLL = LoadLibrary("C:\\component.dll");

// Declare a pointer to the DllGetClassObject function.
typedef HRESULT (____stdcall *PFNDLLGETCLASSOBJECT)(REFCLSID clsid, 
    REFIID riid, void** ppv);

// Get a pointer to the component's DllGetClassObject function.
PFNDLLGETCLASSOBJECT DllGetClassObject = 
    (PFNDLLGETCLASSOBJECT)GetProcAddress(myDLL, "DllGetClassObject");

// Call DllGetClassObject to get a pointer to the class factory.
DllGetClassObject(CLSID_InsideCOM, IID_IClassFactory, (void**)&pClassFactory);
// IClassFactory::CreateInstance and IUnknown::Release
pClassFactory->CreateInstance(NULL, IID_IUnknown, (void**)&pUnknown);
pClassFactory->Release();

References