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.
本文来自博客园,作者:{咏南中间件},转载请注明原文链接:https://www.cnblogs.com/hnxxcxg/archive/2012/01/11/2319963.html