• COM线程池


    //  TThreadedClassFactory.Create (ComServer, TsvrDM, CLASS_Test,   // create com thread pooling
    //    ciMultiInstance);
    unit ThreadComLib;

    {$IFDEF VER100}
    {$DEFINE D3}
    {$ENDIF}

    interface

    uses
      Windows,
      ActiveX,
      Classes,
      ComObj,
      Controls,
      ExtCtrls,
      Grids,
      Variants,
      VCLCom, forms
      ;

    { General COM threading types }
    type
      { apartment types }
      TApartmentType = (atSTA, atMTA);

    { Win32 thread synchronization classes }

    type
      TWaitResult = (wrSignaled, wrTimeout, wrAbandoned, wrError);

      TThreadSyncObject = class
      protected
        FHandle: THandle;
        function GetLastError: longint;
      public
        destructor Destroy; override;
        procedure Acquire; virtual;
        function Lock (bLock: boolean): boolean; virtual;
        procedure Release; virtual;
        function WaitFor (iTimeout: dword): TWaitResult; virtual;
        property Handle: THandle read FHandle;
        property LastError: longint read GetLastError;
      end;

      TCriticalSection = class (TThreadSyncObject)
      protected
        FCS: TRTLCriticalSection;
      public
        constructor Create;
        destructor Destroy; override;
        procedure Enter;
        function Lock (bLock: boolean): boolean; override;
        procedure Leave;
      end;

      TEvent = class (TThreadSyncObject)
      public
        constructor Create (psa: PSecurityAttributes;
          bManualReset, bInitState: boolean; const sName: string);
        constructor CreateSimple;
        function PulseEvent: boolean;
        function ResetEvent: boolean;
        function SetEvent: boolean;
      end;

      { Message queue driven thread }
      TMQThread = class (TThread)
      protected
        FQuitWaitTimeout: integer;
        FReadyEvent: TEvent;
        procedure EnsureMessageQueue; virtual;
        function ProcessMessage (var rMsg: TMsg): boolean; virtual;
      public
        constructor Create (bSuspend: boolean);
        destructor Destroy; override;
        procedure Execute; override;
        function Quit (bWait: boolean): boolean; virtual;
        procedure SignalReady; virtual;
        procedure WaitForQuit; virtual;
        function WaitForReady: boolean; virtual;
        property QuitWaitTimeout: integer read FQuitWaitTimeout write FQuitWaitTimeout;
      end;

      { encapsulates CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream }
      TObjectMarshaler = class
      protected
        FMarshalIID: TGUID;
        FStream: pointer;
        procedure ReleaseStream;
      public
        constructor CreateMarshalObject (const iid: TGUID; const pUnk: IUnknown);
        constructor CreateMarshalOleObject (const vObj: olevariant);
        destructor Destroy; override;
        function MarshalObject (const iid: TGUID; const pUnk: IUnknown): boolean;
        function MarshalOleObject (const vObj: olevariant): boolean;
        function UnMarshalObject (out pObj): boolean;
        function UnMarshalOleObject (out vObj: olevariant): boolean;
      end;

      TApartmentObjectMarshaler = TObjectMarshaler;  // backward compatibility

      { server threading models }
      {$IFDEF D3}
      TThreadingModel = (tmSingle, tmApartment, tmFree, tmBoth);
      {$ENDIF}

      { STA allocation modes }
      TSTAAllocMode = (amDefault, amPooled, amDistinct);

      { abstract apartment manager classes }

      { forwards }
      TApartment = class;
      TApartments = class;
      TApartmentThread = class;
      TApartmentThreads = class;
      TApartmentObject = class;
      TApartmentAllocator = class;

      TFuncFactoryCreateInstance = function (
        OwnerApartment: TApartment; const UnkOuter: IUnknown; const iid: TGUID; out pObject
      ): HResult of object; stdcall;

      { abstract apartment "manager" class. allows TApartment classes to be reused! }
      TApartmentManagerObject = class
      protected
        FAllocator: TApartmentAllocator;
        function GetAllocator: TApartmentAllocator; virtual;
        function GetApartments: TApartments; virtual; abstract;
        function GetServerObjectCount: integer; virtual;
      public
        destructor Destroy; override;
        function CanCreateInstance (var hr: HResult): boolean; virtual;
        function CreateInstance (
          cf: TComObjectFactory; pfnci: TFuncFactoryCreateInstance;
          tm: TThreadingModel; am: TSTAAllocMode; pUnkOuter: IUnknown;
          const iid: TGUID; out pObject): HResult; virtual;
        procedure GarbageCollect (Sender: TObject); virtual; abstract;
        function GetPooledSTACount: integer; virtual; abstract;
        procedure LastReleased (var bShutdown: boolean); virtual;
        procedure Resume; virtual;
        function ServerIsShuttingDown: boolean; virtual; abstract;
        property Allocator: TApartmentAllocator read GetAllocator;
        property Apartments: TApartments read GetApartments;
        property ServerObjectCount: integer read GetServerObjectCount;
      end;

      { apartments garbage collector }
      TApartmentsGC = class;
      TApartmentsGCThread = class (TMQThread)
      protected
        FOwner: TApartmentsGC;
        function GetManager: TApartmentManagerObject;
        function ProcessMessage (var rMsg: TMsg): boolean; override;
      public
        constructor Create (Owner: TApartmentsGC; tp: TThreadPriority); virtual;
        procedure GarbageCollect (Sender: TObject); virtual;
        property Manager: TApartmentManagerObject read GetManager;
      end;

      TApartmentsGC = class
      protected
        FManager: TApartmentManagerObject;
        FThread: TApartmentsGCThread;
        procedure InitializeThread (tp: TThreadPriority); virtual;
      public
        constructor Create (am: TApartmentManagerObject; tp: TThreadPriority);
        destructor Destroy; override;
        procedure Activate (Sender: TObject); virtual;
        procedure Terminate; virtual;
        property Manager: TApartmentManagerObject read FManager;
        property Thread: TApartmentsGCThread read FThread;
      end;

      { create instance info structure }
      PCreateInstanceInfo = ^TCreateInstanceInfo;
      TCreateInstanceInfo = record
        ApartmentObject: TApartmentObject;
        ApartmentThread: TApartmentThread;
        Data: pointer;
      end;

      { base create instance data structure }
      PCreateInstanceData = ^TCreateInstanceData;
      TCreateInstanceData = record
        IsThreadedComClass: boolean;
      end;

      { helper class to handle marshaled creation of objects out of apartments }
      TApartmentObject = class
      protected
        FCreateIID: TGUID;
        FCreateInstanceFunc: TFuncFactoryCreateInstance;
        FCreateResult: HResult;
        FCreateStream: pointer;
        procedure Clear; virtual;
        function CreateOnThread (pciInfo: PCreateInstanceInfo): boolean; virtual;
        function GetInstance (out pObject): HResult; virtual;
        function MarshalInterface (const iid: TGUID; pUnk: IUnknown): HResult; virtual;
        function UnmarshalInterface (const iid: TGUID; out pObject): HResult; virtual;
      public
        constructor Create (
          pCreateInstance: TFuncFactoryCreateInstance; const iid: TGUID); virtual;
        destructor Destroy; override;
        function CreateInApartmentThread (pciInfo: PCreateInstanceInfo; out pObject): HResult; virtual;
      end;

      { base apartment thread class, handles both STA and MTA }
      TApartmentThreadClass = class of TApartmentThread;
      TApartmentThread = class (TMQThread)
      protected
        FOwner: TApartmentThreads;
        FServerWindow: HWnd;
        function GetApartment: TApartment; virtual;
        procedure InitServerWindow (bInit: boolean); virtual;
        procedure InstanceCreated (pciInfo: PCreateInstanceInfo; pUnk: IUnknown); virtual;
        function ProcessMessage (var rMsg: TMsg): boolean; override;
      public
        constructor Create (Owner: TApartmentThreads); virtual;
        destructor Destroy; override;
        function CreateInstance (pCreateInstance: TFuncFactoryCreateInstance;
          const iid: TGUID; pData: pointer; out pObject): HResult; virtual;
        procedure Execute; override;
        function Quit (bWait: boolean): boolean; override;
        property Apartment: TApartment read GetApartment;
        property ServerWindow: HWnd read FServerWindow;
      end;

      { collection of threads per apartment }
      TApartmentThreads = class
      protected
        FCSThreads: TCriticalSection;
        FOwner: TApartment;
        FThreads: TList;
        function CreateThread: TApartmentThread; virtual;
        function GetItemById (iThreadId: integer): TApartmentThread;
        function GetItems (i: integer): TApartmentThread;
        procedure LockThreads (bLock: boolean); virtual;
      public
        constructor Create (Owner: TApartment); virtual;
        destructor Destroy; override;
        function AddThread (at: TApartmentThread): integer; virtual;
        procedure Clear; virtual;
        function Count: integer; virtual;
        function NewThread: TApartmentThread; virtual;
        function Terminate: boolean; virtual;
        property Apartment: TApartment read FOwner;
        property ItemById [iThreadId: integer]: TApartmentThread read GetItemById;
        property Items [i: integer]: TApartmentThread read GetItems; default;
      end;

      { COM apartment abstraction class }
      TApartmentClass = class of TApartment;
      TApartment = class
      protected
        FApartmentType: TApartmentType;
        FAutoDelete: boolean;
        FCSRefCount: TCriticalSection;
        FData: pointer;
        FLockCount: integer;
        FMarkForDelete: boolean;
        FName: string;
        FOwner: TApartments;
        FPooled: boolean;
        FThreads: TApartmentThreads;
        function CreateThreads: TApartmentThreads; virtual;
        function GetLockCount: integer; virtual;
        function GetManager: TApartmentManagerObject; virtual;
        procedure LastReleased; virtual;
        procedure SetAutoDelete (bSet: boolean); virtual;
        procedure SetPooled (bSet: boolean); virtual;
      public
        constructor Create (Owner: TApartments; at: TApartmentType); virtual;
        destructor Destroy; override;
        function CreateInstance (pCreateInstance: TFuncFactoryCreateInstance;
          const iid: TGUID; pData: pointer; out pObject): HResult; virtual;
        function CountObject (bLock: boolean): integer; virtual;
        function GarbageCollect: boolean; virtual;
        procedure LockRefCount (bLock: boolean); virtual;
        function TerminateThreads: boolean; virtual;
        property ApartmentType: TApartmentType read FApartmentType write FApartmentType;
        property AutoDelete: boolean read FAutoDelete write SetAutoDelete;
        property Data: pointer read FData write FData;  // any user defined object/data
        property LockCount: integer read GetLockCount;
        property Manager: TApartmentManagerObject read GetManager;
        property MarkForDelete: boolean read FMarkForDelete write FMarkForDelete;
        property Name: string read FName write FName;
        property ObjectCount: integer read GetLockCount;
        property Pooled: boolean read FPooled write SetPooled;
        property Threads: TApartmentThreads read FThreads;
      end;

      { collection of apartments per server }
      TApartments = class
      protected
        FApartments: TList;
        FCSApartments: TCriticalSection;
        FManager: TApartmentManagerObject;
        function CreateApartment (at: TApartmentType): TApartment; virtual;
        function GetActiveApartment: TApartment; virtual;
        function GetActiveApartmentName: string; virtual;
        function GetItemByName (const sName: string): TApartment; virtual;
        function GetItems (i: integer): TApartment;
        function GetLockCount: integer; virtual;
        function GetObjects (i: integer): pointer;
        function GetPooledCount: integer; virtual;
        procedure LockApartments (bLock: boolean);
        procedure SetObjects (i: integer; pObj: pointer);
      public
        constructor Create (am: TApartmentManagerObject); virtual;
        destructor Destroy; override;
        procedure Clear; virtual;
        function Count: integer; virtual;
        function DeleteApartment (i: integer): boolean; virtual;
        procedure DeleteEmptyApartments; virtual;
        function EnsureMTA: TApartment; virtual;
        function FindMTA (var mta: TApartment): boolean; virtual;
        function GarbageCollect (iCount: integer): integer; virtual;
        function GetCurrentApartment (var apt: TApartment; var thrd: TApartmentThread): boolean; virtual;
        function HasMTA: boolean;
        function IndexOfApartment (apt: TApartment): integer; virtual;
        function NewApartment (const sName: string; at: TApartmentType; bCreateThread: boolean): TApartment; virtual;
        function NewPooledSTA: TApartment; virtual;
        function RemoveApartment (apt: TApartment): boolean; virtual;
        function SafeRemoveApartment (apt: TApartment): boolean;
        function TerminateThreads: boolean; virtual;

        property ActiveApartment: TApartment read GetActiveApartment;
        property ActiveApartmentName: string read GetActiveApartmentName;
        property ItemByName [const sName: string]: TApartment read GetItemByName;
        property Items [i: integer]: TApartment read GetItems; default;
        property LockCount: integer read GetLockCount;
        property Manager: TApartmentManagerObject read FManager;
        property Objects [i: integer]: pointer read GetObjects write SetObjects;  // each apartment can have a userdef object
        property ObjectCount: integer read GetLockCount;
        property PooledCount: integer read GetPooledCount;
      end;

      { base apartment allocator for server objects }
      TApartmentAllocator = class
      protected
        FApartmentIndex: integer;
        FManager: TApartmentManagerObject;
      public
        constructor Create (am: TApartmentManagerObject); virtual;
        destructor Destroy; override;
        function AllocateApartment (am: TSTAAllocMode): TApartment; virtual;
        property Manager: TApartmentManagerObject read FManager;
      end;

      { apartment statistics component }
      TApartmentStats = class (TComponent)
      protected
        FAlign: TAlign;
        FApartments: TApartments;
        FEnabled: boolean;
        FGrid: TStringGrid;
        FGridParent: TWinControl;
        FTimer: TTimer;
        FUpdateInterval: integer;
        FVisible: boolean;
        function GetGrid: TStringGrid;
        function GetTimer: TTimer;
        procedure Notification (cmp: TComponent; op: TOperation); override;
        procedure SetAlign (al: TAlign);
        procedure SetEnabled (bSet: boolean);
        procedure SetGridParent (ctlParent: TWinControl);
        procedure SetUpdateInterval (iValue: integer);
        procedure SetVisible (bSet: boolean);
        procedure TimerUpdate (Sender: TObject);
        procedure UpdateGrid;
        property Timer: TTimer read GetTimer;
      public
        constructor Create (pOwner: TComponent); override;
        destructor Destroy; override;
        property Apartments: TApartments read FApartments write FApartments;
        property Grid: TStringGrid read GetGrid;
      published
        property Align: TAlign read FAlign write SetAlign default alNone;
        property Enabled: boolean read FEnabled write SetEnabled default TRUE;
        property GridParent: TWinControl read FGridParent write SetGridParent;
        property UpdateInterval: integer
          read FUpdateInterval write SetUpdateInterval default 1000;
        property Visible: boolean read FVisible write SetVisible default TRUE;
      end;

      { TComObjectFactory replacement, enables apartment allocation }
      TThreadedComObjectFactory = class(TComObjectFactory, IClassFactory, IExternalConnection)
      protected
        { IClassFactory }
        function CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
          out Obj): HResult; virtual; stdcall;
        function LockServer(fLock: BOOL): HResult; stdcall;
        { IExternalConnection }
        function AddConnection (extconn: longint; reserved: longint): longint; stdcall;
        function ReleaseConnection (extconn: longint; reserved: longint; fLastReleaseCloses: BOOL): longint; stdcall;
      protected
        FRegisterClass: longint;
        FSTAAllocMode: TSTAAllocMode;
        FThreadingModel: TThreadingModel;  // duplication due to private FThreadingModel field
        function DoCreateInstance(pApt: TApartment;
          const UnkOuter: IUnknown; const IID: TGUID; out Obj): HResult; virtual; stdcall;
        procedure Initialize; virtual;
      public
        // backward compatibility
        constructor CreateThreaded (ComServer: TComServerObject; ComClass: TComClass;
          const ClassID: TGUID; const ClassName, Description: string;
          Instancing: TClassInstancing; tm: TThreadingModel);
        constructor CreateThreadedEx (ComServer: TComServerObject; ComClass: TComClass;
          const ClassID: TGUID; const ClassName, Description: string;
          Instancing: TClassInstancing; tm: TThreadingModel; am: TSTAAllocMode);
        {$IFDEF D3}
        constructor Create (ComServer: TComServerObject; ComClass: TComClass;
          const ClassID: TGUID; const ClassName, Description: string;
          Instancing: TClassInstancing);
        {$ELSE}
        constructor Create (ComServer: TComServerObject; ComClass: TComClass;
          const ClassID: TGUID; const ClassName, Description: string;
          Instancing: TClassInstancing); overload;
        constructor Create (ComServer: TComServerObject; ComClass: TComClass;
          const ClassID: TGUID; const ClassName, Description: string;
          Instancing: TClassInstancing; tm: TThreadingModel); overload;
        constructor Create (ComServer: TComServerObject; ComClass: TComClass;
          const ClassID: TGUID; const ClassName, Description: string;
          Instancing: TClassInstancing; tm: TThreadingModel; am: TSTAAllocMode); overload;
        {$ENDIF}
        destructor Destroy; override;
        procedure RegisterClass (bRegister: boolean);
        procedure UpdateRegistry (bRegister: boolean); override;

        property STAAllocMode: TSTAAllocMode read FSTAAllocMode;
        property ThreadingModel: TThreadingModel read FThreadingModel;
      end;

      { TAutoObjectFactory replacement, enables apartment allocation }
      TThreadedAutoObjectFactory = class(TAutoObjectFactory, IClassFactory, IExternalConnection)
      protected
        { IClassFactory }
        function CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
          out Obj): HResult; virtual; stdcall;
        function LockServer(fLock: BOOL): HResult; stdcall;
        { IExternalConnection }
        function AddConnection (extconn: longint; reserved: longint): longint; stdcall;
        function ReleaseConnection (extconn: longint; reserved: longint; fLastReleaseCloses: BOOL): longint; stdcall;
      protected
        FRegisterClass: longint;
        FSTAAllocMode: TSTAAllocMode;
        FThreadingModel: TThreadingModel;  // duplication due to private FThreadingModel field
        function DoCreateInstance(pApt: TApartment;
          const UnkOuter: IUnknown; const IID: TGUID; out Obj): HResult; virtual; stdcall;
        procedure Initialize; virtual;
      public
        // backward compatibility
        constructor CreateThreaded (ComServer: TComServerObject; AutoClass: TAutoClass;
          const ClassID: TGUID; Instancing: TClassInstancing; tm: TThreadingModel);
        constructor CreateThreadedEx (ComServer: TComServerObject; AutoClass: TAutoClass;
          const ClassID: TGUID; Instancing: TClassInstancing; tm: TThreadingModel; am: TSTAAllocMode);
        {$IFDEF D3}
        constructor Create(ComServer: TComServerObject; AutoClass: TAutoClass;
          const ClassID: TGUID; Instancing: TClassInstancing);
        {$ELSE}
        constructor Create(ComServer: TComServerObject; AutoClass: TAutoClass;
          const ClassID: TGUID; Instancing: TClassInstancing); overload;
        constructor Create(ComServer: TComServerObject; AutoClass: TAutoClass;
          const ClassID: TGUID; Instancing: TClassInstancing;
          tm: TThreadingModel); overload;
        constructor Create(ComServer: TComServerObject; AutoClass: TAutoClass;
          const ClassID: TGUID; Instancing: TClassInstancing;
          tm: TThreadingModel; am: TSTAAllocMode); overload;
        {$ENDIF}
        destructor Destroy; override;
        procedure RegisterClass (bRegister: boolean);
        procedure UpdateRegistry (bRegister: boolean); override;

        property STAAllocMode: TSTAAllocMode read FSTAAllocMode;
        property ThreadingModel: TThreadingModel read FThreadingModel;
      end;

      { TClassFactory replacement, enables apartment allocation }
      TThreadedClassFactory = class(TComponentFactory, IClassFactory, IExternalConnection)
      protected
        { IClassFactory }
        function CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
          out Obj): HResult; virtual; stdcall;
        function LockServer(fLock: BOOL): HResult; stdcall;
        { IExternalConnection }
        function AddConnection (extconn: longint; reserved: longint): longint; stdcall;
        function ReleaseConnection (extconn: longint; reserved: longint; fLastReleaseCloses: BOOL): longint; stdcall;
      protected
        FRegisterClass: longint;
        FSTAAllocMode: TSTAAllocMode;
        FThreadingModel: TThreadingModel;  // duplication due to private FThreadingModel field
        function DoCreateInstance(pApt: TApartment;
          const UnkOuter: IUnknown; const IID: TGUID; out Obj): HResult; virtual; stdcall;
        procedure Initialize; virtual;
      public
        // backward compatibility
        constructor CreateThreaded (ComServer: TComServerObject;
          ComponentClass: TComponentClass; const ClassID: TGUID;
          Instancing: TClassInstancing; tm: TThreadingModel);
        constructor CreateThreadedEx (ComServer: TComServerObject;
          ComponentClass: TComponentClass; const ClassID: TGUID;
          Instancing: TClassInstancing; tm: TThreadingModel; am: TSTAAllocMode);
        {$IFDEF D3}
        constructor Create (ComServer: TComServerObject;
          ComponentClass: TComponentClass; const ClassID: TGUID;
          Instancing: TClassInstancing);
        {$ELSE}
        constructor Create(ComServer: TComServerObject;
          ComponentClass: TComponentClass; const ClassID: TGUID;
          Instancing: TClassInstancing); overload;
        constructor Create(ComServer: TComServerObject;
          ComponentClass: TComponentClass; const ClassID: TGUID;
          Instancing: TClassInstancing; tm: TThreadingModel); overload;
        constructor Create(ComServer: TComServerObject;
          ComponentClass: TComponentClass; const ClassID: TGUID;
          Instancing: TClassInstancing; tm: TThreadingModel; am: TSTAAllocMode); overload;
        {$ENDIF}
        destructor Destroy; override;
        function CreateComObject (const Controller: IUnknown): TComObject; override;
        procedure RegisterClass (bRegister: boolean);
        procedure UpdateRegistry (bRegister: boolean); override;

        property STAAllocMode: TSTAAllocMode read FSTAAllocMode;
        property ThreadingModel: TThreadingModel read FThreadingModel;
      end;

      { backward compatibility }
      TThreadedVCLComObjectFactory = TThreadedClassFactory;

      { TComObject replacement, robust integration for both D3 and D4 }
      TThreadedComObject = class (TComObject)
      protected
        FApartment: TApartment;
        FCSRefCount: TCriticalSection;
        FCSSelfLock: TCriticalSection;
        FFTM: IUnknown;
        FFTMSupported: boolean;
        function GetFTM: IUnknown;
        function GetRefCountLock: TCriticalSection;
        function GetSelfLock: TCriticalSection;
        property FTM: IUnknown read GetFTM;
        property RefCountLock: TCriticalSection read GetRefCountLock;
        property SelfLock: TCriticalSection read GetSelfLock;  // convenient locking mechanism for self!
      public
        destructor Destroy; override;
        procedure Initialize; override;
        function ObjAddRef: integer; override;
        function ObjRelease: integer; override;
        function ObjQueryInterface (const IID: TGUID; out pObj): HResult; override;
        property Apartment: TApartment read FApartment;
        property FTMSupported: boolean read FFTMSupported write FFTMSupported;
      end;

      { TAutoObject replacement, robust integration for both D3 and D4 }
      TThreadedAutoObject = class (TAutoObject)
      protected
        FApartment: TApartment;
        FCSRefCount: TCriticalSection;
        FCSSelfLock: TCriticalSection;
        FFTM: IUnknown;
        FFTMSupported: boolean;
        function GetFTM: IUnknown;
        function GetRefCountLock: TCriticalSection;
        function GetSelfLock: TCriticalSection;
        property FTM: IUnknown read GetFTM;
        property RefCountLock: TCriticalSection read GetRefCountLock;
        property SelfLock: TCriticalSection read GetSelfLock;  // convenient locking mechanism for self!
      public
        destructor Destroy; override;
        procedure Initialize; override;
        function ObjAddRef: integer; override;
        function ObjRelease: integer; override;
        function ObjQueryInterface (const IID: TGUID; out pObj): HResult; override;
        property Apartment: TApartment read FApartment;
        property FTMSupported: boolean read FFTMSupported write FFTMSupported;
      end;

      { singleton com object }
      TSingletonComObject = class (TThreadedComObjectFactory, IUnknown)
      protected
        { IUnknown }
        function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
        function _AddRef: Integer; stdcall;
        function _Release: Integer; stdcall;
      protected
        function CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
          out Obj): HResult; override;
      public
        function SafeCallException(ExceptObject: TObject;
          ExceptAddr: Pointer): HResult; override;
        { added for convenience }
        function ObjAddRef: Integer; virtual; stdcall;
        function ObjQueryInterface(const IID: TGUID; out Obj): Integer; virtual; stdcall;
        function ObjRelease: Integer; virtual; stdcall;
      end;

      TSingletonComClass = class of TSingletonComObject;

      { singleton auto object }
      TSingletonAutoObject = class (TThreadedAutoObjectFactory, IUnknown, IProvideClassInfo, IDispatch)
      protected
        { IUnknown }
        function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
        function _AddRef: Integer; stdcall;
        function _Release: Integer; stdcall;
        { IProvideClassInfo }
        function GetClassInfo(out TypeInfo: ITypeInfo): HResult; stdcall;
        { IDispatch }
        function GetIDsOfNames(const IID: TGUID; Names: Pointer;
          NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; virtual; stdcall;
        function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; virtual; stdcall;
        function GetTypeInfoCount(out Count: Integer): HResult; virtual; stdcall;
        function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
          Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; virtual; stdcall;
      protected
        function CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
          out Obj): HResult; override;
      public
        function SafeCallException(ExceptObject: TObject;
          ExceptAddr: Pointer): HResult; override;
        function GetIntfEntry (Guid: TGUID): PInterfaceEntry; override;
        { added for convenience }
        function ObjAddRef: Integer; virtual; stdcall;
        function ObjQueryInterface(const IID: TGUID; out Obj): Integer; virtual; stdcall;
        function ObjRelease: Integer; virtual; stdcall;
      end;

      TSingletonAutoClass = class of TSingletonAutoObject;

      { default apartment manager implementation }
      TDefaultApartmentManager = class (TApartmentManagerObject)
      protected
        FApartments: TApartments;
        FGC: TApartmentsGC;
        FGCTimer: TTimer;
        FShuttingDown: boolean;
        function GetApartments: TApartments; override;
        procedure TimerUpdate (pSender: TObject);
      public
        constructor Create;
        destructor Destroy; override;
        procedure GarbageCollect (Sender: TObject); override;
        function GetPooledSTACount: integer; override;
        procedure LastReleased (var bShutdown: boolean); override;
        procedure Resume; override;
        function ServerIsShuttingDown: boolean; override;
        property GCTimer: TTimer read FGCTimer;
      end;

    { returns global apartment manager }
    function ApartmentManager: TApartmentManagerObject;

    { waits on a threading primitive handle }
    function WaitForThreadSyncObject (iHandle: THandle; iTimeOut: dword): TWaitResult;

    { utility fn for entering/leaving apartments }
    function InitializeCOM (bInit: boolean; at: TApartmentType): boolean;

    { std verify routine }
    procedure Verify (bAssert: boolean; const sError: string);

    { returns delegated CreateInstanceFunc for a class factory }
    function GetFactoryCreateInstanceFunc (cf: TComObjectFactory): TFuncFactoryCreateInstance;

    { proper registration for a class factory }
    procedure RegisterFactory (cf: TComObjectFactory; bRegister: boolean);

    { determines if a class factory is threaded }
    function IsThreadedFactory (cf: TComObjectFactory): boolean;

    { marks an implicitly revoked class factory }
    function MarkRevokedFactory (cf: TComObjectFactory): boolean;

    { initializes singleton objects }
    procedure InitializeSingletonComObject (ComServer: TComServerObject;
      SingletonClass: TSingletonComClass; const clsid: TGUID;
      const sClassName, sDescription: string);

    procedure InitializeSingletonAutoObject (ComServer: TComServerObject;
      SingletonClass: TSingletonAutoClass; const clsid: TGUID);

    procedure Register;

    type
      {$IFDEF D3}
      { NT4 DCOM extended APIs }
      TCoInitializeEx = function (pvReserved: Pointer; coInit: Longint): HResult; stdcall;
      TCoAddRefServerProcess = function: longint; stdcall;
      TCoReleaseServerProcess = function: longint; stdcall;
      TCoSuspendClassObjects = function: HResult; stdcall;
      TCoResumeClassObjects = function: HResult; stdcall;
      {$ENDIF}
     
      TCoCreateFreeThreadedMarshaler = function (unkOuter: IUnknown; out unkMarshal: IUnknown): HResult; stdcall;

    var
      cNT4DCOMSupported: boolean = FALSE;  // must be READONLY!

      {$IFDEF D3}
      CoInitializeEx: TCoInitializeEx = NIL;
      CoAddRefServerProcess: TCoAddRefServerProcess = NIL;
      CoReleaseServerProcess: TCoReleaseServerProcess = NIL;
      CoSuspendClassObjects: TCoSuspendClassObjects = NIL;
      CoResumeClassObjects: TCoResumeClassObjects = NIL;
      {$ENDIF}

      CoCreateFreeThreadedMarshaler: TCoCreateFreeThreadedMarshaler = NIL;

    var
      { default server threading model: all objects are apartment threaded }
      cDefServerThreadingModel: TThreadingModel = tmApartment;

      { default STA allocation mode: All STA objects are allocated from a pool
        of STAs whose count is cDefApartmentPoolCount
      }
      cDefApartmentAllocMode: TSTAAllocMode = amPooled;

      { default wait timeout to terminate message queue threads }
      cDefMQThreadQuitWaitTimeout: integer = 15000;  { 15 secs }

      { default apartment classes. allows user to easily use their own apartment classes }
      cDefApartmentThreadClass: TApartmentThreadClass = TApartmentThread;
      cDefApartmentClass: TApartmentClass = TApartment;

      { global apartment manager object }
      cApartmentManager: TApartmentManagerObject = NIL;
    const
      { default STA pool size }
      cDefApartmentPoolCount: integer = 30;

      { default apartment garbage collector timer interval, ms }
      cDefApartmentGCTimerInterval= 15000;

    implementation

    uses
      Messages,
      SysUtils
      ;

    var
      WM_CREATEOBJECTINTHREAD: UINT = WM_USER + $1234;
      WM_GARBAGECOLLECTCOMSERVER: UINT = WM_USER + $1235;

    const
      ThreadingModelFlags: array [TThreadingModel] of string = (
        '', 'Apartment', 'Free', 'Both','Neutral');
      RegFlags: array [ciSingleInstance..ciMultiInstance] of integer = (
        REGCLS_SINGLEUSE, REGCLS_MULTIPLEUSE
      );
      SuspendedFlags: array [boolean] of integer = (0, REGCLS_SUSPENDED);

    procedure DeleteRegValue (const sKey, sName: string);
    var
      hkTemp: HKey;
    begin
      if (RegOpenKeyEx (HKEY_CLASSES_ROOT, PChar (sKey), 0,
          KEY_ALL_ACCESS, hkTemp) = ERROR_SUCCESS)
      then begin
        RegDeleteValue (hkTemp, PChar (sName));
        RegCloseKey (hkTemp);
      end;  { if }
    end;

    function WaitForThreadSyncObject (iHandle: THandle; iTimeOut: dword): TWaitResult;
    begin
      Assert (iHandle <> 0);
      case WaitForSingleObject (iHandle, iTimeout) of
        WAIT_OBJECT_0 :
          Result := wrSignaled;
        WAIT_TIMEOUT :
          Result := wrTimeout;
        WAIT_ABANDONED :
          Result := wrAbandoned;
        else
          Result := wrError;
      end;  { case }
    end;

    procedure Verify (bAssert: boolean; const sError: string);
    begin
      if not (bAssert) then
      begin
        if (IsLibrary) then MessageBox (0, pchar (sError), 'Server Error', MB_ICONERROR OR MB_OK);
        raise Exception.Create (sError);
      end;  { if }
    end;

    function InitializeCOM (bInit: boolean; at: TApartmentType): boolean;
    const
      cApartmentInitFlags: array [TApartmentType] of integer = (
        COINIT_APARTMENTTHREADED,
        COINIT_MULTITHREADED
      );

     function InitializeMTA: boolean;
     begin
       Verify (@CoInitializeEx <> NIL,
         'Operating system does not support multithreaded apartments!'#13 +
         'You will need to install the latest DCOM version for this feature to work.'
       );
       Result := Succeeded (CoInitializeEx (NIL, COINIT_MULTITHREADED));
     end;

    begin
      //Result := FALSE;
      if (bInit) then
      begin
        case at of
          atSTA :
            Result := Succeeded (CoInitialize (NIL));
          atMTA :
            Result := InitializeMTA;
          else
            raise Exception.Create ('Apartment type not supported!');
        end;  { case }
      end
      else begin
        CoUninitialize;
        Result := TRUE;
      end;  { else }
    end;

    function GetFactoryCreateInstanceFunc (cf: TComObjectFactory): TFuncFactoryCreateInstance;
    begin
      Assert (cf <> NIL);
      Result := NIL;
      if (cf.InheritsFrom (TThreadedComObjectFactory)) then
        Result := TThreadedComObjectFactory (cf).DoCreateInstance
      else
      if (cf.InheritsFrom (TThreadedAutoObjectFactory)) then
        Result := TThreadedAutoObjectFactory (cf).DoCreateInstance
      else
      if (cf.InheritsFrom (TThreadedClassFactory)) then
        Result := TThreadedClassFactory (cf).DoCreateInstance
      ;
    end;

    procedure RegisterFactory (cf: TComObjectFactory; bRegister: boolean);
    begin
      Assert (cf <> NIL);
      if (cf.InheritsFrom (TThreadedComObjectFactory)) then
        TThreadedComObjectFactory (cf).RegisterClass (bRegister)
      else
      if (cf.InheritsFrom (TThreadedAutoObjectFactory)) then
        TThreadedAutoObjectFactory (cf).RegisterClass (bRegister)
      else
      if (cf.InheritsFrom (TThreadedClassFactory)) then
        TThreadedClassFactory (cf).RegisterClass (bRegister)
      else
      if (bRegister) then
        cf.RegisterClassObject;
    end;

    function IsThreadedFactory (cf: TComObjectFactory): boolean;
    var
      clscf: TClass;
    begin
      Assert (cf <> NIL);
      clscf := cf.ComClass;
      Result := (cf.InheritsFrom (TThreadedClassFactory) or
                 clscf.InheritsFrom (TThreadedComObject) or
                 clscf.InheritsFrom (TThreadedAutoObject)
      );
    end;

    function MarkRevokedFactory (cf: TComObjectFactory): boolean;
    begin
      Result := FALSE;
      Assert (cf <> NIL);
      if (cf.InheritsFrom (TThreadedComObjectFactory)) then
        TThreadedComObjectFactory (cf).FRegisterClass := -1
      else
      if (cf.InheritsFrom (TThreadedAutoObjectFactory)) then
        TThreadedAutoObjectFactory (cf).FRegisterClass := -1
      else
      if (cf.InheritsFrom (TThreadedClassFactory)) then
        TThreadedClassFactory (cf).FRegisterClass := -1
      ;
    end;

    procedure RegisterClassFactory (bRegister: boolean; const clsid: TClsId;
      pUnk: IUnknown; ci: TClassInstancing; var iRegister: longint);
    begin
      if (ci = ciInternal) then Exit;
      if (bRegister) then
      begin
        if (iRegister <> -1) then CoRevokeClassObject (iRegister);
        OleCheck (CoRegisterClassObject (clsid, pUnk, CLSCTX_LOCAL_SERVER,
          RegFlags [ci] or SuspendedFlags [cNT4DCOMSupported], iRegister)
        );
      end
      else begin
        if (iRegister <> -1) then CoRevokeClassObject (iRegister);
        iRegister := -1;
      end;  { else }
    end;

    function DefaultApartmentManager: TApartmentManagerObject;
    begin
      Result := TDefaultApartmentManager.Create;
    end;

    var
      csApartmentManager: TCriticalSection = NIL;
      cDefaultApartmentManagerUsed: boolean = FALSE;

    function ApartmentManager: TApartmentManagerObject;
    begin
      Assert (csApartmentManager <> NIL);
      csApartmentManager.Lock (TRUE);
      try
        if (cApartmentManager = NIL) then
        begin
          cDefaultApartmentManagerUsed:=TRUE;
          cApartmentManager := DefaultApartmentManager;
        end;  { if }
        Result := cApartmentManager;
      finally
        csApartmentManager.Lock (FALSE);
      end;  { finally }
    end;

    procedure DestroyDefaultApartmentManager;
    begin
      Assert (csApartmentManager <> NIL);
      csApartmentManager.Lock (TRUE);
      try
        if not (cDefaultApartmentManagerUsed) then Exit;
        if (cApartmentManager <> NIL) then cApartmentManager.Free;
        cApartmentManager := NIL;
        cDefaultApartmentManagerUsed := FALSE;
      finally
        csApartmentManager.Lock (FALSE);
      end;  { finally }
    end;


    { TThreadSyncObject }

    function TThreadSyncObject.GetLastError: longint;
    begin
      Result := Windows.GetLastError;
    end;

    destructor TThreadSyncObject.Destroy;
    begin
      if (FHandle <> 0) then
        CloseHandle (FHandle);
      inherited;
    end;

    procedure TThreadSyncObject.Acquire;
    begin
      Lock (TRUE);
    end;

    function TThreadSyncObject.Lock (bLock: boolean): boolean;
    begin
      //Result := FALSE;
      raise Exception.Create ('Not implemented!');
    end;

    procedure TThreadSyncObject.Release;
    begin
      Lock (FALSE);
    end;

    function TThreadSyncObject.WaitFor (iTimeout: dword): TWaitResult;
    begin
      Result := WaitForThreadSyncObject (Handle, iTimeOut);
    end;


    { TCriticalSection }

    constructor TCriticalSection.Create;
    begin
      inherited Create;
      InitializeCriticalSection (FCS);
    end;

    destructor TCriticalSection.Destroy;
    begin
      DeleteCriticalSection (FCS);
      inherited;
    end;

    procedure TCriticalSection.Enter;
    begin
      Lock (TRUE);
    end;

    function TCriticalSection.Lock (bLock: boolean): boolean;
    begin
      if (bLock) then
        EnterCriticalSection (FCS)
      else
        LeaveCriticalSection (FCS);
      Result := TRUE;
    end;

    procedure TCriticalSection.Leave;
    begin
      Lock (FALSE);
    end;


    { TEvent }

    constructor TEvent.Create (psa: PSecurityAttributes;
      bManualReset, bInitState: boolean; const sName: string);
    var
      pName: pchar;
    begin
      inherited Create;
      if (sName <> '') then pName := pchar (sName) else pName := NIL;
      FHandle := CreateEvent (psa, bManualReset, bInitState, pName);
    end;

    constructor TEvent.CreateSimple;
    begin
      Create (NIL, TRUE, FALSE, '');
    end;

    function TEvent.PulseEvent: boolean;
    begin
      Result := Windows.PulseEvent (Handle);
    end;

    function TEvent.ResetEvent: boolean;
    begin
      Result := Windows.ResetEvent (Handle);
    end;

    function TEvent.SetEvent: boolean;
    begin
      Result := Windows.SetEvent (Handle);
    end;


    { TMQThread }

    procedure TMQThread.EnsureMessageQueue;
    var
      rMsg: TMsg;
    begin
      { signal to creating thread that we are ready! }
      PeekMessage (rMsg, 0, 0, 0, PM_NOREMOVE);  // force thread message queue!
      SignalReady;  // notify requesting thread we're open for business
    end;

    function TMQThread.ProcessMessage (var rMsg: TMsg): boolean;
    begin
      Result := FALSE;
    end;

    constructor TMQThread.Create (bSuspend: boolean);
    begin
      inherited Create (bSuspend);
      FReadyEvent := TEvent.CreateSimple;
      QuitWaitTimeout := cDefMQThreadQuitWaitTimeout;
    end;

    destructor TMQThread.Destroy;
    begin
      FReadyEvent.Free;
      inherited;
    end;

    procedure TMQThread.Execute;
    var
      rMsg: TMsg;
    begin
      EnsureMessageQueue;

      { thread message loop }
      while (TRUE) do
      begin
        if (GetMessage (rMsg, 0, 0, 0)) then
        begin
          { handle message }
          ProcessMessage (rMsg);

          { check any next pending messages }
          Continue;
        end
        else begin
          Terminate;
        end;  { else }

        if (Terminated) then Break;
      end;  { while }
    end;

    function TMQThread.Quit (bWait: boolean): boolean;
    begin
      if not (Terminated) then
      begin
        PostThreadMessage (ThreadId, WM_QUIT, 0, 0);
        if (bWait) then WaitForQuit;
      end;  { if }
      Result := TRUE;
    end;

    procedure TMQThread.SignalReady;
    begin
      Assert (FReadyEvent <> NIL);
      FReadyEvent.SetEvent;
    end;

    procedure TMQThread.WaitForQuit;
    var
      wr: TWaitResult;
    begin
      Verify (ThreadId <> GetCurrentThreadId,
        'Message queue thread cannot be terminated from within its own thread!'
      );
      wr := WaitForThreadSyncObject (Handle, QuitWaitTimeout);

      { if wait was unsuccessful, take thread by force }
      if (wr <> wrSignaled) then
        TerminateThread (Handle, 1);
    end;

    function TMQThread.WaitForReady: boolean;
    begin
      Assert (FReadyEvent <> NIL);
      Result := (FReadyEvent.WaitFor (INFINITE) = wrSignaled);
    end;


    { apartment handler window }

    function ApartmentThreadWndProc (hWndTarget: HWND; iMessage, wParam, lParam: longint): longint; stdcall;
    var
      at: TApartmentThread;
      ao: TApartmentObject;
      pUnk: IUnknown;
      pciInfo: PCreateInstanceInfo absolute wParam;
    begin
      Result := 0;  // default to success

      if (UINT (iMessage) = WM_CREATEOBJECTINTHREAD) then
      begin
        { wParam points to a PCreateInstanceInfo }
        Assert (pciInfo <> NIL);

        at := pciInfo^.ApartmentThread;
        Assert (at <> NIL);
        with at do
        begin
          ao := pciInfo^.ApartmentObject;
          Assert (ao <> NIL);
          with ao do
          begin
            { class factory create }
            Assert (@FCreateInstanceFunc <> NIL);
            FCreateResult := FCreateInstanceFunc (Apartment, NIL, FCreateIID, pUnk);

            { marshal newly created interface pointer }
            if Succeeded (FCreateResult) then
              FCreateResult := MarshalInterface (FCreateIID, pUnk);

            { return status }
            if not (Succeeded (FCreateResult)) then Result := FCreateResult;
          end;  { with }
        end;  { with }
      end
      else
      if (iMessage = WM_QUIT) then
      begin
        DestroyWindow (hWndTarget);
      end
      else begin
        Result := DefWindowProc (hWndTarget, iMessage, wParam, lParam);
      end;  { else }
    end;

    var
      cApartmentThreadWindowClass: TWndClass = (
        style: 0;
        lpfnWndProc: @ApartmentThreadWndProc;
        cbClsExtra: 0;
        cbWndExtra: 0;
        hInstance: 0;
        hIcon: 0;
        hCursor: 0;
        hbrBackground: 0;
        lpszMenuName: NIL;
        lpszClassName: 'TApartmentThreadWindow'
      );

    function CreateApartmentThreadWindow: HWnd;
    var
      pWndClass: TWndClass;
      bRegistered: boolean;
    begin
      cApartmentThreadWindowClass.hInstance := HInstance;
      bRegistered := GetClassInfo (
        HInstance, cApartmentThreadWindowClass.lpszClassName, pWndClass
      );

      { need to register? }
      if not (bRegistered) or (pWndClass.lpfnWndProc <> @ApartmentThreadWndProc) then
      begin
        if (bRegistered) then
          Windows.UnregisterClass (cApartmentThreadWindowClass.lpszClassName, HInstance);
        Windows.RegisterClass(cApartmentThreadWindowClass);
      end;  { if }

      { create }
      Result := CreateWindow (
        cApartmentThreadWindowClass.lpszClassName, '', 0, 0, 0, 0, 0, 0, 0, HInstance, NIL
      );
    end;

    { TObjectMarshaler }

    procedure TObjectMarshaler.ReleaseStream;
    begin
      { release any held/marshaled object }
      IStream (FStream) := NIL;
    end;

    constructor TObjectMarshaler.CreateMarshalObject (const iid: TGUID; const pUnk: IUnknown);
    begin
      Verify (not (IsEqualIID (iid, GUID_NULL)) and (pUnk <> NIL), 'Invalid object parameters');
      inherited Create;
      MarshalObject (iid, pUnk);
    end;

    constructor TObjectMarshaler.CreateMarshalOleObject (const vObj: olevariant);
    begin
      CreateMarshalObject (IDispatch, IDispatch (vObj) as IUnknown);
    end;

    destructor TObjectMarshaler.Destroy;
    begin
      ReleaseStream;
      inherited;
    end;

    function TObjectMarshaler.MarshalObject (const iid: TGUID; const pUnk: IUnknown): boolean;
    begin
      Assert (pUnk <> NIL);
      ReleaseStream;
      OleCheck (CoMarshalInterThreadInterfaceInStream (iid, pUnk, IStream (FStream)));
      FMarshalIID := iid;
      Result := TRUE;
    end;

    function TObjectMarshaler.MarshalOleObject (const vObj: olevariant): boolean;
    begin
      Assert (not VarIsEmpty (vObj));
      { marshal as IDispatch }
      Result := MarshalObject (IDispatch, IDispatch (vObj) as IUnknown);
    end;

    function TObjectMarshaler.UnMarshalObject (out pObj): boolean;
    begin
      Result := FALSE;
      if (FStream = NIL) then Exit;
      OleCheck (CoGetInterfaceAndReleaseStream (IStream (FStream), FMarshalIID, pObj));
      FStream := NIL;
      Result := TRUE;
    end;

    function TObjectMarshaler.UnMarshalOleObject (out vObj: olevariant): boolean;
    var
      pDispatch: IDispatch;
    begin
      Result := UnMarshalObject (pDispatch);
      if (Result) then vObj := pDispatch;
    end;


    { TApartmentManagerObject }

    function TApartmentManagerObject.GetAllocator: TApartmentAllocator;
    begin
      if (FAllocator = NIL) then FAllocator := TApartmentAllocator.Create (Self);
      Result := FAllocator;
    end;

    function TApartmentManagerObject.GetServerObjectCount: integer;
    begin
      Result := -1;  // -1 means not implemented
    end;

    destructor TApartmentManagerObject.Destroy;
    begin
      if (FAllocator <> NIL) then FAllocator.Free;
      FAllocator := NIL;
      inherited;
    end;

    function TApartmentManagerObject.CanCreateInstance (var hr: HResult): boolean;
    begin
      Result := TRUE;
      if (ServerIsShuttingDown) then Result := FALSE;
      if not (Result) then
      begin
        hr := CLASS_E_CLASSNOTAVAILABLE;
        if (cNT4DCOMSupported) then
        begin
          hr := CO_E_SERVER_STOPPING;
          Beep;
        end;  { if }
      end;  { if }
    end;

    function TApartmentManagerObject.CreateInstance (
      cf: TComObjectFactory; pfnci: TFuncFactoryCreateInstance;
      tm: TThreadingModel; am: TSTAAllocMode; pUnkOuter: IUnknown;
      const iid: TGUID; out pObject): HResult;
    var
      pcf: IClassFactory;
      apt: TApartment;
      rciData: TCreateInstanceData;
    begin
      Assert (@pfnci <> NIL);
      //Result := E_FAIL;

      { ensure server is locked while we are in the process of creating an instance!
        if we rely on the OS to call LockServer, we're doomed! =)
      }
      if (cf <> NIL) then pcf := cf as IClassFactory else pcf := NIL;
      if (pcf <> NIL) then pcf.LockServer (TRUE);
      try
        { proactive apartments are only supported for EXE servers }
        if (IsLibrary) then
        begin
          Result := pfnci (NIL, pUnkOuter, iid, pObject);
          Exit;
        end;  { if }

        { plug shutdown race-condition! }
        if not CanCreateInstance (Result) then Exit;

        if (pUnkOuter <> NIL) then
        begin
          { cannot aggregate across apartments }
          Result := CLASS_E_NOAGGREGATION;
          Exit;
        end
        else begin
          { init rciData }
          Fillchar (rciData, sizeof (rciData), 0);
          if (cf <> NIL) then rciData.IsThreadedComClass := IsThreadedFactory (cf);

          case tm of
            tmApartment :
            begin
              { allocate then create in STA }
              if (Allocator <> NIL) then
              begin
                apt := Allocator.AllocateApartment (am);
                if (apt <> NIL) then
                begin
                  Result := apt.CreateInstance (pfnci, IID, @rciData, pObject);
                  Exit;
                end;  { if }
              end;  { if }
            end;

            tmFree,
            tmBoth :
            begin
              { "both" threading is really not defined for outproc objects;
                by default, we just assume that the user wants MTA
              }
              apt := Apartments.EnsureMTA;
              if (apt <> NIL) then
              begin
                Result := apt.CreateInstance (pfnci, IID, @rciData, pObject);
                Exit;
              end;  { if }
            end;
          end;  { case }

          { for all other settings, use default handler! }
          Result := pfnci (NIL, pUnkOuter, IID, pObject);
        end;  { else }
      finally
        if (pcf <> NIL) then pcf.LockServer (FALSE);
      end;  { finally }
    end;

    procedure TApartmentManagerObject.LastReleased (var bShutdown: boolean);
    begin
    end;

    procedure TApartmentManagerObject.Resume;
    begin
    end;


    { TApartmentsGCThread }

    function TApartmentsGCThread.GetManager: TApartmentManagerObject;
    begin
      Result := FOwner.Manager;
    end;

    function TApartmentsGCThread.ProcessMessage (var rMsg: TMsg): boolean;
    begin
      Result := FALSE;
      if (Manager.ServerIsShuttingDown) then Exit;

      if (rMsg.Message = WM_GARBAGECOLLECTCOMSERVER) then
      begin
        GarbageCollect (TObject (rMsg.lParam));
        Result := TRUE;  { handled }
      end;  { if }
    end;

    constructor TApartmentsGCThread.Create (Owner: TApartmentsGC; tp: TThreadPriority);
    begin
      Assert (Owner <> NIL);
      inherited Create (TRUE);
      FOwner := Owner;
      Priority := tp;
      Resume;
    end;

    procedure TApartmentsGCThread.GarbageCollect (Sender: TObject);
    begin
      with Manager do
      begin
        if (ServerIsShuttingDown) then Exit;
        if (Sender = NIL) then
          Apartments.DeleteEmptyApartments
        else
          Apartments.SafeRemoveApartment (TApartment (Sender));
      end;  { with }
    end;


    { TApartmentsGC }

    procedure TApartmentsGC.InitializeThread (tp: TThreadPriority);
    begin
      FThread := TApartmentsGCThread.Create (Self, tp);
      FThread.WaitForReady;
    end;

    constructor TApartmentsGC.Create (am: TApartmentManagerObject; tp: TThreadPriority);
    begin
      Assert (am <> NIL);
      inherited Create;
      FManager := am;
      InitializeThread (tp);
      Verify (Thread <> NIL, 'Garbage collector thread must be initialized!');
    end;

    destructor TApartmentsGC.Destroy;
    begin
      Terminate;
      FThread.Free;
      inherited;
    end;

    procedure TApartmentsGC.Activate (Sender: TObject);
    begin
      PostThreadMessage (Thread.ThreadId, WM_GARBAGECOLLECTCOMSERVER, 0, lParam (Sender));
    end;

    procedure TApartmentsGC.Terminate;
    begin
      Verify (GetCurrentThreadId <> Thread.ThreadID,
        'Garbage collector cannot be terminated from within its own thread!'
      );
      Thread.Quit (TRUE);
    end;


    { TApartmentObject }

    procedure TApartmentObject.Clear;
    begin
      FCreateStream := NIL;
      FCreateResult := E_FAIL;
    end;

    function TApartmentObject.CreateOnThread (pciInfo: PCreateInstanceInfo): boolean;
    var
      at: TApartmentThread;
    begin
      Assert (pciInfo <> NIL);
      at := pciInfo^.ApartmentThread;
      Assert (at <> NIL);
      Clear;
      pciInfo^.ApartmentObject := Self;
      Result := (SendMessage (
        at.ServerWindow, WM_CREATEOBJECTINTHREAD, wParam (pciInfo), 0) = 0
      );
    end;

    function TApartmentObject.GetInstance (out pObject): HResult;
    begin
      Result := UnmarshalInterface (FCreateIID, pObject);
    end;

    function TApartmentObject.MarshalInterface (const iid: TGUID; pUnk: IUnknown): HResult;
    begin
      Result := CoMarshalInterThreadInterfaceInStream (iid, pUnk, IStream (FCreateStream));
    end;

    function TApartmentObject.UnmarshalInterface (const iid: TGUID; out pObject): HResult;
    begin
      Assert (FCreateStream <> NIL);
      Result := CoGetInterfaceAndReleaseStream (IStream (FCreateStream), iid, pObject);
      FCreateStream := NIL;
    end;

    constructor TApartmentObject.Create (
      pCreateInstance: TFuncFactoryCreateInstance; const iid: TGUID);
    begin
      Assert (@pCreateInstance <> NIL);
      inherited Create;
      FCreateInstanceFunc := pCreateInstance;
      FCreateIID := iid;
    end;

    destructor TApartmentObject.Destroy;
    begin
      inherited;
    end;

    function TApartmentObject.CreateInApartmentThread (pciInfo: PCreateInstanceInfo; out pObject): HResult;
    var
      at: TApartmentThread;
    begin
      Assert (pciInfo <> NIL);
      at := pciInfo^.ApartmentThread;
      Assert (at <> NIL);
      Assert (at.ThreadId <> GetCurrentThreadId);

      { create object on thread }
      if (CreateOnThread (pciInfo)) then
        Result := GetInstance (pObject)
      else
        Result := FCreateResult;
    end;


    { TApartmentThread }

    procedure TApartmentThread.Execute;
    var
      rMsg: TMsg;
    begin
      InitializeCOM (TRUE, Apartment.ApartmentType);
      try
        { create apartment window that handles object creation }
        InitServerWindow (TRUE);

        { flush out pending window messages }
        while PeekMessage (rMsg, FServerWindow, 0, 0, PM_NOREMOVE) do
          DispatchMessage (rMsg);

        { enter default loop }
        inherited;
      finally
        InitializeCOM (FALSE, Apartment.ApartmentType);
      end;  { finally }
    end;

    function TApartmentThread.GetApartment: TApartment;
    begin
      Result := FOwner.FOwner;
    end;

    procedure TApartmentThread.InitServerWindow (bInit: boolean);
    begin
      if (bInit) then
      begin
        if (ServerWindow <> 0) then Exit;
        FServerWindow := CreateApartmentThreadWindow;
      end
      else begin
        if (ServerWindow = 0) then Exit;
        SendMessage (ServerWindow, WM_QUIT, 0, 0);
        FServerWindow := 0;
      end;  { else }
    end;

    procedure TApartmentThread.InstanceCreated (pciInfo: PCreateInstanceInfo; pUnk: IUnknown);
    begin
    end;

    function TApartmentThread.ProcessMessage (var rMsg: TMsg): boolean;
    begin
      DispatchMessage (rMsg);
      Result := TRUE;
    end;

    constructor TApartmentThread.Create (Owner: TApartmentThreads);
    begin
      Assert (Owner <> NIL);
      inherited Create (TRUE);
      FOwner := Owner;
    end;

    destructor TApartmentThread.Destroy;
    begin
      InitServerWindow (FALSE);
      inherited;
    end;

    function TApartmentThread.CreateInstance (pCreateInstance: TFuncFactoryCreateInstance;
      const iid: TGUID; pData: pointer; out pObject): HResult;
    var
      ao: TApartmentObject;
      rciInfo: TCreateInstanceInfo;
    begin
      Assert (@pCreateInstance <> NIL);

      { init rciInfo }
      Fillchar (rciInfo, sizeof (rciInfo), 0);
      rciInfo.ApartmentThread := Self;
      rciInfo.Data := pData;

      if (GetCurrentThreadId = ThreadId) then
      begin
        { if we're requesting a create instance on this thread, then we are already
          in this thread's apartment and thus, there is no need to create and
          marshal it out of this apartment.
        }
        Result := pCreateInstance (Apartment, NIL, iid, pObject);
      end
      else begin
        { Create and marshal out from this thread }
        ao := TApartmentObject.Create (pCreateInstance, iid);
        try
          Result := ao.CreateInApartmentThread (@rciInfo, pObject);
        finally
          ao.Free;
        end;  { finally }
      end;  { else }

      { log instance creation }
      if (Succeeded (Result)) then InstanceCreated (@rciInfo, IUnknown (pObject));
    end;

    function TApartmentThread.Quit (bWait: boolean): boolean;
    begin
      InitServerWindow (FALSE);
      Result := inherited Quit (bWait);
    end;


    { TApartmentThreads }

    function TApartmentThreads.CreateThread: TApartmentThread;
    begin
      Result := cDefApartmentThreadClass.Create (Self);
    end;

    function TApartmentThreads.GetItemById (iThreadId: integer): TApartmentThread;
    var
      i: integer;
    begin
      LockThreads (TRUE);
      try
        Result := NIL;
        for i := 0 to FThreads.Count - 1 do
          if (TApartmentThread (FThreads [i]).ThreadId = THandle (iThreadId)) then
          begin
            Result := FThreads [i];
            Break;
          end;  { if }
      finally
        LockThreads (FALSE);
      end;  { finally }
    end;

    function TApartmentThreads.GetItems (i: integer): TApartmentThread;
    begin
      LockThreads (TRUE);
      try
        Assert ((i >= 0) and (i < Count));
        Result := FThreads [i];
      finally
        LockThreads (FALSE);
      end;  { finally }
    end;

    procedure TApartmentThreads.LockThreads (bLock: boolean);
    begin
      Assert (FCSThreads <> NIL);
      FCSThreads.Lock (bLock);
    end;

    constructor TApartmentThreads.Create (Owner: TApartment);
    begin
      Assert (Owner <> NIL);
      inherited Create;
      FCSThreads := TCriticalSection.Create;
      FOwner := Owner;
      FThreads := TList.Create;
    end;

    destructor TApartmentThreads.Destroy;
    begin
      Clear;
      FThreads.Free;
      FCSThreads.Free;
      inherited;
    end;

    function TApartmentThreads.AddThread (at: TApartmentThread): integer;
    begin
      Assert (at <> NIL);
      LockThreads (TRUE);
      try
        Result := FThreads.Add (at);
      finally
        LockThreads (FALSE);
      end;  { finally }
    end;

    procedure TApartmentThreads.Clear;
    var
      i: integer;
    begin
      LockThreads (TRUE);
      try
        Terminate;
        for i := 0 to Count - 1 do
          Items [i].Free;
        FThreads.Clear;
      finally
        LockThreads (FALSE);
      end;  { finally }
    end;

    function TApartmentThreads.Count: integer;
    begin
      LockThreads (TRUE);
      try
        Result := FThreads.Count;
      finally
        LockThreads (FALSE);
      end;  { finally }
    end;

    function TApartmentThreads.NewThread: TApartmentThread;

     procedure ValidateRequest;
     begin
       { Verify STAs only have at most 1 thread }
       if (Apartment.ApartmentType = atSTA) then
         Verify ((Count < 1), 'Only 1 thread can be created in a single-threaded apartment!');
     end;

    var
      at: TApartmentThread;
    begin
      LockThreads (TRUE);
      try
        ValidateRequest;

        { new thread }
        at := CreateThread;
        FThreads.Add (at);
        at.Resume;

        { wait for thread to be ready for use }
        at.WaitForReady;

        Result := at;
      finally
        LockThreads (FALSE);
      end;  { finally }
    end;

    function TApartmentThreads.Terminate: boolean;
    var
      i: integer;
    begin
      LockThreads (TRUE);
      try
        { signal and wait for done }
        for i := 0 to Count - 1 do
          Items [i].Quit (TRUE);

        Result := TRUE;
      finally
        LockThreads (FALSE);
      end;  { finally }
    end;


    { TApartment }

    function TApartment.GetManager: TApartmentManagerObject;
    begin
      Result := FOwner.Manager;
    end;

    function TApartment.CreateThreads: TApartmentThreads;
    begin
      Result := TApartmentThreads.Create (Self);
    end;

    function TApartment.GetLockCount: integer;
    begin
      LockRefCount (TRUE);
      try
        Result := FLockCount;
      finally
        LockRefCount (FALSE);
      end;  { finally }
    end;

    procedure TApartment.LastReleased;
    begin
      { wake garbage collector }
      if not (Manager.ServerIsShuttingDown) and (AutoDelete) then
      begin
        MarkForDelete := TRUE;
        Manager.GarbageCollect (Self);
      end;  { if }
    end;

    procedure TApartment.SetAutoDelete (bSet: boolean);
    begin
      LockRefCount (TRUE);
      try
        FAutoDelete := bSet;
      finally
        LockRefCount (FALSE);
      end;  { finally }
    end;

    procedure TApartment.SetPooled (bSet: boolean);
    begin
      if (Pooled = bSet) then Exit;
      FPooled := bSet;
      //if (Pooled) then AutoDelete := FALSE;  pooled ones can be removed too! =)
    end;

    constructor TApartment.Create (Owner: TApartments; at: TApartmentType);
    begin
      Assert (Owner <> NIL);
      inherited Create;
      FCSRefCount := TCriticalSection.Create;
      FThreads := CreateThreads;
      Verify (FThreads <> NIL, 'Apartment must have at least 1 thread!');
      FApartmentType := at;
      FOwner := Owner;
      FAutoDelete := (at = atSTA);
    end;

    destructor TApartment.Destroy;
    begin
      FThreads.Free;
      FCSRefCount.Free;
      inherited;
    end;

    function TApartment.CreateInstance (pCreateInstance: TFuncFactoryCreateInstance;
      const iid: TGUID; pData: pointer; out pObject): HResult;
    begin
      Assert (Threads.Count > 0);
      Result := Threads [0].CreateInstance (pCreateInstance, iid, pData, pObject);
    end;

    function TApartment.CountObject (bLock: boolean): integer;
    begin
      LockRefCount (TRUE);
      try
        if (bLock) then
        begin
          inc (FLockCount);
          Result := FLockCount;
          if (Result > 0) then MarkForDelete := FALSE;
        end
        else begin
          dec (FLockCount);
          Result := FLockCount;
          if (Result = 0) then LastReleased;
        end;  { else }
      finally
        LockRefCount (FALSE);
      end;  { finally }
    end;

    function TApartment.GarbageCollect: boolean;
    begin
      Result := TRUE;
    end;

    procedure TApartment.LockRefCount (bLock: boolean);
    begin
      Assert (FCSRefCount <> NIL);
      FCSRefCount.Lock (bLock);
    end;

    function TApartment.TerminateThreads: boolean;
    begin
      Result := Threads.Terminate;
    end;


    { TApartments }

    function TApartments.CreateApartment (at: TApartmentType): TApartment;
    begin
      Result := cDefApartmentClass.Create (Self, at);
    end;

    function TApartments.GetActiveApartment: TApartment;
    var
      thrd: TApartmentThread;
    begin
      if not (GetCurrentApartment (Result, thrd)) then Result := NIL;
    end;

    function TApartments.GetActiveApartmentName: string;
    var
      apt: TApartment;
    begin
      Result := '';
      apt := ActiveApartment;
      if (apt <> NIL) then Result := apt.Name;
    end;

    function TApartments.GetItemByName (const sName: string): TApartment;
    var
      i: integer;
    begin
      LockApartments (TRUE);
      try
        Result := NIL;
        for i := 0 to Count - 1 do
          if (AnsiCompareText (Items [i].Name, sName) = 0) then
          begin
            Result := Items [i];
            Break;
          end;  { if }
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.GetItems (i: integer): TApartment;
    begin
      LockApartments (TRUE);
      try
        Result := NIL;
        if (i < 0) or (i >= Count) then Exit;
        Result := FApartments [i];
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.GetLockCount: integer;
    var
      i: integer;
    begin
      LockApartments (TRUE);
      try
        Result := 0;
        for i := 0 to Count - 1 do
          Result := Result + Items [i].LockCount;
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.GetObjects (i: integer): pointer;
    begin
      LockApartments (TRUE);
      try
        Result := NIL;
        if (i < 0) or (i >= Count) then Exit;
        Result := Items [i].Data;
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.GetPooledCount: integer;
    var
      i: integer;
    begin
      LockApartments (TRUE);
      try
        Result := 0;
        for i := 0 to Count - 1 do
          if (Items [i].Pooled) then
            Result := Result + 1;
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    procedure TApartments.LockApartments (bLock: boolean);
    begin
      Assert (FCSApartments <> NIL);
      FCSApartments.Lock (bLock);
    end;

    procedure TApartments.SetObjects (i: integer; pObj: pointer);
    begin
      LockApartments (TRUE);
      try
        if (i < 0) or (i >= Count) then Exit;
        Items [i].Data := pObj;
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    constructor TApartments.Create (am: TApartmentManagerObject);
    begin
      Assert (am <> NIL);
      inherited Create;
      FCSApartments := TCriticalSection.Create;
      FApartments := TList.Create;
      FManager := am;
    end;

    destructor TApartments.Destroy;
    begin
      Clear;
      FApartments.Free;
      FCSApartments.Free;
      inherited;
    end;

    procedure TApartments.Clear;
    var
      i: integer;
    begin
      LockApartments (TRUE);
      try
        TerminateThreads;
        for i := 0 to Count - 1 do
          Items [i].Free;
        FApartments.Clear;
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.Count: integer;
    begin
      LockApartments (TRUE);
      try
        Result := FApartments.Count;
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.DeleteApartment (i: integer): boolean;
    var
      apt: TApartment;
    begin
      LockApartments (TRUE);
      try
        Result := FALSE;
        if (i < 0) or (i > Count) then Exit;
        apt := Items [i];
        if not (apt.TerminateThreads) then Exit;
        FApartments.Delete (i);
        apt.Free;
        Result := TRUE;
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    procedure TApartments.DeleteEmptyApartments;
    var
      i: integer;
    begin
      { this method should only be called from a low-priority garbage collector thread }
      LockApartments (TRUE);
      try
        { free any hanging unlocked apartments }
        i := 0;
        while (i < Count) do
        begin
          { check shutdown per pass }
          if (Manager.ServerIsShuttingDown) then Exit;

          if (Items [i].AutoDelete) and (Items [i].MarkForDelete) then
          begin
            DeleteApartment (i);
            Continue;
          end;  { if }

          { next }
          i := i + 1;
        end;  { while }
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.EnsureMTA: TApartment;
    begin
      LockApartments (TRUE);
      try
        Result := NIL;
        if not (FindMTA (Result)) then
          Result := NewApartment ('', atMTA, TRUE);
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.FindMTA (var mta: TApartment): boolean;
    var
      i: integer;
    begin
      Result := FALSE;
      LockApartments (TRUE);
      try
        for i := 0 to FApartments.Count - 1 do
          if (TApartment (FApartments [i]).ApartmentType = atMTA) then
          begin
            Result := TRUE;
            mta := FApartments [i];
            Break;
          end;  { if }
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.GarbageCollect (iCount: integer): integer;
    var
      i: integer;
      bgc: boolean;
    begin
      Result := 0;
      LockApartments (TRUE);
      try
        { if iCount = -1, do all }
        if (iCount = -1) then iCount := Count;
        for i := Count - 1 downto 0 do
        begin
          { check shutdown per pass }
          if (Manager.ServerIsShuttingDown) then Exit;
         
          if (iCount <= 0) then Break;
          bgc := Items [i].GarbageCollect;
          if (bgc) then
          begin
            dec (iCount);
            inc (Result);
          end;  { if }
        end;  { for }
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.GetCurrentApartment (var apt: TApartment; var thrd: TApartmentThread): boolean;
    var
      i: integer;
    begin
      LockApartments (TRUE);
      try
        Result := FALSE;
        { this reverse-order might be more efficient! }
        for i := FApartments.Count - 1 downto 0 do
        begin
          apt := FApartments [i];
          thrd := apt.FThreads.ItemById [GetCurrentThreadId];
          if (thrd <> NIL) then
          begin
            Result := TRUE;
            Break;
          end;  { if }
        end;  { for }
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.HasMTA: boolean;
    var
      mta: TApartment;
    begin
      Result := FindMTA (mta);
    end;

    function TApartments.IndexOfApartment (apt: TApartment): integer;
    var
      i: integer;
    begin
      Result := -1;
      if (apt = NIL) then Exit;

      LockApartments (TRUE);
      try
        for i := 0 to Count - 1 do
          if (Items [i] = apt) then
          begin
            Result := i;
            Break;
          end;  { if }
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.NewApartment (const sName: string; at: TApartmentType; bCreateThread: boolean): TApartment;

     procedure ValidateRequest;
     begin
       { Verify only 1 MTA exists }
       if (at = atMTA) then
         Verify (not (HasMTA), 'Only 1 multithreaded apartment can be created per process!');
     end;

    var
      apt: TApartment;
    begin
      LockApartments (TRUE);
      try
        ValidateRequest;

        { create apartment }
        apt := CreateApartment (at);
        apt.Name := sName;
        FApartments.Add (apt);

        { create thread? }
        if (bCreateThread) then
        begin
          apt.Threads.NewThread;

          { generate default apartment names  }
          if (sName = '') then
            case at of
              atSTA :
                apt.Name := Format ('STA (%d)', [apt.Threads [0].ThreadId]);
              atMTA :
                apt.Name := Format ('MTA (%d)', [apt.Threads [0].ThreadId]);
            end;  { case }
        end;  { if }

        Result := apt;
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.NewPooledSTA: TApartment;
    var
      apt: TApartment;
    begin
      LockApartments (TRUE);
      try
        apt := NewApartment ('', atSTA, TRUE);
        apt.Pooled := TRUE;
        apt.Name := Format ('STA Pool (%d)', [apt.Threads [0].ThreadId]);
        Result := apt;
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.RemoveApartment (apt: TApartment): boolean;
    var
      i: integer;
    begin
      LockApartments (TRUE);
      try
        i := IndexOfApartment (apt);
        Result := DeleteApartment (i);
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.SafeRemoveApartment (apt: TApartment): boolean;
    begin
      Assert (apt <> NIL);
      LockApartments (TRUE);
      try
        Result := FALSE;
        if (apt.MarkForDelete) then Result := RemoveApartment (apt);
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;

    function TApartments.TerminateThreads: boolean;
    var
      i: integer;
    begin
      LockApartments (TRUE);
      try
        Result := TRUE;
        for i := 0 to Count - 1 do
        begin
          Result := Items [i].TerminateThreads;
          if not (Result) then Break;
        end;  { for }
      finally
        LockApartments (FALSE);
      end;  { finally }
    end;


    { TApartmentAllocator }

    constructor TApartmentAllocator.Create (am: TApartmentManagerObject);
    begin
      Assert (am <> NIL);
      inherited Create;
      FApartmentIndex := -1;
      FManager := am;
    end;

    destructor TApartmentAllocator.Destroy;
    begin
      inherited;
    end;

    function TApartmentAllocator.AllocateApartment (am: TSTAAllocMode): TApartment;
    var
      apt: TApartment;
    begin
      Result := NIL;
      with Manager do
      begin
        case am of
          amPooled :
          begin
            Apartments.LockApartments (TRUE);
            try
              { ensure pooled STA }
              if (Apartments.PooledCount < GetPooledSTACount) then
              begin
                { alloc new }
                Result := Apartments.NewPooledSTA;
                PostMessage(Application.MainForm.Handle, 8888, 51,0); // by cxg 创建一个新的
                { if alloced, assume it baby! }
                if (Result <> NIL) then Exit;
              end;  { if }

              { safety! bail if no pool! }
              if (Apartments.PooledCount <= 0) then Exit;

              { pool, round-robin! }
              while (TRUE) do
              begin
                FApartmentIndex := FApartmentIndex + 1;
                if (FApartmentIndex >= Apartments.Count) then
                  FApartmentIndex := 0;

                if (Apartments [FApartmentIndex].Pooled) then
                begin
                  Result := Apartments [FApartmentIndex];
                  PostMessage(Application.MainForm.Handle, 8888, 53,0); // by cxg 使用一个空闲的
                  Break;
                end;  { if }
              end;  { while }
            finally
              if (Result <> NIL) then Result.MarkForDelete := FALSE;  { ! }
              Apartments.LockApartments (FALSE);
              PostMessage(Application.MainForm.Handle, 8888, 52,0); // by cxg  归还池中
            end;  { finally }
          end;

          amDistinct :
          begin
            { distinct allocation. always create new STA with 1 thread }
            apt := Apartments.NewApartment ('', atSTA, TRUE);
            Verify (apt <> NIL, 'Unable to allocate a new/distinct apartment!');
            Result := apt;
          end;

          amDefault :
            { do nothing, ApartmentManager will know to create the object in the
              default/main apartment
            }
        end;  { case }
      end;  { with }
    end;


    { TApartmentStats }

    function TApartmentStats.GetGrid: TStringGrid;
    begin
      if (FGrid = NIL) then
      begin
        FGrid := TStringGrid.Create (Self);
        with FGrid do
        begin
          Visible := FALSE;
          Options := Options + [goRowSelect, goColSizing];

          DefaultColWidth := 120;
          Width := (DefaultColWidth + 5) * 3;
          FixedCols := 1;
          ColCount := 3;

          DefaultRowHeight := 18;
          FixedRows := 1;
          RowCount := 2;

          Cells [0, 0] := 'Apartment';
          Cells [1, 0] := 'Thread count';
          Cells [2, 0] := 'Object count';
        end;  { with }
        if (Owner is TWinControl) then FGrid.Parent := TWinControl (Owner);
      end;  { if }
      Result := FGrid;
    end;

    function TApartmentStats.GetTimer: TTimer;
    begin
      if (FTimer = NIL) then
      begin
        FTimer := TTimer.Create (Self);
        FTimer.Enabled := FALSE;
        FTimer.OnTimer := TimerUpdate;
      end;  { if }
      Result := FTimer;
    end;

    procedure TApartmentStats.Notification (cmp: TComponent; op: TOperation);
    begin
      if (cmp = GridParent) and (op = opRemove) then
        GridParent := NIL;
      inherited;
    end;

    procedure TApartmentStats.SetAlign (al: TAlign);
    begin
      FAlign := al;
      if not (csDesigning in ComponentState) then
        Grid.Align := al;
    end;

    procedure TApartmentStats.SetEnabled (bSet: boolean);
    begin
      FEnabled := bSet;
      if not (csDesigning in ComponentState) then
        Timer.Enabled := bSet;
    end;

    procedure TApartmentStats.SetGridParent (ctlParent: TWinControl);
    begin
      FGridParent := ctlParent;
      if not (csDesigning in ComponentState) then
        if (ctlParent <> NIL) then
          Grid.Parent := ctlParent;
    end;

    procedure TApartmentStats.SetUpdateInterval (iValue: integer);
    begin
      FUpdateInterval := iValue;
      if not (csDesigning in ComponentState) then
        Timer.Interval := iValue;
    end;

    procedure TApartmentStats.SetVisible (bSet: boolean);
    begin
      FVisible := bSet;
      if not (csDesigning in ComponentState) then
      begin
        Grid.Visible := bSet;
        if (Visible) and (Enabled) then UpdateGrid;
      end;  { if }
    end;

    procedure TApartmentStats.TimerUpdate (Sender: TObject);
    begin
      if (Visible) then UpdateGrid;
    end;

    procedure TApartmentStats.UpdateGrid;
    var
      apt: TApartment;
      sMainObjects: string;
      i, iThreadCount, iObjectCount, iMainObjectCount: integer;
    begin
      if (Apartments = NIL) then Exit;

      iThreadCount := 0;
      iObjectCount := 0;

      { Note: This grid updating process will lock the global apartments list
        meaning that any calls that need to use the apartments (such as creating
        new objects) will be blocked - which may cause serious performance problems.
        Therefore, it is highly recommended that your TApartmentStats component be
        enabled only for debugging purposes and disabled when in production.
      }
      Apartments.LockApartments (TRUE);
      try
        Grid.RowCount := Apartments.Count + 2;
        if (Apartments.Count > 0) then
        begin
          for i := 0 to Apartments.Count - 1 do
          begin
            apt := Apartments [i];
            if (apt = NIL) then break;
            { Load stats }
            with Grid do
            begin
              inc (iThreadCount, apt.Threads.Count);
              inc (iObjectCount, apt.LockCount);

              Cells [0, i + 2] := apt.Name;
              Cells [1, i + 2] := IntToStr (apt.Threads.Count);
              Cells [2, i + 2] := IntToStr (apt.LockCount);
            end;
          end;
        end;  { if }

        { calc main object count }
        iMainObjectCount := Apartments.Manager.ServerObjectCount - iObjectCount;
      finally
        Apartments.LockApartments (FALSE);
      end;  { finally }

      { totals }
      Grid.Cells [0, 1] := 'Total . . .';
      Grid.Cells [1, 1] := IntToStr (iThreadCount) + ' (+ Main Thread)';
      sMainObjects := '';
      if (iMainObjectCount >= 0) then sMainObjects := IntToStr (iMainObjectCount) + ' ';
      Grid.Cells [2, 1] := IntToStr (iObjectCount) + ' (+ ' + sMainObjects + 'Main Objects)';
    end;

    constructor TApartmentStats.Create (pOwner: TComponent);
    begin
      inherited Create (pOwner);
      Enabled := TRUE;
      UpdateInterval := 1000;
      Visible := TRUE;
      Align := alClient;
    end;

    destructor TApartmentStats.Destroy;
    begin
      { FGrid and FTimer are both owned by self, so were ok! }
      inherited;
    end;


    { TThreadedComObject }

    function TThreadedComObject.GetFTM: IUnknown;
    begin
      SelfLock.Lock (TRUE);
      try
        if (FFTM = NIL) and (cNT4DCOMSupported) then
        begin
          OleCheck (CoCreateFreeThreadedMarshaler (Self, FFTM));
        end;  { if }
        Result := FFTM;
      finally
        SelfLock.Lock (FALSE);
      end;  { finally }
    end;

    function TThreadedComObject.GetRefCountLock: TCriticalSection;
    begin
      { this routine is not thread-safe, however, this routine is guaranteed to
        be called the first time this object is created so were ok!
      }
      if (FCSRefCount = NIL) then FCSRefCount := TCriticalSection.Create;
      Result := FCSRefCount;
    end;

    function TThreadedComObject.GetSelfLock: TCriticalSection;
    begin
      // ensures SelfLock is initialized at create-time!
      Assert (FCSSelfLock <> NIL, 'SelfLock is undefined. You forgot to call inherited Initialize first in your overriden Initialize!');
      Result := FCSSelfLock;
    end;

    destructor TThreadedComObject.Destroy;
    var
      apt: TApartment;
    begin
      apt := Apartment;
      FFTM := NIL;
      if (FCSRefCount <> NIL) then FCSRefCount.Free;
      FCSRefCount := NIL;
      if (FCSSelfLock <> NIL) then FCSSelfLock.Free;
      FCSSelfLock := NIL;
      inherited;
      if (apt <> NIL) then apt.CountObject (FALSE);
    end;

    procedure TThreadedComObject.Initialize;
    begin
      FCSSelfLock := TCriticalSection.Create;
      { initialize owner apartment }
      if not (IsLibrary) then
      begin
        FApartment := ApartmentManager.Apartments.ActiveApartment;
        if (Apartment <> NIL) then Apartment.CountObject (TRUE);
      end;  { if }
      inherited;
    end;

    function TThreadedComObject.ObjAddRef: integer;
    begin
      {$IFDEF D3}
      RefCountLock.Lock (TRUE);
      try
        { make threadsafe for D3 }
        Result := inherited ObjAddRef;
      finally
        RefCountLock.Lock (FALSE);
      end;  { finally }
      {$ELSE}
      Result := inherited ObjAddRef;
      {$ENDIF}
    end;

    function TThreadedComObject.ObjRelease: integer;
    begin
      {$IFDEF D3}
      Result := 1;  // dummy
      RefCountLock.Lock (TRUE);
      try
        Result := inherited ObjRelease;
      finally
        if (Result <> 0) then RefCountLock.Lock (FALSE);
      end;  { finally }
      {$ELSE}
      Result := inherited ObjRelease;
      {$ENDIF}
    end;

    function TThreadedComObject.ObjQueryInterface (const IID: TGUID; out pObj): HResult;
    begin
      if (FTMSupported) and (IsEqualGuid (IID, IMarshal)) then
        if (FTM <> NIL) then
        begin
          { lazy-aggregate with FTM }
          Result := FTM.QueryInterface (IID, pObj);
          Exit;
        end;  { if }

      Result := inherited ObjQueryInterface (IID, pObj);
    end;


    { TThreadedAutoObject }

    function TThreadedAutoObject.GetFTM: IUnknown;
    begin
      SelfLock.Lock (TRUE);
      try
        if (FFTM = NIL) and (cNT4DCOMSupported) then
        begin
          OleCheck (CoCreateFreeThreadedMarshaler (Self, FFTM));
        end;  { if }
        Result := FFTM;
      finally
        SelfLock.Lock (FALSE);
      end;  { finally }
    end;

    function TThreadedAutoObject.GetRefCountLock: TCriticalSection;
    begin
      { this routine is not thread-safe, however, this routine is guaranteed to
        be called the first time this object is created so were ok!
      }
      if (FCSRefCount = NIL) then FCSRefCount := TCriticalSection.Create;
      Result := FCSRefCount;
    end;

    function TThreadedAutoObject.GetSelfLock: TCriticalSection;
    begin
      // ensures SelfLock is initialized at create-time!
      Assert (FCSSelfLock <> NIL, 'SelfLock is undefined. You forgot to call inherited Initialize first in your overriden Initialize!');
      Result := FCSSelfLock;
    end;

    destructor TThreadedAutoObject.Destroy;
    var
      apt: TApartment;
    begin
      apt := Apartment;
      FFTM := NIL;
      if (FCSRefCount <> NIL) then FCSRefCount.Free;
      FCSRefCount := NIL;
      if (FCSSelfLock <> NIL) then FCSSelfLock.Free;
      FCSSelfLock := NIL;
      inherited;
      if (apt <> NIL) then apt.CountObject (FALSE);
    end;

    procedure TThreadedAutoObject.Initialize;
    begin
      FCSSelfLock := TCriticalSection.Create;
      { initialize owner apartment }
      if not (IsLibrary) then
      begin
        FApartment := ApartmentManager.Apartments.ActiveApartment;
        if (Apartment <> NIL) then Apartment.CountObject (TRUE);
      end;  { if }
      inherited;
    end;

    function TThreadedAutoObject.ObjAddRef: integer;
    begin
      {$IFDEF D3}
      RefCountLock.Lock (TRUE);
      try
        { make threadsafe for D3 }
        Result := inherited ObjAddRef;
      finally
        RefCountLock.Lock (FALSE);
      end;  { finally }
      {$ELSE}
      Result := inherited ObjAddRef;
      {$ENDIF}
    end;

    function TThreadedAutoObject.ObjRelease: integer;
    begin
      {$IFDEF D3}
      Result := 1;  // dummy
      RefCountLock.Lock (TRUE);
      try
        Result := inherited ObjRelease;
      finally
        if (Result <> 0) then RefCountLock.Lock (FALSE);
      end;  { finally }
      {$ELSE}
      Result := inherited ObjRelease;
      {$ENDIF}
    end;

    function TThreadedAutoObject.ObjQueryInterface (const IID: TGUID; out pObj): HResult;
    begin
      if (FTMSupported) and (IsEqualGuid (IID, IMarshal)) then
        if (FTM <> NIL) then
        begin
          { lazy-aggregate with FTM }
          Result := FTM.QueryInterface (IID, pObj);
          Exit;
        end;  { if }

      Result := inherited ObjQueryInterface (IID, pObj);
    end;


    type
      { enhanced TVCLAutoObject. derives from TThreadedAutoObject }
      TThreadedVCLAutoObject = class (TThreadedAutoObject, IVCLComObject)
      private
        FComponent: TComponent;
        FOwnsComponent: Boolean;
      protected
        procedure FreeOnRelease;
        function Invoke(DispID: Integer; const IID: TGUID;
          LocaleID: Integer; Flags: Word; var Params;
          VarResult, ExcepInfo, ArgErr: Pointer): HResult; override;
      public
        constructor Create(Factory: TComObjectFactory; Component: TComponent);
        destructor Destroy; override;
        procedure Initialize; override;
        function ObjQueryInterface(const IID: TGUID; out Obj): HResult; override;
      end;

    { TThreadedVCLAutoObject }

    constructor TThreadedVCLAutoObject.Create(Factory: TComObjectFactory;
      Component: TComponent);
    begin
      FComponent := Component;
      CreateFromFactory (Factory, nil);
    end;

    destructor TThreadedVCLAutoObject.Destroy;
    begin
      if FComponent <> nil then
      begin
        FComponent.VCLComObject := nil;
        if FOwnsComponent then FComponent.Free;
      end;
      inherited Destroy;
    end;

    procedure TThreadedVCLAutoObject.FreeOnRelease;
    begin
      FOwnsComponent := True;
    end;

    procedure TThreadedVCLAutoObject.Initialize;
    begin
      inherited Initialize;
      if FComponent = nil then
      begin
        FComponent := TComponentClass(Factory.ComClass).Create(nil);
        FOwnsComponent := True;
      end;
      FComponent.VCLComObject := Pointer(IVCLComObject(Self));
    end;

    function TThreadedVCLAutoObject.Invoke(DispID: Integer; const IID: TGUID;
      LocaleID: Integer; Flags: Word; var Params;
      VarResult, ExcepInfo, ArgErr: Pointer): HResult;
    begin
      Result := DispInvoke(Pointer(Integer(FComponent) +
        TComponentFactory(Factory).DispIntfEntry^.IOffset),
        TComponentFactory(Factory).DispTypeInfo, DispID, Flags,
        TDispParams(Params), VarResult, ExcepInfo, ArgErr);
    end;

    function TThreadedVCLAutoObject.ObjQueryInterface(const IID: TGUID; out Obj): HResult;
    begin
      Result := inherited ObjQueryInterface(IID, Obj);
      if (Result <> 0) and (FComponent <> nil) then
        if FComponent.GetInterface(IID, Obj) then Result := 0;
    end;


    type
      TComServerHack = class (TComServerObject);

    const
      { IExternalConnection constants }
      EXTCONN_STRONG = $0001;
      EXTCONN_WEAK = $0002;
      EXTCONN_CALLABLE = $0004;


    { TThreadedComObjectFactory }

    function TThreadedComObjectFactory.CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
      out Obj): HResult;
    begin
      if (IsLibrary) then
        Result := inherited CreateInstance (UnkOuter, IID, Obj)
      else
        Result := ApartmentManager.CreateInstance (Self, DoCreateInstance, ThreadingModel, STAAllocMode, UnkOuter, iid, Obj);
    end;

    function TThreadedComObjectFactory.LockServer(fLock: BOOL): HResult;
    begin
      {$IFDEF D3}
      TComServerHack (ComServer).CountObject (fLock);
      {$ENDIF}
      Result := inherited LockServer (fLock);
    end;

    function TThreadedComObjectFactory.AddConnection (extconn: longint; reserved: longint): longint;
    begin
      if (extconn AND EXTCONN_STRONG <> 0) then TComServerHack (ComServer).CountObject (TRUE);
      Result := 2;  // dummy
    end;

    function TThreadedComObjectFactory.ReleaseConnection (extconn: longint; reserved: longint; fLastReleaseCloses: BOOL): longint;
    begin
      if (extconn AND EXTCONN_STRONG <> 0) then TComServerHack (ComServer).CountObject (FALSE);
      Result := 1;  // dummy
    end;

    function TThreadedComObjectFactory.DoCreateInstance(pApt: TApartment;
      const UnkOuter: IUnknown; const IID: TGUID; out Obj): HResult;
    begin
      Result := inherited CreateInstance (UnkOuter, IID, Obj);
    end;

    procedure TThreadedComObjectFactory.Initialize;
    begin
    end;

    constructor TThreadedComObjectFactory.CreateThreaded (ComServer: TComServerObject; ComClass: TComClass;
      const ClassID: TGUID; const ClassName, Description: string;
      Instancing: TClassInstancing; tm: TThreadingModel);
    begin
      CreateThreadedEx (ComServer, ComClass, ClassId, ClassName, Description, Instancing, tm, cDefApartmentAllocMode);
    end;

    constructor TThreadedComObjectFactory.CreateThreadedEx (ComServer: TComServerObject; ComClass: TComClass;
      const ClassID: TGUID; const ClassName, Description: string;
      Instancing: TClassInstancing; tm: TThreadingModel; am: TSTAAllocMode);
    begin
      inherited Create (ComServer, ComClass, ClassId, ClassName, Description, Instancing {$IFNDEF D3}, tm {$ENDIF});
      FSTAAllocMode := am;
      FThreadingModel := tm;
      FRegisterClass := -1;
      Initialize;
    end;

    {$IFDEF D3}
    constructor TThreadedComObjectFactory.Create (ComServer: TComServerObject; ComClass: TComClass;
      const ClassID: TGUID; const ClassName, Description: string;
      Instancing: TClassInstancing);
    begin
      CreateThreaded (ComServer, ComClass, ClassId, ClassName, Description, Instancing, cDefServerThreadingModel);
    end;
    {$ELSE}
    constructor TThreadedComObjectFactory.Create (ComServer: TComServerObject; ComClass: TComClass;
      const ClassID: TGUID; const ClassName, Description: string;
      Instancing: TClassInstancing);
    begin
      Create (ComServer, ComClass, ClassId, ClassName, Description, Instancing, cDefServerThreadingModel);
    end;

    constructor TThreadedComObjectFactory.Create (ComServer: TComServerObject; ComClass: TComClass;
      const ClassID: TGUID; const ClassName, Description: string;
      Instancing: TClassInstancing; tm: TThreadingModel);
    begin
      Create (ComServer, ComClass, ClassId, ClassName, Description, Instancing, tm, cDefApartmentAllocMode);
    end;

    constructor TThreadedComObjectFactory.Create (ComServer: TComServerObject; ComClass: TComClass;
      const ClassID: TGUID; const ClassName, Description: string;
      Instancing: TClassInstancing; tm: TThreadingModel; am: TSTAAllocMode);
    begin
      inherited Create (ComServer, ComClass, ClassId, ClassName, Description, Instancing, tm);
      FSTAAllocMode := am;
      FThreadingModel := tm;
      FRegisterClass := -1;
      Initialize;
    end;
    {$ENDIF}

    destructor TThreadedComObjectFactory.Destroy;
    begin
      RegisterClass (FALSE);
      inherited;
    end;

    procedure TThreadedComObjectFactory.RegisterClass (bRegister: boolean);
    begin
      RegisterClassFactory (bRegister, ClassId, Self, Instancing, FRegisterClass);
    end;

    procedure TThreadedComObjectFactory.UpdateRegistry (bRegister: boolean);
    var
      sServerKey: string;
    begin
      if (bRegister) and (IsLibrary) then
      begin
        { remove ThreadingModel value first! }
        sServerKey := 'CLSID\' + GuidToString (ClassID) + '\' + ComServer.ServerKey;
        DeleteRegValue (sServerKey, 'ThreadingModel');
        {$IFDEF D3}
        if (ThreadingModel <> tmSingle) then
        begin
          inherited;
          CreateRegKey (sServerKey, 'ThreadingModel', ThreadingModelFlags [FThreadingModel]);
          Exit;
        end;
        {$ENDIF}
      end;  { if }
      inherited;
    end;


    { TThreadedAutoObjectFactory }

    function TThreadedAutoObjectFactory.CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
      out Obj): HResult;
    begin
      if (IsLibrary) then
        Result := inherited CreateInstance (UnkOuter, IID, Obj)
      else
        Result := ApartmentManager.CreateInstance (Self, DoCreateInstance, ThreadingModel, STAAllocMode, UnkOuter, iid, Obj);
    end;

    function TThreadedAutoObjectFactory.LockServer(fLock: BOOL): HResult;
    begin
      {$IFDEF D3}
      TComServerHack (ComServer).CountObject (fLock);
      {$ENDIF}
      Result := inherited LockServer (fLock);
    end;

    function TThreadedAutoObjectFactory.AddConnection (extconn: longint; reserved: longint): longint;
    begin
      if (extconn AND EXTCONN_STRONG <> 0) then TComServerHack (ComServer).CountObject (TRUE);
      Result := 2;  // dummy
    end;

    function TThreadedAutoObjectFactory.ReleaseConnection (extconn: longint; reserved: longint; fLastReleaseCloses: BOOL): longint;
    begin
      if (extconn AND EXTCONN_STRONG <> 0) then TComServerHack (ComServer).CountObject (FALSE);
      Result := 1;  // dummy
    end;

    function TThreadedAutoObjectFactory.DoCreateInstance (pApt: TApartment;
      const UnkOuter: IUnknown; const IID: TGUID; out Obj): HResult; stdcall;
    begin
      Result := inherited CreateInstance (UnkOuter, IID, Obj);
    end;

    procedure TThreadedAutoObjectFactory.Initialize;
    begin
    end;

    constructor TThreadedAutoObjectFactory.CreateThreaded (ComServer: TComServerObject; AutoClass: TAutoClass;
      const ClassID: TGUID; Instancing: TClassInstancing; tm: TThreadingModel);
    begin
      CreateThreadedEx (ComServer, AutoClass, ClassId, Instancing, tm, cDefApartmentAllocMode);
    end;

    constructor TThreadedAutoObjectFactory.CreateThreadedEx (ComServer: TComServerObject; AutoClass: TAutoClass;
      const ClassID: TGUID; Instancing: TClassInstancing; tm: TThreadingModel; am: TSTAAllocMode);
    begin
      inherited Create (ComServer, AutoClass, ClassId, Instancing {$IFNDEF D3}, tm {$ENDIF});
      FSTAAllocMode := am;
      FThreadingModel := tm;
      FRegisterClass := -1;
      Initialize;
    end;

    {$IFDEF D3}
    constructor TThreadedAutoObjectFactory.Create(ComServer: TComServerObject; AutoClass: TAutoClass;
      const ClassID: TGUID; Instancing: TClassInstancing);
    begin
      CreateThreaded (ComServer, AutoClass, ClassId, Instancing, cDefServerThreadingModel);
    end;
    {$ELSE}
    constructor TThreadedAutoObjectFactory.Create(ComServer: TComServerObject; AutoClass: TAutoClass;
      const ClassID: TGUID; Instancing: TClassInstancing);
    begin
      Create (ComServer, AutoClass, ClassId, Instancing, cDefServerThreadingModel);
    end;

    constructor TThreadedAutoObjectFactory.Create(ComServer: TComServerObject; AutoClass: TAutoClass;
      const ClassID: TGUID; Instancing: TClassInstancing;
      tm: TThreadingModel);
    begin
      Create (ComServer, AutoClass, ClassId, Instancing, tm, cDefApartmentAllocMode);
    end;

    constructor TThreadedAutoObjectFactory.Create(ComServer: TComServerObject; AutoClass: TAutoClass;
      const ClassID: TGUID; Instancing: TClassInstancing;
      tm: TThreadingModel; am: TSTAAllocMode);
    begin
      inherited Create (ComServer, AutoClass, ClassId, Instancing, tm);
      FSTAAllocMode := am;
      FThreadingModel := tm;
      FRegisterClass := -1;
      Initialize;
    end;
    {$ENDIF}

    destructor TThreadedAutoObjectFactory.Destroy;
    begin
      RegisterClass (FALSE);
      inherited;
    end;

    procedure TThreadedAutoObjectFactory.RegisterClass (bRegister: boolean);
    begin
      RegisterClassFactory (bRegister, ClassId, Self, Instancing, FRegisterClass);
    end;

    procedure TThreadedAutoObjectFactory.UpdateRegistry (bRegister: boolean);
    var
      sServerKey: string;
    begin
      if (bRegister) and (IsLibrary) then
      begin
        { remove ThreadingModel value first! }
        sServerKey := 'CLSID\' + GuidToString (ClassID) + '\' + ComServer.ServerKey;
        DeleteRegValue (sServerKey, 'ThreadingModel');
        {$IFDEF D3}
        if (ThreadingModel <> tmSingle) then
        begin
          inherited;
          CreateRegKey (sServerKey, 'ThreadingModel', ThreadingModelFlags [FThreadingModel]);
          Exit;
        end;
        {$ENDIF}
      end;  { if }
      inherited;
    end;


    { TThreadedClassFactory }

    function TThreadedClassFactory.CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
      out Obj): HResult;
    begin
      if (IsLibrary) then
        Result := inherited CreateInstance (UnkOuter, IID, Obj)
      else
        Result := ApartmentManager.CreateInstance (Self, DoCreateInstance, ThreadingModel, STAAllocMode, UnkOuter, iid, Obj);
    end;

    function TThreadedClassFactory.LockServer(fLock: BOOL): HResult;
    begin
      {$IFDEF D3}
      TComServerHack (ComServer).CountObject (fLock);
      {$ENDIF}
      Result := inherited LockServer (fLock);
    end;

    function TThreadedClassFactory.AddConnection (extconn: longint; reserved: longint): longint;
    begin
      if (extconn AND EXTCONN_STRONG <> 0) then TComServerHack (ComServer).CountObject (TRUE);
      Result := 2;  // dummy
    end;

    function TThreadedClassFactory.ReleaseConnection (extconn: longint; reserved: longint; fLastReleaseCloses: BOOL): longint;
    begin
      if (extconn AND EXTCONN_STRONG <> 0) then TComServerHack (ComServer).CountObject (FALSE);
      Result := 1;  // dummy
    end;

    function TThreadedClassFactory.DoCreateInstance(pApt: TApartment;
      const UnkOuter: IUnknown; const IID: TGUID; out Obj): HResult; stdcall;
    begin
      Result := inherited CreateInstance (UnkOuter, IID, Obj);
    end;

    procedure TThreadedClassFactory.Initialize;
    begin
    end;

    constructor TThreadedClassFactory.CreateThreaded (ComServer: TComServerObject;
      ComponentClass: TComponentClass; const ClassID: TGUID;
      Instancing: TClassInstancing; tm: TThreadingModel);
    begin
      CreateThreadedEx (ComServer, ComponentClass, ClassId, Instancing, tm, cDefApartmentAllocMode);
    end;

    constructor TThreadedClassFactory.CreateThreadedEx (ComServer: TComServerObject;
      ComponentClass: TComponentClass; const ClassID: TGUID;
      Instancing: TClassInstancing; tm: TThreadingModel; am: TSTAAllocMode);
    begin
      inherited Create (ComServer, ComponentClass, ClassId, Instancing {$IFNDEF D3}, tm {$ENDIF});
      FSTAAllocMode := am;
      FThreadingModel := tm;
      FRegisterClass := -1;
      Initialize;
    end;

    {$IFDEF D3}
    constructor TThreadedClassFactory.Create (ComServer: TComServerObject;
      ComponentClass: TComponentClass; const ClassID: TGUID;
      Instancing: TClassInstancing);
    begin
      CreateThreaded (ComServer, ComponentClass, ClassId, Instancing, cDefServerThreadingModel);
    end;
    {$ELSE}
    constructor TThreadedClassFactory.Create(ComServer: TComServerObject;
      ComponentClass: TComponentClass; const ClassID: TGUID;
      Instancing: TClassInstancing);
    begin
      Create (ComServer, ComponentClass, ClassId, Instancing, cDefServerThreadingModel);
    end;

    constructor TThreadedClassFactory.Create(ComServer: TComServerObject;
      ComponentClass: TComponentClass; const ClassID: TGUID;
      Instancing: TClassInstancing; tm: TThreadingModel);
    begin
      Create (ComServer, ComponentClass, ClassId, Instancing, tm, cDefApartmentAllocMode);
    end;

    constructor TThreadedClassFactory.Create(ComServer: TComServerObject;
      ComponentClass: TComponentClass; const ClassID: TGUID;
      Instancing: TClassInstancing; tm: TThreadingModel; am: TSTAAllocMode);
    begin
      inherited Create (ComServer, ComponentClass, ClassId, Instancing, tm);
      FSTAAllocMode := am;
      FThreadingModel := tm;
      FRegisterClass := -1;
      Initialize;
    end;
    {$ENDIF}

    destructor TThreadedClassFactory.Destroy;
    begin
      RegisterClass (FALSE);
      inherited;
    end;

    function TThreadedClassFactory.CreateComObject (const Controller: IUnknown): TComObject;
    begin
      Result := TThreadedVCLAutoObject.CreateFromFactory (Self, Controller);
    end;

    procedure TThreadedClassFactory.RegisterClass (bRegister: boolean);
    begin
      RegisterClassFactory (bRegister, ClassId, Self, Instancing, FRegisterClass);
    end;

    procedure TThreadedClassFactory.UpdateRegistry (bRegister: boolean);
    var
      sServerKey: string;
    begin
      if (bRegister) and (IsLibrary) then
      begin
        { remove ThreadingModel value first! }
        sServerKey := 'CLSID\' + GuidToString (ClassID) + '\' + ComServer.ServerKey;
        DeleteRegValue (sServerKey, 'ThreadingModel');
        {$IFDEF D3}
        if (ThreadingModel <> tmSingle) then
        begin
          inherited;
          CreateRegKey (sServerKey, 'ThreadingModel', ThreadingModelFlags [FThreadingModel]);
          Exit;
        end;
        {$ENDIF}
      end;  { if }
      inherited;
    end;


    { TSingletonComObject }

    function TSingletonComObject.QueryInterface(const IID: TGUID; out Obj): HResult;
    begin
      Result := ObjQueryInterface (IID, Obj);
    end;

    function TSingletonComObject._AddRef: Integer;
    begin
      Result := ObjAddRef;
    end;

    function TSingletonComObject._Release: Integer;
    begin
      Result := ObjRelease;
    end;

    function TSingletonComObject.CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
      out Obj): HResult;
    begin
      if (UnkOuter <> NIL) then
        Result := CLASS_E_NOAGGREGATION
      else
        Result := QueryInterface (IID, Obj);
    end;

    function TSingletonComObject.ObjAddRef: Integer;
    begin
      Result := inherited _AddRef;
    end;

    function TSingletonComObject.ObjQueryInterface(const IID: TGUID; out Obj): Integer;
    begin
      Result := inherited QueryInterface (IID, Obj);
    end;

    function TSingletonComObject.ObjRelease: Integer;
    begin
      Result := inherited _Release;
    end;

    procedure InitializeSingletonComObject (ComServer: TComServerObject;
      SingletonClass: TSingletonComClass; const clsid: TGUID;
      const sClassName, sDescription: string);
    begin
      SingletonClass.Create (ComServer, TComObject {dummy}, clsid, sClassName, sDescription, ciMultiInstance);
    end;

    function TSingletonComObject.SafeCallException(ExceptObject: TObject;
      ExceptAddr: Pointer): HResult;
    begin
      Result := HandleSafeCallException (ExceptObject, ExceptAddr,
        ErrorIID, ProgID, ComServer.HelpFileName);
    end;

    { TSingletonAutoObject }

    function TSingletonAutoObject.QueryInterface(const IID: TGUID; out Obj): HResult;
    begin
      Result := ObjQueryInterface (IID, Obj);
    end;

    function TSingletonAutoObject._AddRef: Integer;
    begin
      Result := ObjAddRef;
    end;

    function TSingletonAutoObject._Release: Integer;
    begin
      Result := ObjRelease;
    end;

    function TSingletonAutoObject.GetClassInfo(out TypeInfo: ITypeInfo): HResult;
    begin
      TypeInfo := ClassInfo;
      Result := S_OK;
    end;

    function TSingletonAutoObject.GetIDsOfNames(const IID: TGUID; Names: Pointer;
      NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
    begin
      Result := DispGetIDsOfNames (DispTypeInfo, Names, NameCount, DispIDs);
    end;

    function TSingletonAutoObject.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult;
    begin
      pointer (TypeInfo) := nil;
      if (Index <> 0) then
      begin
        Result := DISP_E_BADINDEX;
        Exit;
      end;  { if }
      ITypeInfo (TypeInfo) := DispTypeInfo;
      Result := S_OK;
    end;

    function TSingletonAutoObject.GetTypeInfoCount(out Count: Integer): HResult;
    begin
      Count := 1;
      Result := S_OK;
    end;

    function TSingletonAutoObject.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;
    const
      INVOKE_PROPERTYSET = INVOKE_PROPERTYPUT or INVOKE_PROPERTYPUTREF;
    begin
      if Flags and INVOKE_PROPERTYSET <> 0 then Flags := INVOKE_PROPERTYSET;
      Result := DispTypeInfo.Invoke (Pointer(Integer(Self) + DispIntfEntry.IOffset),
        DispID, Flags, TDispParams(Params), VarResult, ExcepInfo, ArgErr);
    end;

    function TSingletonAutoObject.CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
      out Obj): HResult;
    begin
      if (UnkOuter <> NIL) then
        Result := CLASS_E_NOAGGREGATION
      else
        Result := QueryInterface (IID, Obj);
    end;

    function TSingletonAutoObject.GetIntfEntry (Guid: TGUID): PInterfaceEntry;
    begin
      Result := GetInterfaceEntry (Guid);
    end;

    function TSingletonAutoObject.ObjAddRef: Integer;
    begin
      Result := inherited _AddRef;
    end;

    function TSingletonAutoObject.ObjQueryInterface(const IID: TGUID; out Obj): Integer;
    begin
      Result := inherited QueryInterface (IID, Obj);
    end;

    function TSingletonAutoObject.ObjRelease: Integer;
    begin
      Result := inherited _Release;
    end;

    procedure InitializeSingletonAutoObject (ComServer: TComServerObject;
      SingletonClass: TSingletonAutoClass; const clsid: TGUID);
    begin
      SingletonClass.Create (ComServer, TAutoObject {dummy}, clsid, ciMultiInstance)
    end;

    function TSingletonAutoObject.SafeCallException(ExceptObject: TObject;
      ExceptAddr: Pointer): HResult;
    begin
      Result := HandleSafeCallException (ExceptObject, ExceptAddr,
        ErrorIID, ProgID, ComServer.HelpFileName);
    end;

    type
      TObjectList = class
      protected
        FApartment: TApartment;
        FCSLock: TCriticalSection;
        FItems: TList;
        function GetCount: integer;
        function GetUnks (i: integer): IUnknown;
        procedure Lock (bLock: boolean);
        procedure SetUnks (i: integer; pUnk: IUnknown);
        property Unks [i: integer]: IUnknown read GetUnks write SetUnks;
      public
        constructor Create (pApt: TApartment);
        destructor Destroy; override;
        procedure Add (pUnk: IUnknown);
        procedure Clear;
        function Compact: boolean;
        property Count: integer read GetCount;
      end;

      TDefaultApartmentThread = class (TApartmentThread)
      protected
        function GetObjects: TObjectList;
        procedure InstanceCreated (pciInfo: PCreateInstanceInfo; pUnk: IUnknown); override;
        property Objects: TObjectList read GetObjects;
      end;

      TDefaultApartmentThreads = class (TApartmentThreads)
      protected
        function CreateThread: TApartmentThread; override;
      end;

      TDefaultApartment = class (TApartment)
      protected
        FHasObjects: boolean;
        FObjects: TObjectList;
        function CreateThreads: TApartmentThreads; override;
      public
        constructor Create (Owner: TApartments; at: TApartmentType); override;
        destructor Destroy; override;
        function GarbageCollect: boolean; override;
        property HasObjects: boolean read FHasObjects write FHasObjects;
        property Objects: TObjectList read FObjects;
      end;

      TDefaultApartments = class (TApartments)
      protected
        function CreateApartment (at: TApartmentType): TApartment; override;
      end;

    { TObjectList }

    function TObjectList.GetCount: integer;
    begin
      Lock (TRUE);
      try
        Result := FItems.Count;
      finally
        Lock (FALSE);
      end;  { finally }
    end;

    function TObjectList.GetUnks (i: integer): IUnknown;
    begin
      Lock (TRUE);
      try
        Result := IUnknown (FItems [i]);
      finally
        Lock (FALSE);
      end;  { finally }
    end;

    procedure TObjectList.Lock (bLock: boolean);
    begin
      FCSLock.Lock (bLock);
    end;

    procedure TObjectList.SetUnks (i: integer; pUnk: IUnknown);
    begin
      Lock (TRUE);
      try
        if (FItems [i] <> NIL) then IUnknown (FItems [i])._Release;
        FItems [i] := pointer (pUnk);
        if (pUnk <> NIL) then IUnknown (FItems [i])._AddRef;
      finally
        Lock (FALSE);
      end;  { finally }
    end;

    constructor TObjectList.Create (pApt: TApartment);
    begin
      Assert (pApt <> NIL);
      inherited Create;
      FCSLock := TCriticalSection.Create;
      FItems := TList.Create;
      FApartment := pApt;
    end;

    destructor TObjectList.Destroy;
    begin
      Clear;
      FItems.Free;
      FCSLock.Free;
      inherited;
    end;

    procedure TObjectList.Add (pUnk: IUnknown);
    var
      i: integer;
    begin
      Assert (pUnk <> NIL);
      Lock (TRUE);
      try
        i := FItems.Add (NIL);
        if (i >= 0) then
        begin
          FApartment.CountObject (TRUE);
          Unks [i] := pUnk;
        end;  { if }
      finally
        Lock (FALSE);
      end;  { finally }
    end;

    procedure TObjectList.Clear;
    var
      i: integer;
    begin
      Lock (TRUE);
      try
        for i := Count - 1 downto 0 do
          Unks [i] := NIL;
        FItems.Clear;
      finally
        Lock (FALSE);
      end;  { finally }
    end;

    { releases unused objects. returns TRUE if everything was just released! }
    function TObjectList.Compact: boolean;
    var
      i, iLastCount: integer;
    begin
      Result := FALSE;
      Lock (TRUE);
      try
        for i := Count - 1 downto 0 do
        begin
          { note: must manipulate as raw IUnknowns to avoid Delphi's automatic
            refcounting!
          }
          IUnknown (FItems [i])._AddRef;
          if (IUnknown (FItems [i])._Release = 1) then
          begin
            Unks [i] := NIL;
            iLastCount := FApartment.CountObject (FALSE);
            if (iLastCount = 0) then Result := TRUE;
          end;  { if }
        end;  { for }
        FItems.Pack;
      finally
        Lock (FALSE);
      end;  { finally }
    end;


    { TDefaultApartmentThread }

    function TDefaultApartmentThread.GetObjects: TObjectList;
    begin
      Result := TObjectList (TDefaultApartment (Apartment).Objects);
    end;

    procedure TDefaultApartmentThread.InstanceCreated (pciInfo: PCreateInstanceInfo; pUnk: IUnknown);
    var
      pciData: PCreateInstanceData;
    begin
      { we don't need to track a threaded com class because they already know how
        to refcount their corresponding apartments. this way, we're a lot more
        efficient!
      }
      if (pciInfo <> NIL) then
      begin
        pciData := pciInfo^.Data;
        if (pciData <> NIL) then
          if (pciData^.IsThreadedComClass) then Exit;
      end;  { if }

      { can't enlist object if calling thread is not the main thread (apartment
        where all class factories are registered) because we'd be violating the
        rules of COM threading!
      }
      if (GetCurrentThreadId <> MainThreadId) then Exit;

      { add object into manageables list }
      Objects.Add (pUnk);
      TDefaultApartment (Apartment).HasObjects := TRUE;
    end;


    { TDefaultApartmentThreads }

    function TDefaultApartmentThreads.CreateThread: TApartmentThread;
    begin
      Result := TDefaultApartmentThread.Create (Self);
    end;


    { TDefaultApartment }

    function TDefaultApartment.CreateThreads: TApartmentThreads;
    begin
      Result := TDefaultApartmentThreads.Create (Self);
    end;

    constructor TDefaultApartment.Create (Owner: TApartments; at: TApartmentType);
    begin
      inherited Create (Owner, at);
      FObjects := TObjectList.Create (Self);
    end;

    destructor TDefaultApartment.Destroy;
    begin
      FObjects.Free;
      inherited;
    end;

    function TDefaultApartment.GarbageCollect: boolean;
    begin
      Result := FALSE;
      if not (HasObjects) then Exit;
      Result := Objects.Compact;
    end;


    { TDefaultApartments }

    function TDefaultApartments.CreateApartment (at: TApartmentType): TApartment;
    begin
      Result := TDefaultApartment.Create (Self, at);
    end;


    { TDefaultApartmentManager }

    function TDefaultApartmentManager.GetApartments: TApartments;
    begin
      Result := FApartments;
    end;

    procedure TDefaultApartmentManager.TimerUpdate (pSender: TObject);
    const
      cDiv = 3;
    begin
      { regularly clean out apartments! }
      FGCTimer.Enabled := FALSE;
      try
        Apartments.GarbageCollect ((Apartments.Count DIV cDiv) + cDiv);
      finally
        FGCTimer.Enabled := TRUE;
      end;  { finally }
    end;

    constructor TDefaultApartmentManager.Create;
    begin
      inherited Create;
      FApartments := TDefaultApartments.Create (Self);
      FGC := TApartmentsGC.Create (Self, tpIdle);
      FGCTimer := TTimer.Create (NIL);
      FGCTimer.Interval := cDefApartmentGCTimerInterval;
      FGCTimer.OnTimer := TimerUpdate;
    end;

    destructor TDefaultApartmentManager.Destroy;
    begin
      FShuttingDown := TRUE;
      FGCTimer.Free;
      Apartments.GarbageCollect (-1);
      FGC.Free;
      FApartments.Free;
      inherited;
    end;

    procedure TDefaultApartmentManager.GarbageCollect (Sender: TObject);
    begin
      FGC.Activate (Sender);
    end;

    function TDefaultApartmentManager.GetPooledSTACount: integer;
    begin
      Result := cDefApartmentPoolCount;
    end;

    { this is meant to be called after your LastReleased handler! }
    procedure TDefaultApartmentManager.LastReleased (var bShutdown: boolean);
    begin
      if (bShutdown) then
      begin
        FShuttingDown := TRUE;
        GCTimer.Enabled := FALSE;
        if not (IsLibrary) then
        begin
          if (cNT4DCOMSupported) then CoSuspendClassObjects;
          PostThreadMessage (MainThreadID, WM_QUIT, 0, 0);  // D3 fix!
        end;  { if }
      end;  { if }
    end;

    procedure TDefaultApartmentManager.Resume;
    begin
      FShuttingDown := FALSE;
      GCTimer.Enabled := TRUE;
    end;

    function TDefaultApartmentManager.ServerIsShuttingDown: boolean;
    begin
      Result := FShuttingDown;
    end;


    procedure Register;
    begin
      RegisterComponents ('ThreadComLib', [TApartmentStats]);
    end;

    { init }

    procedure InitializeNT4DCOMExtensions;
    var
      hOle32: THandle;
    begin
      hOle32 := GetModuleHandle ('ole32.dll');
      if (hOle32 <> 0) then
      begin
        {$IFDEF D3}  //D3
        @CoInitializeEx := GetProcAddress (hOle32, 'CoInitializeEx');
        cNT4DCOMSupported := (@CoInitializeEx <> NIL);
        if (cNT4DCOMSupported) then
        begin
          @CoAddRefServerProcess := GetProcAddress (hOle32, 'CoAddRefServerProcess');
          @CoReleaseServerProcess := GetProcAddress (hOle32, 'CoReleaseServerProcess');
          @CoSuspendClassObjects := GetProcAddress (hOle32, 'CoSuspendClassObjects');
          @CoResumeClassObjects := GetProcAddress (hOle32, 'CoResumeClassObjects');
        end;  { if }
        {$ELSE}
        cNT4DCOMSupported := (@CoInitializeEx <> NIL);
        {$ENDIF}

        { bind to CoCreateFTM for D3 and D4! }
        if (cNT4DCOMSupported) then
        begin
          @CoCreateFreeThreadedMarshaler := GetProcAddress (hOle32, 'CoCreateFreeThreadedMarshaler');
        end;  { if }
      end;  { if }
    end;

    initialization
      csApartmentManager := TCriticalSection.Create;
     
      { initialize NT4 DCOM extension APIs }
      InitializeNT4DCOMExtensions;

      if not (IsLibrary) then
      begin
        { ensure unique values }
        WM_CREATEOBJECTINTHREAD := RegisterWindowMessage ('WM_CREATEOBJECTINTHREAD');
        WM_GARBAGECOLLECTCOMSERVER := RegisterWindowMessage ('WM_GARBAGECOLLECTCOMSERVER');
      end;  { if }

    finalization
      DestroyDefaultApartmentManager;
      csApartmentManager.Free;
      csApartmentManager := NIL;
    end.

  • 相关阅读:
    git 备忘录
    模拟HTTP协议接收请求并返回信息
    微信公众号支付回调页面处理asp.net
    WinForm下判断文件和文件夹是否存在
    C# 如何判断ie版本号和获取注册表中的信息
    【转】GDI+中发生一般性错误的解决办法
    c# winform 获取当前程序运行根目录
    模拟按下某快捷键:keybd_event使用方法
    如何使用存储过程来实现分页功能
    用ASP.NET实现下载远程图片保存到本地的方法 保存抓取远程图片的方法
  • 原文地址:https://www.cnblogs.com/hnxxcxg/p/2319963.html
Copyright © 2020-2023  润新知