home *** CD-ROM | disk | FTP | other *** search
/ Delphi Anthology / aDELPHI.iso / Runimage / Delphi50 / Source / Vcl / oleauto.pas < prev    next >
Pascal/Delphi Source File  |  1999-08-11  |  44KB  |  1,570 lines

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