Next: LocalServer container implementation, Previous: The COM object implementation, Up: Creating COM objects with GNAT [Contents][Index]

### 5.2 InProc container implementation

Step 1 - The export file

Every InProc container (DLL housing a COM object), needs to export four functions in its export (.def) file.

The def file must start exactly like this, but may contain additional exports following these four:

    EXPORTS
       DllGetClassObject=DllGetClassObject@12     @2
       DllCanUnloadNow=DllCanUnloadNow@0          @3
       DllRegisterServer=DllRegisterServer@0      @4
       DllUnregisterServer=DllUnregisterServer@0  @5

Step 2 - The dll implementation

In addition to the four exports mentioned every DLL must contain an exported `DllMain` procedure. The implementation for the `DllMain` will be the same for all objects, unless additional functionality is needed on the containers startup or shutdown:

    function DllMain (hinstDLL    : Win32.Windef.HINSTANCE;
                      fdwReason   : Win32.DWORD;
                      lpvReserved : Win32.LPVOID)
      return Win32.BOOL
    is
       procedure Adainit;
       pragma Import (C, Adainit);

       procedure Adafinal;
       pragma Import (C, Adafinal);

       DLL_PROCESS_DETACH : constant := 0;
       DLL_PROCESS_ATTACH : constant := 1;
    begin
       case fdwReason is
          when DLL_PROCESS_ATTACH =>
             AdaInit;
             hInstance := hinstDll;
             return 1;

          when DLL_PROCESS_DETACH =>
             AdaFinal;
             return 1;

          when others =>
             return 1;
       end case;
    end DllMain;

The Instance handle assigned to the Dll is save in a variable `hInstance` for use by the exported COM Dll functions.

The `DllGetClassObject` is used by COM to request from the Dll a pointer to the factory object for a particular type of COM object. The implementation needs to check that it does in fact support creation of those objects and then needs to return a pointer to the class factory that will produces objects of the requested type. See the implementation in the `gnatexample-dll.adb` file for an example.

The `DllCanUnloadNow` function is called periodically by the operating system to determine if the Dll can be unloaded or needs to remain in memory. The Dll needs to remain in memory and returns `S_FALSE` if there are any server locks or if there are any created objects still being used. For example:

    function DllCanUnloadNow return HRESULT is
       use type Interfaces.C.Long;
    begin
       if Server_Lock_Count = 0 and then Component_Count = 0 then
          return Win32.Winerror.S_OK;
       else
          return Win32.Winerror.S_FALSE;
       end if;
    end DllCanUnloadNow;

The functions `DllRegisterServer` and `DllUnregisterServer` are called by the application `regsvr32.exe` (or an install program) to request that the Dll add or remove all the relevant COM entries of the registry for its supported objects and type libraries.

The first step in `DllRegisterServer` is to register the type libraries. This is done by first obtaining the full path file name of the Dll. Using this path, the Win32 APIs `LoadTypeLib` and `RegisterTypeLib` are used to load and register the type library. Then the following entries are registered to the NT registry under `HKEY_CLASSES_ROOT` (using a simple helper procedure in the example file):

    Register ("CLSID\" & CLSID, "", Library_Description);
    --  CLSID = CLASS ID of the COM Object (CoClass GUID)
    --  Library_Description = Description of object to appear in COM
    --  browsers

    Register ("CLSID\" & CLSID, "AppID", CLSID);
    --  This entry links together the COM information (under \CLSID)
    --  with the remoting information for DCOM (under \APPID)

    Register ("CLSID\" & CLSID & "\InProcServer32",
              "",
              C.To_Ada (DllPath));
    --  Path to InProcServer 32 bit implementation of the COM object
    --  (a DLL)

    Register ("CLSID\" & CLSID & "\InProcServer32",
              "ThreadingModel",
              "Apartment");
    --  Tells the operating system that this object is aware of
    --  threading issues, but the OS should synchronize access to
    --  the object.
    --  This does not affect the type of clients that can use this
    --  object,ie. a client usingCoInitializeEx(CLSCTX_MULTI_THREADED)
    --  can create this object and the OS will handle synchronization
    --  of calls made by each thread trying to access the object.

    Register ("CLSID\" & CLSID & "\ProgID",
              "",
              Object_Name & "." & Library_Version);
    --  Links the CLSID to the PROGID
    --  The ProgID is the human readable name of the COM object used
    --  by VB and other languages instead of the GUID.
    --  The PROGID also contains a dot followed by the version number.

    Register ("CLSID\" & CLSID & "\VersionIndependentProgID",
              "",
              Object_Name);
    --  Links the CLSID to the PROGID with out the version number

    Register (Object_Name, "", Library_Description);
    --  Creates the version independant PROGID entry

    Register (Object_Name & "\CLSID", "", CLSID);
    --  Links the PROGID to the CLSID of the object

    Register (Object_Name & "\CurVer",
              "",
              Object_Name & "." & Library_Version);
    --  Links the version independant PROGID to the version
    --  dependant PROGID

    Register (Object_Name & "." & Library_Version,
              "",
              Object_Name);
    --  Creates the PROGID entry

    Register (Object_Name & "." & Library_Version & "\CLSID",
              "",
              CLSID);
    --  Links the PROGID to the CLSID

    Register ("AppID\" & CLSID, "", Library_Description);
    --  Creates the AppID entry where remoting infromation about the
    --  COM object is stored.

    Register ("AppID\" & CLSID, "DllSurrogate", "");
    --  Tells the opearting system that if a request is made to run
    --  the COM object as a DCOM object or as a separate process
    --  (LocalServer) to provide this for the dll using a built in NT
    --  application DLLHOST.EXE

The `DllUnregisterServer` function first removes the registered type library using the Win32 API function `UnregisterTypeLib`, it then removes all the entries in the NT registery created by `DllRegisterServer`.

Next: LocalServer container implementation, Previous: The COM object implementation, Up: Creating COM objects with GNAT [Contents][Index]