Next: , Previous: , 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: , Previous: , Up: Creating COM objects with GNAT   [Contents][Index]