home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 1999 February / DPPCPRO0299.ISO / February / Delphi / Install / DATA.Z / OLEAUTO.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-06-09  |  40.2 KB  |  1,530 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Visual Component Library                 }
  5. {                                                       }
  6. {       Copyright (c) 1996 Borland International        }
  7. {                                                       }
  8. {*******************************************************}
  9.  
  10. unit OleAuto;
  11.  
  12. {$R-}
  13.  
  14. interface
  15.  
  16. uses Windows, Ole2, OleCtl, SysUtils;
  17.  
  18. const
  19.  
  20. { Maximum number of dispatch arguments }
  21.  
  22.   MaxDispArgs = 32;
  23.  
  24. type
  25.  
  26. { Forward declarations }
  27.  
  28.   TAutoObject = class;
  29.  
  30. { Dispatch interface for TAutoObject }
  31.  
  32.   TAutoDispatch = class(IDispatch)
  33.   private
  34.     FAutoObject: TAutoObject;
  35.   public
  36.     constructor Create(AutoObject: TAutoObject);
  37.     function QueryInterface(const iid: TIID; var obj): HResult; override;
  38.     function AddRef: Longint; override;
  39.     function Release: Longint; override;
  40.     function GetTypeInfoCount(var ctinfo: Integer): HResult; override;
  41.     function GetTypeInfo(itinfo: Integer; lcid: TLCID;
  42.       var tinfo: ITypeInfo): HResult; override;
  43.     function GetIDsOfNames(const iid: TIID; rgszNames: POleStrList;
  44.       cNames: Integer; lcid: TLCID; rgdispid: PDispIDList): HResult; override;
  45.     function Invoke(dispIDMember: TDispID; const iid: TIID; lcid: TLCID;
  46.       flags: Word; var dispParams: TDispParams; varResult: PVariant;
  47.       excepInfo: PExcepInfo; argErr: PInteger): HResult; override;
  48.     function GetAutoObject: TAutoObject; virtual; stdcall;
  49.     property AutoObject: TAutoObject read FAutoObject;
  50.   end;
  51.  
  52. { TAutoObject - Automation object base class. An automation class is
  53.   implemented by deriving a new class from TAutoObject, and declaring methods
  54.   and properties in an "automated" section in the new class. To expose an
  55.   automation class to external OLE Automation Controllers, the unit that
  56.   implements the automation class must call Automation.RegisterClass in its
  57.   initialization section, passing in a TAutoClassInfo structure. Once a
  58.   class has been registered in this way, the global Automation object
  59.   automatically manages all aspects of interfacing with the OLE Automation
  60.   APIs.
  61.  
  62.   When an external OLE Automation Controller requests an instance of an
  63.   automation class, the Create constructor is called to create the object,
  64.   and when all external references to the object disappear, the Destroy
  65.   destructor is called to destroy the object. As is the case with all OLE
  66.   objects, automation objects are reference counted. }
  67.  
  68.   TAutoObject = class(TObject)
  69.   private
  70.     FRefCount: Integer;
  71.     FAutoDispatch: TAutoDispatch;
  72.     function GetIDsOfNames(Names: POleStrList; Count: Integer;
  73.       DispIDs: PDispIDList): HResult;
  74.     function GetOleObject: Variant;
  75.     function Invoke(DispID: TDispID; Flags: Integer; var Params: TDispParams;
  76.       VarResult: PVariant; ExcepInfo: PExcepInfo; ArgErr: PInteger): HResult;
  77.     procedure InvokeMethod(AutoEntry, Args, Result: Pointer);
  78.     function QueryInterface(const iid: TIID; var obj): HResult;
  79.   protected
  80.     function CreateAutoDispatch: TAutoDispatch; virtual;
  81.     procedure GetExceptionInfo(ExceptObject: TObject;
  82.       var ExcepInfo: TExcepInfo); virtual;
  83.   public
  84.     constructor Create; virtual;
  85.     destructor Destroy; override;
  86.     function AddRef: Integer;
  87.     function Release: Integer;
  88.     property AutoDispatch: TAutoDispatch read FAutoDispatch;
  89.     property OleObject: Variant read GetOleObject;
  90.     property RefCount: Integer read FRefCount;
  91.   end;
  92.  
  93. { Automation object class reference }
  94.  
  95.   TAutoClass = class of TAutoObject;
  96.  
  97. { Instancing mode for local server automation classes }
  98.  
  99.   TAutoClassInstancing = (acInternal, acSingleInstance, acMultiInstance);
  100.  
  101. { Automation class registration info }
  102.  
  103.   TAutoClassInfo = record
  104.     AutoClass: TAutoClass;
  105.     ProgID: string;
  106.     ClassID: string;
  107.     Description: string;
  108.     Instancing: TAutoClassInstancing;
  109.   end;
  110.  
  111. { Class registry entry }
  112.  
  113.   TRegistryClass = class
  114.   private
  115.     FNext: TRegistryClass;
  116.     FAutoClass: TAutoClass;
  117.     FProgID: string;
  118.     FClassID: TCLSID;
  119.     FDescription: string;
  120.     FInstancing: TAutoClassInstancing;
  121.     FRegister: Longint;
  122.   public
  123.     constructor Create(const AutoClassInfo: TAutoClassInfo);
  124.     destructor Destroy; override;
  125.     procedure UpdateRegistry(Register: Boolean);
  126.   end;
  127.  
  128. { Application start mode }
  129.  
  130.   TStartMode = (smStandalone, smAutomation, smRegServer, smUnregServer);
  131.  
  132. { Automation manager event types }
  133.  
  134.   TLastReleaseEvent = procedure(var Shutdown: Boolean) of object;
  135.  
  136. { Automation manager object }
  137.  
  138.   TAutomation = class
  139.   private
  140.     FRegistryList: TRegistryClass;
  141.     FAutoObjectCount: Integer;
  142.     FClassFactoryCount: Integer;
  143.     FSaveInitProc: Pointer;
  144.     FIsInprocServer: Boolean;
  145.     FStartMode: TStartMode;
  146.     FOnLastRelease: TLastReleaseEvent;
  147.     procedure CountAutoObject(Created: Boolean);
  148.     procedure Initialize;
  149.     procedure LastReleased;
  150.   public
  151.     constructor Create;
  152.     destructor Destroy; override;
  153.     procedure RegisterClass(const AutoClassInfo: TAutoClassInfo);
  154.     procedure UpdateRegistry(Register: Boolean);
  155.     property AutoObjectCount: Integer read FAutoObjectCount;
  156.     property IsInprocServer: Boolean read FIsInprocServer write FIsInprocServer;
  157.     property StartMode: TStartMode read FStartMode;
  158.     property OnLastRelease: TLastReleaseEvent read FOnLastRelease write FOnLastRelease;
  159.   end;
  160.  
  161. { OLE exception classes }
  162.  
  163.   EOleError = class(Exception);
  164.  
  165.   EOleSysError = class(EOleError)
  166.   private
  167.     FErrorCode: Integer;
  168.   public
  169.     constructor Create(ErrorCode: Integer);
  170.     property ErrorCode: Integer read FErrorCode;
  171.   end;
  172.  
  173.   EOleException = class(EOleError)
  174.   private
  175.     FErrorCode: Integer;
  176.     FSource: string;
  177.     FHelpFile: string;
  178.   public
  179.     constructor Create(const ExcepInfo: TExcepInfo);
  180.     property ErrorCode: Integer read FErrorCode;
  181.     property HelpFile: string read FHelpFile;
  182.     property Source: string read FSource;
  183.   end;
  184.  
  185. { Dispatch call descriptor }
  186.  
  187.   PCallDesc = ^TCallDesc;
  188.   TCallDesc = packed record
  189.     CallType: Byte;
  190.     ArgCount: Byte;
  191.     NamedArgCount: Byte;
  192.     ArgTypes: array[0..255] of Byte;
  193.   end;
  194.  
  195. var
  196.   Automation: TAutomation;
  197.  
  198. { CreateOleObject creates an OLE automation object of the given class. }
  199.  
  200. function CreateOleObject(const ClassName: string): Variant;
  201.  
  202. { GetActiveOleObject returns the active object for the given class. }
  203.  
  204. function GetActiveOleObject(const ClassName: string): Variant;
  205.  
  206. { The DllXXXX routines implement the required entry points of an in-process
  207.   automation server DLL. These routines must be exported by the DLL using
  208.   an "exports" clause in the library's main module. }
  209.  
  210. function DllGetClassObject(const CLSID: TCLSID; const IID: TIID;
  211.   var Obj): HResult; stdcall;
  212. function DllCanUnloadNow: HResult; stdcall;
  213. function DllRegisterServer: HResult; stdcall;
  214. function DllUnregisterServer: HResult; stdcall;
  215.  
  216. { VarFromInterface returns a variant that contains the a reference to the
  217.   IDispatch interface of the given IUnknown interface. If the Unknown
  218.   parameter is NIL, the resulting variant is set to Unassigned. }
  219.  
  220. function VarFromInterface(Unknown: IUnknown): Variant;
  221.  
  222. { VarToInterface returns the IDispatch interface reference stored in the
  223.   given variant. An exception is raised if the variant does not contain
  224.   an IDispatch interface. VarToInterface does not affect the reference
  225.   count of the returned IDispatch. The caller of VarToInterface must
  226.   manually call AddRef and Release on the returned interface. }
  227.  
  228. function VarToInterface(const V: Variant): IDispatch;
  229.  
  230. { VarToAutoObject returns the TAutoObject instance corresponding to the
  231.   IDispatch interface reference stored in the given variant. An exception
  232.   is raised if the variant does not contain an IDispatch interface, or if
  233.   the IDispatch interface is not that of a TAutoObject instance. }
  234.  
  235. function VarToAutoObject(const V: Variant): TAutoObject;
  236.  
  237. procedure DispInvoke(Dispatch: IDispatch; CallDesc: PCallDesc;
  238.   DispIDs: PDispIDList; Params: Pointer; Result: PVariant);
  239. procedure DispInvokeError(Status: Integer; const ExcepInfo: TExcepInfo);
  240.  
  241. procedure OleError(ErrorCode: HResult);
  242. procedure OleCheck(Result: HResult);
  243.  
  244. function StringToClassID(const S: string): TCLSID;
  245. function ClassIDToString(const ClassID: TCLSID): string;
  246.  
  247. function ProgIDToClassID(const ProgID: string): TCLSID;
  248. function ClassIDToProgID(const ClassID: TCLSID): string;
  249.  
  250. implementation
  251.  
  252. uses OleConst;
  253.  
  254. const
  255.  
  256. { Parameter type masks }
  257.  
  258.   atTypeMask = $7F;
  259.   atByRef    = $80;
  260.  
  261. { Automation entry flags }
  262.  
  263.   afMethod  = $00000001;
  264.   afPropGet = $00000002;
  265.   afPropSet = $00000004;
  266.   afVirtual = $00000008;
  267.  
  268. type
  269.  
  270. { Automation entry parameter list }
  271.  
  272.   PParamList = ^TParamList;
  273.   TParamList = record
  274.     ResType: Byte;
  275.     ParamCount: Byte;
  276.     ParamTypes: array[0..255] of Byte;
  277.   end;
  278.  
  279. { Automation table entry }
  280.  
  281.   PAutoEntry = ^TAutoEntry;
  282.   TAutoEntry = record
  283.     DispID: Integer;
  284.     Name: PShortString;
  285.     Flags: Integer;
  286.     Params: PParamList;
  287.     Address: Pointer;
  288.   end;
  289.  
  290. { Automation table layout }
  291.  
  292.   PAutoTable = ^TAutoTable;
  293.   TAutoTable = record
  294.     EntryCount: Integer;
  295.     Entries: array[0..4095] of TAutoEntry;
  296.   end;
  297.  
  298. { Class factory }
  299.  
  300.   TClassFactory = class(IClassFactory)
  301.   private
  302.     FRefCount: Integer;
  303.     FAutoClass: TAutoClass;
  304.   public
  305.     constructor Create(AutoClass: TAutoClass);
  306.     destructor Destroy; override;
  307.     function QueryInterface(const iid: TIID; var obj): HResult; override;
  308.     function AddRef: Longint; override;
  309.     function Release: Longint; override;
  310.     function CreateInstance(unkOuter: IUnknown; const iid: TIID;
  311.       var obj): HResult; override;
  312.     function LockServer(fLock: BOOL): HResult; override;
  313.   end;
  314.  
  315. { IAutoDispatch interface ID }
  316.  
  317. const
  318.   IID_IAutoDispatch: TGUID = ( {F5B2B8E0-1627-11CF-BD2F-0020AF0E5B81}
  319.     D1:$F5B2B8E0;D2:$1627;D3:$11CF;D4:($BD,$2F,$00,$20,$AF,$0E,$5B,$81));
  320.  
  321. { Raise EOleSysError exception from an error code }
  322.  
  323. procedure OleError(ErrorCode: HResult);
  324. begin
  325.   raise EOleSysError.Create(ErrorCode);
  326. end;
  327.  
  328. { Raise EOleSysError exception if result code indicates an error }
  329.  
  330. procedure OleCheck(Result: HResult);
  331. begin
  332.   if Result < 0 then OleError(Result);
  333. end;
  334.  
  335. { Convert a string to a class ID }
  336.  
  337. function StringToClassID(const S: string): TCLSID;
  338. var
  339.   Buffer: array[0..127] of WideChar;
  340. begin
  341.   OleCheck(CLSIDFromString(StringToWideChar(S, Buffer,
  342.     SizeOf(Buffer) div 2), Result));
  343. end;
  344.  
  345. { Convert a class ID to a string }
  346.  
  347. function ClassIDToString(const ClassID: TCLSID): string;
  348. var
  349.   P: PWideChar;
  350. begin
  351.   OleCheck(StringFromCLSID(ClassID, P));
  352.   Result := WideCharToString(P);
  353.   CoTaskMemFree(P);
  354. end;
  355.  
  356. { Convert a programmatic ID to a class ID }
  357.  
  358. function ProgIDToClassID(const ProgID: string): TCLSID;
  359. var
  360.   Buffer: array[0..127] of WideChar;
  361. begin
  362.   OleCheck(CLSIDFromProgID(StringToWideChar(ProgID, Buffer,
  363.     SizeOf(Buffer) div 2), Result));
  364. end;
  365.  
  366. { Convert a class ID to a programmatic ID }
  367.  
  368. function ClassIDToProgID(const ClassID: TCLSID): string;
  369. var
  370.   P: PWideChar;
  371. begin
  372.   OleCheck(ProgIDFromCLSID(ClassID, P));
  373.   Result := WideCharToString(P);
  374.   CoTaskMemFree(P);
  375. end;
  376.  
  377. { Create registry key }
  378.  
  379. procedure CreateRegKey(const Key, Value: string);
  380. begin
  381.   RegSetValue(HKEY_CLASSES_ROOT, PChar(Key), REG_SZ, PChar(Value),
  382.     Length(Value));
  383. end;
  384.  
  385. { Delete registry key }
  386.  
  387. procedure DeleteRegKey(const Key: string);
  388. begin
  389.   RegDeleteKey(HKEY_CLASSES_ROOT, PChar(Key));
  390. end;
  391.  
  392. { Get server key name }
  393.  
  394. function GetServerKey: string;
  395. begin
  396.   if Automation.IsInprocServer then
  397.     Result := 'InprocServer32' else
  398.     Result := 'LocalServer32';
  399. end;
  400.  
  401. { Find command-line switch }
  402.  
  403. function FindCmdLineSwitch(const Switch: string): Boolean;
  404. var
  405.   I: Integer;
  406.   S: string;
  407. begin
  408.   for I := 1 to ParamCount do
  409.   begin
  410.     S := ParamStr(I);
  411.     if (S[1] in ['-', '/']) and
  412.       (CompareText(Copy(S, 2, Maxint), Switch) = 0) then
  413.     begin
  414.       Result := True;
  415.       Exit;
  416.     end;
  417.   end;
  418.   Result := False;
  419. end;
  420.  
  421. { Convert wide character string to ShortString }
  422.  
  423. procedure WideCharToShortString(P: PWideChar; var S: ShortString);
  424. var
  425.   I: Integer;
  426.   W: WideChar;
  427. begin
  428.   I := 0;
  429.   repeat
  430.     W := P[I];
  431.     if W = #0 then Break;
  432.     if W >= #256 then W := #0;
  433.     Inc(I);
  434.     S[I] := Char(W);
  435.   until I = 255;
  436.   S[0] := Char(I);
  437. end;
  438.  
  439. { Compare two symbols }
  440.  
  441. function SameSymbol(const Ident1, Ident2: ShortString): Boolean;
  442. asm
  443.         PUSH    EBX
  444.         XOR     EBX,EBX
  445.         XOR     ECX,ECX
  446.         MOV     CL,[EAX]
  447.         CMP     CL,[EDX]
  448.         JNE     @@2
  449. @@1:    MOV     BH,[EAX+ECX]
  450.         XOR     BH,[EDX+ECX]
  451.         TEST    BH,0DFH
  452.         JNE     @@2
  453.         DEC     ECX
  454.         JNE     @@1
  455.         INC     EBX
  456. @@2:    XOR     EAX,EAX
  457.         MOV     AL,BL
  458.         POP     EBX
  459. end;
  460.  
  461. { Return automation table of the given class }
  462.  
  463. function GetAutoTable(ClassRef: TClass): PAutoTable;
  464. asm
  465.     MOV    EAX,[EAX-52]
  466. end;
  467.  
  468. { Return dispatch ID of the given name in the given class }
  469.  
  470. function GetDispIDOfName(ClassRef: TClass; const Name: ShortString): Integer;
  471. var
  472.   AutoTable: PAutoTable;
  473.   NameStart: Word;
  474.   I: Integer;
  475.   P: PAutoEntry;
  476. begin
  477.   NameStart := Word((@Name)^);
  478.   repeat
  479.     AutoTable := GetAutoTable(ClassRef);
  480.     if AutoTable <> nil then
  481.     begin
  482.       I := AutoTable^.EntryCount;
  483.       P := @AutoTable^.Entries;
  484.       repeat
  485.         if ((NameStart xor Word(Pointer(P^.Name)^)) and $DFFF = 0) and
  486.           SameSymbol(Name, P^.Name^) then
  487.         begin
  488.           Result := P^.DispID;
  489.           Exit;
  490.         end;
  491.         Inc(Integer(P), SizeOf(TAutoEntry));
  492.         Dec(I);
  493.       until I = 0;
  494.     end;
  495.     ClassRef := ClassRef.ClassParent;
  496.   until ClassRef = nil;
  497.   Result := -1;
  498. end;
  499.  
  500. { Return automation table entry for the given dispatch ID and dispatch
  501.   flags in the given class }
  502.  
  503. function GetAutoEntry(ClassRef: TClass; DispID, Flags: Integer): PAutoEntry;
  504. var
  505.   AutoTable: PAutoTable;
  506.   I: Integer;
  507. begin
  508.   repeat
  509.     AutoTable := GetAutoTable(ClassRef);
  510.     if AutoTable <> nil then
  511.     begin
  512.       I := AutoTable^.EntryCount;
  513.       Result := @AutoTable^.Entries;
  514.       repeat
  515.         if (Result^.DispID = DispID) and
  516.           (Result^.Flags and Flags <> 0) then Exit;
  517.         Inc(Integer(Result), SizeOf(TAutoEntry));
  518.         Dec(I);
  519.       until I = 0;
  520.     end;
  521.     ClassRef := ClassRef.ClassParent;
  522.   until ClassRef = nil;
  523.   Result := nil;
  524. end;
  525.  
  526. { Create an OLE object variant given an IDispatch }
  527.  
  528. function VarFromInterface(Unknown: IUnknown): Variant;
  529. var
  530.   Dispatch: IDispatch;
  531. begin
  532.   VarClear(Result);
  533.   if Unknown <> nil then
  534.   begin
  535.     OleCheck(Unknown.QueryInterface(IID_IDispatch, Dispatch));
  536.     TVarData(Result).VType := varDispatch;
  537.     TVarData(Result).VDispatch := Dispatch;
  538.   end;
  539. end;
  540.  
  541. { Return OLE object stored in a variant }
  542.  
  543. function VarToInterface(const V: Variant): IDispatch;
  544. begin
  545.   Result := nil;
  546.   if TVarData(V).VType = varDispatch then
  547.     Result := TVarData(V).VDispatch
  548.   else if TVarData(V).VType = (varDispatch or varByRef) then
  549.     Result := Pointer(TVarData(V).VPointer^);
  550.   if Result = nil then raise EOleError.CreateRes(SVarNotObject);
  551. end;
  552.  
  553. { Return TAutoObject referenced by the given variant }
  554.  
  555. function VarToAutoObject(const V: Variant): TAutoObject;
  556. var
  557.   Dispatch: IDispatch;
  558.   AutoDispatch: TAutoDispatch;
  559. begin
  560.   Dispatch := VarToInterface(V);
  561.   if Dispatch.QueryInterface(IID_IAutoDispatch, AutoDispatch) <> S_OK then
  562.     raise EOleError.CreateRes(SVarNotAutoObject);
  563.   Result := AutoDispatch.GetAutoObject;
  564.   AutoDispatch.Release;
  565. end;
  566.  
  567. { Create an OLE object variant given a class name }
  568.  
  569. function CreateOleObject(const ClassName: string): Variant;
  570. var
  571.   Unknown: IUnknown;
  572.   ClassID: TCLSID;
  573.   WideCharBuf: array[0..127] of WideChar;
  574. begin
  575.   StringToWideChar(ClassName, WideCharBuf, SizeOf(WideCharBuf) div 2);
  576.   OleCheck(CLSIDFromProgID(WideCharBuf, ClassID));
  577.   OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
  578.     CLSCTX_LOCAL_SERVER, IID_IUnknown, Unknown));
  579.   try
  580.     Result := VarFromInterface(Unknown);
  581.   finally;
  582.     Unknown.Release;
  583.   end;
  584. end;
  585.  
  586. { Get active OLE object for a given class name }
  587.  
  588. function GetActiveOleObject(const ClassName: string): Variant;
  589. var
  590.   Unknown: IUnknown;
  591.   ClassID: TCLSID;
  592.   WideCharBuf: array[0..127] of WideChar;
  593. begin
  594.   StringToWideChar(ClassName, WideCharBuf, SizeOf(WideCharBuf) div 2);
  595.   OleCheck(CLSIDFromProgID(WideCharBuf, ClassID));
  596.   OleCheck(GetActiveObject(ClassID, nil, Unknown));
  597.   try
  598.     Result := VarFromInterface(Unknown);
  599.   finally;
  600.     Unknown.Release;
  601.   end;
  602. end;
  603.  
  604. { Call Invoke method on the given IDispatch interface using the given
  605.   call descriptor, dispatch IDs, parameters, and result }
  606.  
  607. procedure DispInvoke(Dispatch: IDispatch; CallDesc: PCallDesc;
  608.   DispIDs: PDispIDList; Params: Pointer; Result: PVariant);
  609. type
  610.   PVarArg = ^TVarArg;
  611.   TVarArg = array[0..3] of Integer;
  612.   TStringDesc = record
  613.     BStr: PWideChar;
  614.     PStr: PString;
  615.   end;
  616. var
  617.   I, J, K, ArgType, ArgCount, StrCount, DispID, InvKind, Status: Integer;
  618.   VarFlag: Byte;
  619.   ParamPtr: ^Integer;
  620.   ArgPtr, VarPtr: PVarArg;
  621.   DispParams: TDispParams;
  622.   ExcepInfo: TExcepInfo;
  623.   Strings: array[0..MaxDispArgs - 1] of TStringDesc;
  624.   Args: array[0..MaxDispArgs - 1] of TVarArg;
  625. begin
  626.   StrCount := 0;
  627.   try
  628.     ArgCount := CallDesc^.ArgCount;
  629.     if ArgCount <> 0 then
  630.     begin
  631.       ParamPtr := Params;
  632.       ArgPtr := @Args[ArgCount];
  633.       I := 0;
  634.       repeat
  635.         Dec(Integer(ArgPtr), SizeOf(TVarData));
  636.         ArgType := CallDesc^.ArgTypes[I] and atTypeMask;
  637.         VarFlag := CallDesc^.ArgTypes[I] and atByRef;
  638.         if ArgType = varError then
  639.         begin
  640.           ArgPtr^[0] := varError;
  641.           ArgPtr^[2] := DISP_E_PARAMNOTFOUND;
  642.         end else
  643.         begin
  644.           if ArgType = varOleStr then
  645.           begin
  646.             with Strings[StrCount] do
  647.               if VarFlag <> 0 then
  648.               begin
  649.                 BStr := StringToOleStr(PString(ParamPtr^)^);
  650.                 PStr := PString(ParamPtr^);
  651.                 ArgPtr^[0] := varOleStr or varByRef;
  652.                 ArgPtr^[2] := Integer(@BStr);
  653.               end else
  654.               begin
  655.                 BStr := StringToOleStr(PString(ParamPtr)^);
  656.                 PStr := nil;
  657.                 ArgPtr^[0] := varOleStr;
  658.                 ArgPtr^[2] := Integer(BStr);
  659.               end;
  660.             Inc(StrCount);
  661.           end else
  662.           if VarFlag <> 0 then
  663.           begin
  664.             if (ArgType = varVariant) and
  665.               (PVarData(ParamPtr^)^.VType = varString) then
  666.               VarCast(PVariant(ParamPtr^)^, PVariant(ParamPtr^)^, varOleStr);
  667.             ArgPtr^[0] := ArgType or varByRef;
  668.             ArgPtr^[2] := ParamPtr^;
  669.           end else
  670.           if ArgType = varVariant then
  671.           begin
  672.             if PVarData(ParamPtr^)^.VType = varString then
  673.             begin
  674.               with Strings[StrCount] do
  675.               begin
  676.                 BStr := StringToOleStr(string(PVarData(ParamPtr^)^.VString));
  677.                 PStr := nil;
  678.                 ArgPtr^[0] := varOleStr;
  679.                 ArgPtr^[2] := Integer(BStr);
  680.               end;
  681.               Inc(StrCount);
  682.             end else
  683.             begin
  684.               VarPtr := PVarArg(ParamPtr^);
  685.               ArgPtr^[0] := VarPtr^[0];
  686.               ArgPtr^[1] := VarPtr^[1];
  687.               ArgPtr^[2] := VarPtr^[2];
  688.               ArgPtr^[3] := VarPtr^[3];
  689.             end;
  690.           end else
  691.           begin
  692.             ArgPtr^[0] := ArgType;
  693.             ArgPtr^[2] := ParamPtr^;
  694.             if (ArgType >= varDouble) and (ArgType <= varDate) then
  695.             begin
  696.               Inc(Integer(ParamPtr), 4);
  697.               ArgPtr^[3] := ParamPtr^;
  698.             end;
  699.           end;
  700.           Inc(Integer(ParamPtr), 4);
  701.         end;
  702.         Inc(I);
  703.       until I = ArgCount;
  704.     end;
  705.     DispParams.rgvarg := @Args;
  706.     DispParams.rgdispidNamedArgs := @DispIDs[1];
  707.     DispParams.cArgs := ArgCount;
  708.     DispParams.cNamedArgs := CallDesc^.NamedArgCount;
  709.     DispID := DispIDs[0];
  710.     InvKind := CallDesc^.CallType;
  711.     if InvKind = DISPATCH_PROPERTYPUT then
  712.     begin
  713.       if Args[0][0] and varTypeMask = varDispatch then
  714.         InvKind := DISPATCH_PROPERTYPUTREF;
  715.       DispIDs[0] := DISPID_PROPERTYPUT;
  716.       Dec(Integer(DispParams.rgdispidNamedArgs), SizeOf(Integer));
  717.       Inc(DispParams.cNamedArgs);
  718.     end else
  719.       if (InvKind = DISPATCH_METHOD) and (ArgCount = 0) and (Result <> nil) then
  720.         InvKind := DISPATCH_METHOD or DISPATCH_PROPERTYGET;
  721.     Status := Dispatch.Invoke(DispID, GUID_NULL, 0, InvKind, DispParams,
  722.       Result, @ExcepInfo, nil);
  723.     if Status <> 0 then DispInvokeError(Status, ExcepInfo);
  724.     J := StrCount;
  725.     while J <> 0 do
  726.     begin
  727.       Dec(J);
  728.       with Strings[J] do
  729.         if PStr <> nil then OleStrToStrVar(BStr, PStr^);
  730.     end;
  731.   finally
  732.     K := StrCount;
  733.     while K <> 0 do
  734.     begin
  735.       Dec(K);
  736.       SysFreeString(Strings[K].BStr);
  737.     end;
  738.   end;
  739. end;
  740.  
  741. { Raise exception given an OLE return code and TExcepInfo structure }
  742.  
  743. procedure DispInvokeError(Status: Integer; const ExcepInfo: TExcepInfo);
  744. var
  745.   E: EOleException;
  746. begin
  747.   if Status <> DISP_E_EXCEPTION then OleError(Status);
  748.   E := EOleException.Create(ExcepInfo);
  749.   with ExcepInfo do
  750.   begin
  751.     if bstrSource <> nil then SysFreeString(bstrSource);
  752.     if bstrDescription <> nil then SysFreeString(bstrDescription);
  753.     if bstrHelpFile <> nil then SysFreeString(bstrHelpFile);
  754.   end;
  755.   raise E;
  756. end;
  757.  
  758. { Call GetIDsOfNames method on the given IDispatch interface }
  759.  
  760. procedure GetIDsOfNames(Dispatch: IDispatch; Names: PChar;
  761.   NameCount: Integer; DispIDs: PDispIDList);
  762. var
  763.   I, N: Integer;
  764.   Ch: WideChar;
  765.   P: PWideChar;
  766.   NameRefs: array[0..MaxDispArgs - 1] of PWideChar;
  767.   WideNames: array[0..1023] of WideChar;
  768. begin
  769.   I := 0;
  770.   N := 0;
  771.   repeat
  772.     P := @WideNames[I];
  773.     if N = 0 then NameRefs[0] := P else NameRefs[NameCount - N] := P;
  774.     repeat
  775.       Ch := WideChar(Names[I]);
  776.       WideNames[I] := Ch;
  777.       Inc(I);
  778.     until Char(Ch) = #0;
  779.     Inc(N);
  780.   until N = NameCount;
  781.   if Dispatch.GetIDsOfNames(GUID_NULL, @NameRefs, NameCount,
  782.     LOCALE_SYSTEM_DEFAULT, DispIDs) <> 0 then
  783.     raise EOleError.CreateResFmt(SNoMethod, [Names]);
  784. end;
  785.  
  786. { Central call dispatcher }
  787.  
  788. procedure VarDispInvoke(Result: PVariant; const Instance: Variant;
  789.   CallDesc: PCallDesc; Params: Pointer); cdecl;
  790. var
  791.   Dispatch: IDispatch;
  792.   DispIDs: array[0..MaxDispArgs - 1] of Integer;
  793. begin
  794.   Dispatch := VarToInterface(Instance);
  795.   GetIDsOfNames(Dispatch, @CallDesc^.ArgTypes[CallDesc^.ArgCount],
  796.     CallDesc^.NamedArgCount + 1, @DispIDs);
  797.   if Result <> nil then VarClear(Result^);
  798.   DispInvoke(Dispatch, CallDesc, @DispIDs, @Params, Result);
  799. end;
  800.  
  801. function DllGetClassObject(const CLSID: TCLSID; const IID: TIID;
  802.   var Obj): HResult;
  803. var
  804.   RegistryClass: TRegistryClass;
  805.   ClassFactory: TClassFactory;
  806. begin
  807.   RegistryClass := Automation.FRegistryList;
  808.   while RegistryClass <> nil do
  809.   begin
  810.     if IsEqualCLSID(RegistryClass.FClassID, CLSID) then
  811.     begin
  812.       try
  813.         ClassFactory := TClassFactory.Create(RegistryClass.FAutoClass);
  814.       except
  815.         Result := E_UNEXPECTED;
  816.         Exit;
  817.       end;
  818.       Result := ClassFactory.QueryInterface(IID, Obj);
  819.       ClassFactory.Release;
  820.       Exit;
  821.     end;
  822.     RegistryClass := RegistryClass.FNext;
  823.   end;
  824.   Pointer(Obj) := nil;
  825.   Result := CLASS_E_CLASSNOTAVAILABLE;
  826. end;
  827.  
  828. function DllCanUnloadNow: HResult;
  829. begin
  830.   Result := S_FALSE;
  831.   if (Automation.FAutoObjectCount = 0) and
  832.     (Automation.FClassFactoryCount = 0) then Result := S_OK;
  833. end;
  834.  
  835. function DllRegisterServer: HResult;
  836. begin
  837.   Automation.UpdateRegistry(True);
  838.   Result := S_OK;
  839. end;
  840.  
  841. function DllUnregisterServer: HResult;
  842. begin
  843.   Automation.UpdateRegistry(False);
  844.   Result := S_OK;
  845. end;
  846.  
  847. { EOleSysError }
  848.  
  849. constructor EOleSysError.Create(ErrorCode: Integer);
  850. var
  851.   Message: string;
  852. begin
  853.   Message := SysErrorMessage(ErrorCode);
  854.   if Message = '' then FmtStr(Message, LoadStr(SOleError), [ErrorCode]);
  855.   inherited Create(Message);
  856.   FErrorCode := ErrorCode;
  857. end;
  858.  
  859. { EOleException }
  860.  
  861. constructor EOleException.Create(const ExcepInfo: TExcepInfo);
  862. var
  863.   Message: string;
  864.   Len: Integer;
  865. begin
  866.   with ExcepInfo do
  867.   begin
  868.     if bstrDescription <> nil then
  869.     begin
  870.       WideCharToStrVar(bstrDescription, Message);
  871.       Len := Length(Message);
  872.       while (Len > 0) and (Message[Len] in [#0..#32, '.']) do Dec(Len);
  873.       SetLength(Message, Len);
  874.     end;
  875.     inherited CreateHelp(Message, dwHelpContext);
  876.     if scode <> 0 then FErrorCode := scode else FErrorCode := wCode;
  877.     if bstrSource <> nil then WideCharToStrVar(bstrSource, FSource);
  878.     if bstrHelpFile <> nil then WideCharToStrVar(bstrHelpFile, FHelpFile);
  879.   end;
  880. end;
  881.  
  882. { TAutoDispatch }
  883.  
  884. constructor TAutoDispatch.Create(AutoObject: TAutoObject);
  885. begin
  886.   FAutoObject := AutoObject;
  887. end;
  888.  
  889. function TAutoDispatch.QueryInterface(const iid: TIID; var obj): HResult;
  890. begin
  891.   Result := FAutoObject.QueryInterface(iid, obj);
  892. end;
  893.  
  894. function TAutoDispatch.AddRef: Longint;
  895. begin
  896.   Result := FAutoObject.AddRef;
  897. end;
  898.  
  899. function TAutoDispatch.Release: Longint;
  900. begin
  901.   Result := FAutoObject.Release;
  902. end;
  903.  
  904. function TAutoDispatch.GetTypeInfoCount(var ctinfo: Integer): HResult;
  905. begin
  906.   ctinfo := 0;
  907.   Result := S_OK;
  908. end;
  909.  
  910. function TAutoDispatch.GetTypeInfo(itinfo: Integer; lcid: TLCID;
  911.   var tinfo: ITypeInfo): HResult;
  912. begin
  913.   tinfo := nil;
  914.   Result := E_NOTIMPL;
  915. end;
  916.  
  917. function TAutoDispatch.GetIDsOfNames(const iid: TIID; rgszNames: POleStrList;
  918.   cNames: Integer; lcid: TLCID; rgdispid: PDispIDList): HResult;
  919. begin
  920.   Result := FAutoObject.GetIDsOfNames(rgszNames, cNames, rgdispid);
  921. end;
  922.  
  923. function TAutoDispatch.Invoke(dispIDMember: TDispID; const iid: TIID;
  924.   lcid: TLCID; flags: Word; var dispParams: TDispParams; varResult: PVariant;
  925.   excepInfo: PExcepInfo; argErr: PInteger): HResult;
  926. begin
  927.   Result := FAutoObject.Invoke(dispIDMember, flags, dispParams,
  928.     varResult, excepInfo, argErr);
  929. end;
  930.  
  931. function TAutoDispatch.GetAutoObject: TAutoObject;
  932. begin
  933.   Result := FAutoObject;
  934. end;
  935.  
  936. { TAutoObject }
  937.  
  938. constructor TAutoObject.Create;
  939. begin
  940.   Automation.CountAutoObject(True);
  941.   FRefCount := 1;
  942.   FAutoDispatch := CreateAutoDispatch;
  943. end;
  944.  
  945. destructor TAutoObject.Destroy;
  946. begin
  947.   FAutoDispatch.Free;
  948.   Automation.CountAutoObject(False);
  949. end;
  950.  
  951. function TAutoObject.AddRef: Integer;
  952. begin
  953.   Inc(FRefCount);
  954.   Result := FRefCount;
  955. end;
  956.  
  957. function TAutoObject.CreateAutoDispatch: TAutoDispatch;
  958. begin
  959.   Result := TAutoDispatch.Create(Self);
  960. end;
  961.  
  962. procedure TAutoObject.GetExceptionInfo(ExceptObject: TObject;
  963.   var ExcepInfo: TExcepInfo);
  964. begin
  965.   with ExcepInfo do
  966.   begin
  967.     bstrSource := StringToOleStr(ClassName);
  968.     if ExceptObject is Exception then
  969.       bstrDescription := StringToOleStr(Exception(ExceptObject).Message);
  970.     scode := E_FAIL;
  971.   end;
  972. end;
  973.  
  974. function TAutoObject.GetIDsOfNames(Names: POleStrList;
  975.   Count: Integer; DispIDs: PDispIDList): HResult;
  976. var
  977.   I, DispID: Integer;
  978.   Name: ShortString;
  979. begin
  980.   WideCharToShortString(Names^[0], Name);
  981.   DispID := GetDispIDOfName(ClassType, Name);
  982.   DispIDs^[0] := DispID;
  983.   if Count > 1 then
  984.     for I := 1 to Count - 1 do DispIDs^[I] := -1;
  985.   if (DispID = -1) or (Count > 1) then
  986.     Result := DISP_E_UNKNOWNNAME else
  987.     Result := S_OK;
  988. end;
  989.  
  990. function TAutoObject.GetOleObject: Variant;
  991. begin
  992.   VarClear(Result);
  993.   TVarData(Result).VType := varDispatch;
  994.   TVarData(Result).VDispatch := FAutoDispatch;
  995.   AddRef;
  996. end;
  997.  
  998. function TAutoObject.Invoke(DispID: TDispID; Flags: Integer;
  999.   var Params: TDispParams; VarResult: PVariant; ExcepInfo: PExcepInfo;
  1000.   ArgErr: PInteger): HResult;
  1001. type
  1002.   TVarStrDesc = record
  1003.     PStr: Pointer;
  1004.     BStr: PBStr;
  1005.   end;
  1006. var
  1007.   AutoEntry: PAutoEntry;
  1008.   ArgCount, NamedArgCount, ArgIndex, StrCount, I, J, K: Integer;
  1009.   ParamPtr, ArgPtr: PVarData;
  1010.   ArgType, VarFlag: Byte;
  1011.   StringPtr: Pointer;
  1012.   OleStr: TBStr;
  1013.   ResVar: TVarData;
  1014.   Strings: array[0..MaxDispArgs - 1] of TVarStrDesc;
  1015.   Args: array[0..MaxDispArgs - 1] of TVarData;
  1016. begin
  1017.   if Flags = DISPATCH_PROPERTYPUTREF then Flags := DISPATCH_PROPERTYPUT;
  1018.   AutoEntry := GetAutoEntry(ClassType, DispID, Flags);
  1019.   if (AutoEntry = nil) or (AutoEntry^.Params^.ResType = 0) and
  1020.     (VarResult <> nil) then
  1021.   begin
  1022.     Result := DISP_E_MEMBERNOTFOUND;
  1023.     Exit;
  1024.   end;
  1025.   NamedArgCount := Params.cNamedArgs;
  1026.   if Flags = DISPATCH_PROPERTYPUT then Dec(NamedArgCount);
  1027.   if NamedArgCount <> 0 then
  1028.   begin
  1029.     Result := DISP_E_NONAMEDARGS;
  1030.     Exit;
  1031.   end;
  1032.   ArgCount := Params.cArgs;
  1033.   if ArgCount <> AutoEntry^.Params^.ParamCount then
  1034.   begin
  1035.     Result := DISP_E_BADPARAMCOUNT;
  1036.     Exit;
  1037.   end;
  1038.   Result := S_OK;
  1039.   StrCount := 0;
  1040.   for I := 0 to ArgCount - 1 do Args[I].VType := varEmpty;
  1041.   ResVar.VType := varEmpty;
  1042.   try
  1043.     try
  1044.       if ArgCount <> 0 then
  1045.       begin
  1046.         ParamPtr := @Params.rgvarg^[ArgCount];
  1047.         ArgPtr := @Args;
  1048.         ArgIndex := 0;
  1049.         repeat
  1050.           Dec(Integer(ParamPtr), SizeOf(Variant));
  1051.           ArgType := AutoEntry^.Params^.ParamTypes[ArgIndex] and atTypeMask;
  1052.           VarFlag := AutoEntry^.Params^.ParamTypes[ArgIndex] and atByRef;
  1053.           if (ParamPtr^.VType = varError) and ((ArgType <> varVariant) or
  1054.             (VarFlag <> 0)) then
  1055.           begin
  1056.             Result := DISP_E_PARAMNOTOPTIONAL;
  1057.             Break;
  1058.           end;
  1059.           if VarFlag <> 0 then
  1060.           begin
  1061.             if ParamPtr^.VType <> (ArgType or varByRef) then
  1062.             begin
  1063.               Result := DISP_E_TYPEMISMATCH;
  1064.               Break;
  1065.             end;
  1066.             if ArgType = varOleStr then
  1067.             begin
  1068.               with Strings[StrCount] do
  1069.               begin
  1070.                 PStr := nil;
  1071.                 BStr := ParamPtr^.VPointer;
  1072.                 OleStrToStrVar(BStr^, string(PStr));
  1073.                 ArgPtr^.VType := varString or varByRef;
  1074.                 ArgPtr^.VPointer := @PStr;
  1075.               end;
  1076.               Inc(StrCount);
  1077.             end else
  1078.             begin
  1079.               ArgPtr^.VType := ParamPtr^.VType;
  1080.               ArgPtr^.VPointer := ParamPtr^.VPointer;
  1081.             end;
  1082.           end else
  1083.           if ArgType = varVariant then
  1084.           begin
  1085.             ArgPtr^.VType := varVariant or varByRef;
  1086.             ArgPtr^.VPointer := ParamPtr;
  1087.           end else
  1088.           begin
  1089.             Result := VariantChangeTypeEx(PVariant(ArgPtr)^,
  1090.               PVariant(ParamPtr)^, LOCALE_USER_DEFAULT, 0, ArgType);
  1091.             if Result <> S_OK then Break;
  1092.             if ArgType = varOleStr then
  1093.             begin
  1094.               StringPtr := nil;
  1095.               OleStrToStrVar(ArgPtr^.VOleStr, string(StringPtr));
  1096.               VariantClear(PVariant(ArgPtr)^);
  1097.               ArgPtr^.VType := varString;
  1098.               ArgPtr^.VString := StringPtr;
  1099.             end;
  1100.           end;
  1101.           Inc(Integer(ArgPtr), SizeOf(Variant));
  1102.           Inc(ArgIndex);
  1103.         until ArgIndex = ArgCount;
  1104.         if Result <> S_OK then
  1105.         begin
  1106.           if ArgErr <> nil then ArgErr^ := ArgCount - ArgIndex - 1;
  1107.           Exit;
  1108.         end;
  1109.       end;
  1110.       InvokeMethod(AutoEntry, @Args, @ResVar);
  1111.       for J := 0 to StrCount - 1 do
  1112.         with Strings[J] do
  1113.         begin
  1114.           OleStr := StringToOleStr(string(PStr));
  1115.           SysFreeString(BStr^);
  1116.           BStr^ := OleStr;
  1117.         end;
  1118.       if VarResult <> nil then
  1119.         if ResVar.VType = varString then
  1120.         begin
  1121.           OleStr := StringToOleStr(string(ResVar.VString));
  1122.           VariantClear(VarResult^);
  1123.           PVarData(VarResult)^.VType := varOleStr;
  1124.           PVarData(VarResult)^.VOleStr := OleStr;
  1125.         end else
  1126.         begin
  1127.           VariantClear(VarResult^);
  1128.           Move(ResVar, VarResult^, SizeOf(Variant));
  1129.           ResVar.VType := varEmpty;
  1130.         end;
  1131.     finally
  1132.       for K := 0 to StrCount - 1 do string(Strings[K].PStr) := '';
  1133.       for K := 0 to ArgCount - 1 do VarClear(Variant(Args[K]));
  1134.       VarClear(Variant(ResVar));
  1135.     end;
  1136.   except
  1137.     if ExcepInfo <> nil then
  1138.     begin
  1139.       FillChar(ExcepInfo^, 0, SizeOf(TExcepInfo));
  1140.       GetExceptionInfo(ExceptObject, ExcepInfo^);
  1141.     end;
  1142.     Result := DISP_E_EXCEPTION;
  1143.   end;
  1144. end;
  1145.  
  1146. procedure TAutoObject.InvokeMethod(AutoEntry, Args, Result: Pointer);
  1147. var
  1148.   Instance, AutoData: Pointer;
  1149. asm
  1150.     PUSH    EBX
  1151.     PUSH    ESI
  1152.     PUSH    EDI
  1153.     MOV    Instance,EAX
  1154.     MOV    EBX,EDX
  1155.     MOV    ESI,[EBX].TAutoEntry.Params
  1156.     MOV    EDI,-2
  1157.     MOVZX    EAX,[ESI].TParamList.ParamCount
  1158.     OR    EAX,EAX
  1159.     JE    @CheckResult
  1160.     MOV    AutoData,EBX
  1161.     MOV    EBX,EAX
  1162.     MOV    ESI,ECX
  1163.  
  1164. @PushLoop:
  1165.     MOV    AX,[ESI].Word[0]
  1166.     CMP    EAX,varSingle
  1167.     JE    @Push4
  1168.     CMP    EAX,varDouble
  1169.     JE    @Push8
  1170.     CMP    EAX,varCurrency
  1171.     JE    @Push8
  1172.     INC    EDI
  1173.     JG    @Push4
  1174.     JE    @LoadECX
  1175.  
  1176. @LoadEDX:
  1177.     MOV    EDX,[ESI].Integer[8]
  1178.     JMP    @PushNext
  1179.  
  1180. @LoadECX:
  1181.     MOV    ECX,[ESI].Integer[8]
  1182.     JMP    @PushNext
  1183.  
  1184. @Push8:
  1185.     PUSH    [ESI].Integer[12]
  1186.  
  1187. @Push4:
  1188.     PUSH    [ESI].Integer[8]
  1189.  
  1190. @PushNext:
  1191.     ADD    ESI,16
  1192.     DEC    EBX
  1193.     JNE    @PushLoop
  1194.     MOV    EBX,AutoData
  1195.     MOV    ESI,[EBX].TAutoEntry.Params
  1196.  
  1197. @CheckResult:
  1198.     MOV    AL,[ESI].TParamList.ResType
  1199.     CMP    AL,varOleStr
  1200.     JE    @PassStrRes
  1201.     CMP    AL,varVariant
  1202.     JNE    @Invoke
  1203.  
  1204. @PassVarRes:
  1205.     MOV    EAX,Result
  1206.     JMP    @PassResult
  1207.  
  1208. @PassStrRes:
  1209.     MOV    EAX,Result
  1210.     MOV    [EAX].Word,varString
  1211.     ADD    EAX,8
  1212.     MOV    [EAX].Integer,0
  1213.  
  1214. @PassResult:
  1215.     INC    EDI
  1216.     JG    @ResultPush
  1217.     JE    @ResultECX
  1218.  
  1219. @ResultEDX:
  1220.     MOV    EDX,EAX
  1221.     JMP    @Invoke
  1222.  
  1223. @ResultECX:
  1224.     MOV    ECX,EAX
  1225.     JMP    @Invoke
  1226.  
  1227. @ResultPush:
  1228.     PUSH    EAX
  1229.  
  1230. @Invoke:
  1231.     MOV    EAX,Instance
  1232.     LEA    EDI,[EBX].TAutoEntry.Address
  1233.     TEST    [EBX].TAutoEntry.Flags,afVirtual
  1234.     JE    @CallMethod
  1235.     MOV    EDI,[EAX]
  1236.     ADD    EDI,[EBX].TAutoEntry.Address
  1237.  
  1238. @CallMethod:
  1239.     CALL    [EDI].Pointer
  1240.     MOV    EDX,Result
  1241.     MOVZX    ECX,[ESI].TParamList.ResType
  1242.     JMP    @ResultTable.Pointer[ECX*4]
  1243.  
  1244. @ResultTable:
  1245.     DD    @ResNone
  1246.     DD    @ResNone
  1247.     DD    @ResInteger
  1248.     DD    @ResInteger
  1249.     DD    @ResSingle
  1250.     DD    @ResDouble
  1251.     DD    @ResCurrency
  1252.     DD    @ResDouble
  1253.     DD    @ResNone
  1254.     DD    @ResNone
  1255.     DD    @ResNone
  1256.     DD    @ResInteger
  1257.     DD    @ResNone
  1258.  
  1259. @ResSingle:
  1260.     FSTP    [EDX].Single[8]
  1261.     FWAIT
  1262.     JMP    @ResSetType
  1263.  
  1264. @ResDouble:
  1265.     FSTP    [EDX].Double[8]
  1266.     FWAIT
  1267.     JMP    @ResSetType
  1268.  
  1269. @ResCurrency:
  1270.     FISTP    [EDX].Currency[8]
  1271.     FWAIT
  1272.     JMP    @ResSetType
  1273.  
  1274. @ResInteger:
  1275.     MOV    [EDX].Integer[8],EAX
  1276.  
  1277. @ResSetType:
  1278.     MOV    [EDX].Word,CX
  1279.  
  1280. @ResNone:
  1281.     POP    EDI
  1282.     POP    ESI
  1283.     POP    EBX
  1284. end;
  1285.  
  1286. function TAutoObject.QueryInterface(const iid: TIID; var obj): HResult;
  1287. begin
  1288.   if IsEqualIID(iid, IID_IUnknown) or IsEqualIID(iid, IID_IDispatch) or
  1289.     IsEqualIID(iid, IID_IAutoDispatch) then
  1290.   begin
  1291.     Pointer(obj) := FAutoDispatch;
  1292.     AddRef;
  1293.     Result := S_OK;
  1294.   end else
  1295.   begin
  1296.     Pointer(obj) := nil;
  1297.     Result := E_NOINTERFACE;
  1298.   end;
  1299. end;
  1300.  
  1301. function TAutoObject.Release: Integer;
  1302. begin
  1303.   Dec(FRefCount);
  1304.   Result := FRefCount;
  1305.   if FRefCount = 0 then Free;
  1306. end;
  1307.  
  1308. { TClassFactory }
  1309.  
  1310. constructor TClassFactory.Create(AutoClass: TAutoClass);
  1311. begin
  1312.   Inc(Automation.FClassFactoryCount);
  1313.   FRefCount := 1;
  1314.   FAutoClass := AutoClass;
  1315. end;
  1316.  
  1317. destructor TClassFactory.Destroy;
  1318. begin
  1319.   Dec(Automation.FClassFactoryCount);
  1320. end;
  1321.  
  1322. function TClassFactory.QueryInterface(const iid: TIID; var obj): HResult;
  1323. begin
  1324.   if IsEqualIID(iid, IID_IUnknown) or IsEqualIID(iid, IID_IClassFactory) then
  1325.   begin
  1326.     Pointer(obj) := Self;
  1327.     AddRef;
  1328.     Result := S_OK;
  1329.   end else
  1330.   begin
  1331.     Pointer(obj) := nil;
  1332.     Result := E_NOINTERFACE;
  1333.   end;
  1334. end;
  1335.  
  1336. function TClassFactory.AddRef: Longint;
  1337. begin
  1338.   Inc(FRefCount);
  1339.   Result := FRefCount;
  1340. end;
  1341.  
  1342. function TClassFactory.Release: Longint;
  1343. begin
  1344.   Dec(FRefCount);
  1345.   Result := FRefCount;
  1346.   if FRefCount = 0 then Free;
  1347. end;
  1348.  
  1349. function TClassFactory.CreateInstance(unkOuter: IUnknown; const iid: TIID;
  1350.   var obj): HResult;
  1351. var
  1352.   AutoObject: TAutoObject;
  1353. begin
  1354.   Pointer(obj) := nil;
  1355.   if unkOuter <> nil then
  1356.   begin
  1357.     Result := CLASS_E_NOAGGREGATION;
  1358.     Exit;
  1359.   end;
  1360.   try
  1361.     AutoObject := FAutoClass.Create;
  1362.   except
  1363.     Result := E_UNEXPECTED;
  1364.     Exit;
  1365.   end;
  1366.   Result := AutoObject.QueryInterface(iid, obj);
  1367.   AutoObject.Release;
  1368. end;
  1369.  
  1370. function TClassFactory.LockServer(fLock: BOOL): HResult;
  1371. begin
  1372.   Automation.CountAutoObject(fLock);
  1373.   Result := S_OK;
  1374. end;
  1375.  
  1376. { TRegistryClass }
  1377.  
  1378. constructor TRegistryClass.Create(const AutoClassInfo: TAutoClassInfo);
  1379. const
  1380.   RegFlags: array[acSingleInstance..acMultiInstance] of Integer = (
  1381.     REGCLS_SINGLEUSE, REGCLS_MULTIPLEUSE);
  1382. var
  1383.   ClassFactory: TClassFactory;
  1384. begin
  1385.   FAutoClass := AutoClassInfo.AutoClass;
  1386.   FProgID := AutoClassInfo.ProgID;
  1387.   FClassID := StringToClassID(AutoClassInfo.ClassID);
  1388.   FDescription := AutoClassInfo.Description;
  1389.   FInstancing := AutoClassInfo.Instancing;
  1390.   if not Automation.IsInprocServer and (FInstancing <> acInternal) then
  1391.   begin
  1392.     ClassFactory := TClassFactory.Create(FAutoClass);
  1393.     CoRegisterClassObject(FClassID, ClassFactory, CLSCTX_LOCAL_SERVER,
  1394.       RegFlags[FInstancing], FRegister);
  1395.     ClassFactory.Release;
  1396.   end;
  1397. end;
  1398.  
  1399. destructor TRegistryClass.Destroy;
  1400. begin
  1401.   if FRegister <> 0 then CoRevokeClassObject(FRegister);
  1402. end;
  1403.  
  1404. procedure TRegistryClass.UpdateRegistry(Register: Boolean);
  1405. var
  1406.   ClassID, FileName: string;
  1407.   Buffer: array[0..261] of Char;
  1408. begin
  1409.   if FInstancing <> acInternal then
  1410.   begin
  1411.     ClassID := ClassIDToString(FClassID);
  1412.     SetString(FileName, Buffer, GetModuleFileName(HInstance, Buffer,
  1413.       SizeOf(Buffer)));
  1414.     if Register then
  1415.     begin
  1416.       CreateRegKey(FProgID, FDescription);
  1417.       CreateRegKey(FProgID + '\Clsid', ClassID);
  1418.       CreateRegKey('CLSID\' + ClassID, FDescription);
  1419.       CreateRegKey('CLSID\' + ClassID + '\ProgID', FProgID);
  1420.       CreateRegKey('CLSID\' + ClassID + '\' + GetServerKey, FileName);
  1421.     end else
  1422.     begin
  1423.       DeleteRegKey('CLSID\' + ClassID + '\' + GetServerKey);
  1424.       DeleteRegKey('CLSID\' + ClassID + '\ProgID');
  1425.       DeleteRegKey('CLSID\' + ClassID);
  1426.       DeleteRegKey(FProgID + '\Clsid');
  1427.       DeleteRegKey(FProgID);
  1428.     end;
  1429.   end;
  1430. end;
  1431.  
  1432. { TAutomation }
  1433.  
  1434. var
  1435.   SaveInitProc: Pointer;
  1436.  
  1437. procedure InitAutomation;
  1438. begin
  1439.   if SaveInitProc <> nil then TProcedure(SaveInitProc);
  1440.   Automation.Initialize;
  1441. end;
  1442.  
  1443. constructor TAutomation.Create;
  1444. begin
  1445.   FIsInprocServer := IsLibrary;
  1446.   if FindCmdLineSwitch('AUTOMATION') or FindCmdLineSwitch('EMBEDDING') then
  1447.     FStartMode := smAutomation
  1448.   else if FindCmdLineSwitch('REGSERVER') then
  1449.     FStartMode := smRegServer
  1450.   else if FindCmdLineSwitch('UNREGSERVER') then
  1451.     FStartMode := smUnregServer;
  1452. end;
  1453.  
  1454. destructor TAutomation.Destroy;
  1455. var
  1456.   RegistryClass: TRegistryClass;
  1457. begin
  1458.   while FRegistryList <> nil do
  1459.   begin
  1460.     RegistryClass := FRegistryList;
  1461.     FRegistryList := RegistryClass.FNext;
  1462.     RegistryClass.Free;
  1463.   end;
  1464. end;
  1465.  
  1466. procedure TAutomation.CountAutoObject(Created: Boolean);
  1467. begin
  1468.   if Created then Inc(FAutoObjectCount) else
  1469.   begin
  1470.     Dec(FAutoObjectCount);
  1471.     if FAutoObjectCount = 0 then LastReleased;
  1472.   end;
  1473. end;
  1474.  
  1475. procedure TAutomation.Initialize;
  1476. begin
  1477.   UpdateRegistry(FStartMode <> smUnregServer);
  1478.   if FStartMode in [smRegServer, smUnregServer] then Halt;
  1479. end;
  1480.  
  1481. procedure TAutomation.LastReleased;
  1482. var
  1483.   Shutdown: Boolean;
  1484. begin
  1485.   if not FIsInprocServer then
  1486.   begin
  1487.     Shutdown := FStartMode = smAutomation;
  1488.     if Assigned(FOnLastRelease) then FOnLastRelease(Shutdown);
  1489.     if Shutdown then PostQuitMessage(0);
  1490.   end;
  1491. end;
  1492.  
  1493. procedure TAutomation.RegisterClass(const AutoClassInfo: TAutoClassInfo);
  1494. var
  1495.   RegistryClass: TRegistryClass;
  1496. begin
  1497.   RegistryClass := TRegistryClass.Create(AutoClassInfo);
  1498.   RegistryClass.FNext := FRegistryList;
  1499.   FRegistryList := RegistryClass;
  1500. end;
  1501.  
  1502. procedure TAutomation.UpdateRegistry(Register: Boolean);
  1503. var
  1504.   RegistryClass: TRegistryClass;
  1505. begin
  1506.   RegistryClass := FRegistryList;
  1507.   while RegistryClass <> nil do
  1508.   begin
  1509.     RegistryClass.UpdateRegistry(Register);
  1510.     RegistryClass := RegistryClass.FNext;
  1511.   end;
  1512. end;
  1513.  
  1514. initialization
  1515. begin
  1516.   OleInitialize(nil);
  1517.   VarDispProc := @VarDispInvoke;
  1518.   Automation := TAutomation.Create;
  1519.   SaveInitProc := InitProc;
  1520.   InitProc := @InitAutomation;
  1521. end;
  1522.  
  1523. finalization
  1524. begin
  1525.   Automation.Free;
  1526.   OleUninitialize;
  1527. end;
  1528.  
  1529. end.
  1530.