home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Runimage / Delphi50 / Source / Rtl / Sys / COMSERV.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-08-11  |  10.6 KB  |  411 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Borland Delphi Runtime Library                  }
  5. {       COM server support                              }
  6. {                                                       }
  7. {       Copyright (C) 1997,99 Inprise Corporation       }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11. unit ComServ;
  12.  
  13. {$DENYPACKAGEUNIT}
  14.  
  15. interface
  16.  
  17. uses Windows, Messages, ActiveX, SysUtils, ComObj;
  18.  
  19. type
  20.  
  21. { Application start mode }
  22.  
  23.   TStartMode = (smStandalone, smAutomation, smRegServer, smUnregServer);
  24.  
  25. { Class manager event types }
  26.  
  27.   TLastReleaseEvent = procedure(var Shutdown: Boolean) of object;
  28.  
  29. { TComServer }
  30.  
  31.   TComServer = class(TComServerObject)
  32.   private
  33.     FObjectCount: Integer;
  34.     FFactoryCount: Integer;
  35.     FTypeLib: ITypeLib;
  36.     FServerName: string;
  37.     FHelpFileName: string;
  38.     FIsInprocServer: Boolean;
  39.     FStartMode: TStartMode;
  40.     FStartSuspended: Boolean;
  41.     FRegister: Boolean;
  42.     FUIInteractive: Boolean;
  43.     FOnLastRelease: TLastReleaseEvent;
  44.     procedure FactoryFree(Factory: TComObjectFactory);
  45.     procedure FactoryRegisterClassObject(Factory: TComObjectFactory);
  46.     procedure FactoryUpdateRegistry(Factory: TComObjectFactory);
  47.     procedure LastReleased;
  48.   protected
  49.     function CountObject(Created: Boolean): Integer; override;
  50.     function CountFactory(Created: Boolean): Integer; override;
  51.     function GetHelpFileName: string; override;
  52.     function GetServerFileName: string; override;
  53.     function GetServerKey: string; override;
  54.     function GetServerName: string; override;
  55.     function GetStartSuspended: Boolean; override;
  56.     function GetTypeLib: ITypeLib; override;
  57.     procedure SetHelpFileName(const Value: string); override;
  58.   public
  59.     constructor Create;
  60.     destructor Destroy; override;
  61.     procedure Initialize;
  62.     procedure LoadTypeLib;
  63.     procedure SetServerName(const Name: string);
  64.     procedure UpdateRegistry(Register: Boolean);
  65.     property IsInprocServer: Boolean read FIsInprocServer write FIsInprocServer;
  66.     property ObjectCount: Integer read FObjectCount;
  67.     property StartMode: TStartMode read FStartMode;
  68.     property UIInteractive: Boolean read FUIInteractive write FUIInteractive;
  69.     property OnLastRelease: TLastReleaseEvent read FOnLastRelease write FOnLastRelease;
  70.   end;
  71.  
  72. var
  73.   ComServer: TComServer;
  74.  
  75. function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult; stdcall;
  76. function DllCanUnloadNow: HResult; stdcall;
  77. function DllRegisterServer: HResult; stdcall;
  78. function DllUnregisterServer: HResult; stdcall;
  79.  
  80. implementation
  81.  
  82. uses ComConst;
  83.  
  84. function GetModuleFileName: string;
  85. var
  86.   Buffer: array[0..261] of Char;
  87. begin
  88.   SetString(Result, Buffer, Windows.GetModuleFileName(HInstance,
  89.     Buffer, SizeOf(Buffer)));
  90. end;
  91.  
  92. function GetModuleName: string;
  93. begin
  94.   Result := ChangeFileExt(ExtractFileName(GetModuleFileName), '');
  95. end;
  96.  
  97. function LoadTypeLibrary(const ModuleName: string): ITypeLib;
  98. begin
  99.   OleCheck(LoadTypeLib(PWideChar(WideString(ModuleName)), Result));
  100. end;
  101.  
  102. procedure RegisterTypeLibrary(TypeLib: ITypeLib; const ModuleName: string);
  103. var
  104.   Name: WideString;
  105.   HelpPath: WideString;
  106. begin
  107.   Name := ModuleName;
  108.   HelpPath := ExtractFilePath(ModuleName);
  109.   OleCheck(RegisterTypeLib(TypeLib, PWideChar(Name), PWideChar(HelpPath)));
  110. end;
  111.  
  112. procedure UnregisterTypeLibrary(TypeLib: ITypeLib);
  113. type
  114.   TUnregisterProc = function(const GUID: TGUID; VerMajor, VerMinor: Word;
  115.     LCID: TLCID; SysKind: TSysKind): HResult stdcall;
  116. var
  117.   Handle: THandle;
  118.   UnregisterProc: TUnregisterProc;
  119.   LibAttr: PTLibAttr;
  120. begin
  121.   Handle := GetModuleHandle('OLEAUT32.DLL');
  122.   if Handle <> 0 then
  123.   begin
  124.     @UnregisterProc := GetProcAddress(Handle, 'UnRegisterTypeLib');
  125.     if @UnregisterProc <> nil then
  126.     begin
  127.       OleCheck(ComServer.TypeLib.GetLibAttr(LibAttr));
  128.       with LibAttr^ do
  129.         UnregisterProc(guid, wMajorVerNum, wMinorVerNum, lcid, syskind);
  130.       ComServer.TypeLib.ReleaseTLibAttr(LibAttr);
  131.     end;
  132.   end;
  133. end;
  134.  
  135. function GetTypeLibName(TypeLib: ITypeLib): string;
  136. var
  137.   Name: WideString;
  138. begin
  139.   OleCheck(TypeLib.GetDocumentation(-1, @Name, nil, nil, nil));
  140.   Result := Name;
  141. end;
  142.  
  143. function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult;
  144. var
  145.   Factory: TComObjectFactory;
  146. begin
  147.   Factory := ComClassManager.GetFactoryFromClassID(CLSID);
  148.   if Factory <> nil then
  149.     if Factory.GetInterface(IID, Obj) then
  150.       Result := S_OK
  151.     else
  152.       Result := E_NOINTERFACE
  153.   else
  154.   begin
  155.     Pointer(Obj) := nil;
  156.     Result := CLASS_E_CLASSNOTAVAILABLE;
  157.   end;
  158. end;
  159.  
  160. function DllCanUnloadNow: HResult;
  161. begin
  162.   if (ComServer = nil) or
  163.     ((ComServer.FObjectCount = 0) and (ComServer.FFactoryCount = 0)) then
  164.     Result := S_OK
  165.   else
  166.     Result := S_FALSE;
  167. end;
  168.  
  169. function DllRegisterServer: HResult;
  170. begin
  171.   Result := S_OK;
  172.   try
  173.     ComServer.UpdateRegistry(True);
  174.   except
  175.     Result := E_FAIL;
  176.   end;
  177. end;
  178.  
  179. function DllUnregisterServer: HResult;
  180. begin
  181.   Result := S_OK;
  182.   try
  183.     ComServer.UpdateRegistry(False);
  184.   except
  185.     Result := E_FAIL;
  186.   end;
  187. end;
  188.  
  189. { Automation TerminateProc }
  190.  
  191. function AutomationTerminateProc: Boolean;
  192. begin
  193.   Result := True;
  194.   if (ComServer <> nil) and (ComServer.ObjectCount > 0) and ComServer.UIInteractive then
  195.   begin
  196.     Result := MessageBox(0, PChar(SNoCloseActiveServer1 + SNoCloseActiveServer2),
  197.       PChar(SAutomationWarning), MB_YESNO or MB_TASKMODAL or
  198.       MB_ICONWARNING or MB_DEFBUTTON2) = IDYES;
  199.   end;
  200. end;
  201.  
  202. { TComServer }
  203.  
  204. constructor TComServer.Create;
  205.  
  206.   function FindSwitch(const Switch: string): Boolean;
  207.   begin
  208.     Result := FindCmdLineSwitch(Switch, ['-', '/'], True);
  209.   end;
  210.  
  211. begin
  212.   FTypeLib := nil;
  213.   FIsInprocServer := ModuleIsLib;
  214.   if FindSwitch('AUTOMATION') or FindSwitch('EMBEDDING') then
  215.     FStartMode := smAutomation
  216.   else if FindSwitch('REGSERVER') then
  217.     FStartMode := smRegServer
  218.   else if FindSwitch('UNREGSERVER') then
  219.     FStartMode := smUnregServer;
  220.   FUIInteractive := True;
  221. end;
  222.  
  223. destructor TComServer.Destroy;
  224. begin
  225.   ComClassManager.ForEachFactory(Self, FactoryFree);
  226. end;
  227.  
  228. function TComServer.CountObject(Created: Boolean): Integer;
  229. begin
  230.   if Created then
  231.   begin
  232.     Result := InterlockedIncrement(FObjectCount);
  233.     if (not IsInProcServer) and (StartMode = smAutomation)
  234.       and Assigned(ComObj.CoAddRefServerProcess) then
  235.       ComObj.CoAddRefServerProcess;
  236.   end
  237.   else
  238.   begin
  239.     Result := InterlockedDecrement(FObjectCount);
  240.     if (not IsInProcServer) and (StartMode = smAutomation)
  241.       and Assigned(ComObj.CoReleaseServerProcess) then
  242.     begin
  243.       if ComObj.CoReleaseServerProcess = 0 then
  244.         LastReleased;
  245.     end
  246.     else if Result = 0 then
  247.       LastReleased;
  248.   end;
  249. end;
  250.  
  251. function TComServer.CountFactory(Created: Boolean): Integer;
  252. begin
  253.   if Created then
  254.     Result := InterlockedIncrement(FFactoryCount)
  255.   else
  256.     Result := InterlockedDecrement(FFactoryCount);
  257. end;
  258.  
  259. procedure TComServer.FactoryFree(Factory: TComObjectFactory);
  260. begin
  261.   Factory.Free;
  262. end;
  263.  
  264. procedure TComServer.FactoryRegisterClassObject(Factory: TComObjectFactory);
  265. begin
  266.   Factory.RegisterClassObject;
  267. end;
  268.  
  269. procedure TComServer.FactoryUpdateRegistry(Factory: TComObjectFactory);
  270. begin
  271.   if Factory.Instancing <> ciInternal then
  272.     Factory.UpdateRegistry(FRegister);
  273. end;
  274.  
  275. function TComServer.GetHelpFileName: string;
  276. begin
  277.   Result := FHelpFileName;
  278. end;
  279.  
  280. function TComServer.GetServerFileName: string;
  281. begin
  282.   Result := GetModuleFileName;
  283. end;
  284.  
  285. function TComServer.GetServerKey: string;
  286. begin
  287.   if FIsInprocServer then
  288.     Result := 'InprocServer32' else
  289.     Result := 'LocalServer32';
  290. end;
  291.  
  292. function TComServer.GetServerName: string;
  293. begin
  294.   if FServerName <> '' then
  295.     Result := FServerName
  296.   else
  297.     if FTypeLib <> nil then
  298.       Result := GetTypeLibName(FTypeLib)
  299.     else
  300.       Result := GetModuleName;
  301. end;
  302.  
  303. procedure TComServer.SetServerName(const Name: string);
  304. begin
  305.   if FTypeLib = nil then
  306.     FServerName := Name;
  307. end;
  308.  
  309. function TComServer.GetTypeLib: ITypeLib;
  310. begin
  311.   LoadTypeLib;
  312.   Result := FTypeLib;
  313. end;
  314.  
  315. procedure TComServer.Initialize;
  316. begin
  317.   try
  318.     UpdateRegistry(FStartMode <> smUnregServer);
  319.   except
  320.     on E: EOleRegistrationError do
  321.       // User may not have write access to the registry.
  322.       // Squelch the exception unless we were explicitly told to register.
  323.       if FStartMode = smRegServer then raise;
  324.   end;
  325.   if FStartMode in [smRegServer, smUnregServer] then Halt;
  326.   ComClassManager.ForEachFactory(Self, FactoryRegisterClassObject);
  327. end;
  328.  
  329. procedure TComServer.LastReleased;
  330. var
  331.   Shutdown: Boolean;
  332. begin
  333.   if not FIsInprocServer then
  334.   begin
  335.     Shutdown := FStartMode = smAutomation;
  336.     try
  337.       if Assigned(FOnLastRelease) then FOnLastRelease(Shutdown);
  338.     finally
  339.       if Shutdown then PostThreadMessage(MainThreadID, WM_QUIT, 0, 0);
  340.     end;
  341.   end;
  342. end;
  343.  
  344. procedure TComServer.LoadTypeLib;
  345. var
  346.   Temp: ITypeLib;
  347. begin
  348.   if FTypeLib = nil then
  349.   begin
  350.   // this may load typelib more than once, but avoids need for critical section
  351.   // and releases the interface correctly
  352.     Temp := LoadTypeLibrary(GetModuleFileName);
  353.     Integer(Temp) := InterlockedExchange(Integer(FTypeLib), Integer(Temp));
  354.   end;
  355. end;
  356.  
  357. procedure TComServer.UpdateRegistry(Register: Boolean);
  358. begin
  359.   if FTypeLib <> nil then
  360.     if Register then
  361.       RegisterTypeLibrary(FTypeLib, GetModuleFileName) else
  362.       UnregisterTypeLibrary(FTypeLib);
  363.   FRegister := Register;
  364.   ComClassManager.ForEachFactory(Self, FactoryUpdateRegistry);
  365. end;
  366.  
  367. var
  368.   SaveInitProc: Pointer = nil;
  369.   OleAutHandle: Integer;
  370.  
  371. procedure InitComServer;
  372. begin
  373.   if SaveInitProc <> nil then TProcedure(SaveInitProc);
  374.   ComServer.FStartSuspended := (CoInitFlags <> -1) and
  375.     Assigned(ComObj.CoInitializeEx) and Assigned(ComObj.CoResumeClassObjects);
  376.   ComServer.Initialize;
  377.   if ComServer.FStartSuspended then
  378.     ComObj.CoResumeClassObjects;
  379. end;
  380.  
  381. function TComServer.GetStartSuspended: Boolean;
  382. begin
  383.   Result := FStartSuspended;
  384. end;
  385.  
  386. procedure TComServer.SetHelpFileName(const Value: string);
  387. begin
  388.   FHelpFileName := Value;
  389. end;
  390.  
  391. initialization
  392. begin
  393.   OleAutHandle := SafeLoadLibrary('OLEAUT32.DLL');
  394.   ComServer := TComServer.Create;
  395.   if not ModuleIsLib then
  396.   begin
  397.     SaveInitProc := InitProc;
  398.     InitProc := @InitComServer;
  399.     AddTerminateProc(@AutomationTerminateProc);
  400.   end;
  401. end;
  402.  
  403. finalization
  404. begin
  405.   ComServer.Free;
  406.   ComServer := nil;
  407.   FreeLibrary(OleAutHandle);
  408. end;
  409.  
  410. end.
  411.