{----------------------------------------------------------------------------- The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: JvOle2Auto.PAS, released on 2002-07-04. The Initial Developers of the Original Code are: Fedor Koshevnikov, Igor Pavluk and Serge Korolev Copyright (c) 1997, 1998 Fedor Koshevnikov, Igor Pavluk and Serge Korolev Copyright (c) 2001,2002 SGB Software All Rights Reserved. Last Modified: 2002-07-04 You may retrieve the latest version of this file at the Project JEDI's JVCL home page, located at http://jvcl.sourceforge.net Known Issues: -----------------------------------------------------------------------------} {$I JVCL.INC} unit JvOle2Auto; interface {$IFDEF WIN32} uses Windows, SysUtils, {$IFDEF COMPILER3_UP} ActiveX, ComObj; {$ELSE} Ole2, OleAuto, OleCtl; {$ENDIF} {$ELSE} uses WinTypes, WinProcs, SysUtils, Ole2, Dispatch; {$ENDIF} const { Maximum number of dispatch arguments } {$IFDEF COMPILER3_UP} MaxDispArgs = 64; {$ELSE} MaxDispArgs = 32; {$ENDIF} {$IFNDEF WIN32} type TDispID = DISPID; PDispID = ^TDispID; TDispParams = DISPPARAMS; TLCID = LCID; TExcepInfo = EXCEPINFO; PDispIDList = ^TDispIDList; TDispIDList = array [0..MaxDispArgs] of TDispID; EOleError = class(EJVCLException); {$ENDIF WIN32} {$IFNDEF COMPILER3_UP} type EPropReadOnly = class(EOleError); EPropWriteOnly = class(EOleError); {$ENDIF} {$IFNDEF WIN32} const { Primary language IDs. } LANG_NEUTRAL = $00; LANG_AFRIKAANS = $36; LANG_ALBANIAN = $1C; LANG_ARABIC = $01; LANG_BASQUE = $2D; LANG_BELARUSIAN = $23; LANG_BULGARIAN = $02; LANG_CATALAN = $03; LANG_CHINESE = $04; LANG_CROATIAN = $1A; LANG_CZECH = $05; LANG_DANISH = $06; LANG_DUTCH = $13; LANG_ENGLISH = $09; LANG_ESTONIAN = $25; LANG_FAEROESE = $38; LANG_FARSI = $29; LANG_FINNISH = $0B; LANG_FRENCH = $0C; LANG_GERMAN = $07; LANG_GREEK = $08; LANG_HEBREW = $0D; LANG_HUNGARIAN = $0E; LANG_ICELANDIC = $0F; LANG_INDONESIAN = $21; LANG_ITALIAN = $10; LANG_JAPANESE = $11; LANG_KOREAN = $12; LANG_LATVIAN = $26; LANG_LITHUANIAN = $27; LANG_NORWEGIAN = $14; LANG_POLISH = $15; LANG_PORTUGUESE = $16; LANG_ROMANIAN = $18; LANG_RUSSIAN = $19; LANG_SERBIAN = $1A; LANG_SLOVAK = $1B; LANG_SLOVENIAN = $24; LANG_SPANISH = $0A; LANG_SWEDISH = $1D; LANG_THAI = $1E; LANG_TURKISH = $1F; LANG_UKRAINIAN = $22; LANG_VIETNAMESE = $2A; { Sublanguage IDs. } SUBLANG_NEUTRAL = $00; { language neutral } SUBLANG_DEFAULT = $01; { user default } SUBLANG_SYS_DEFAULT = $02; { system default } SUBLANG_CHINESE_TRADITIONAL = $01; { Chinese (Taiwan) } SUBLANG_CHINESE_SIMPLIFIED = $02; { Chinese (PR China) } SUBLANG_CHINESE_HONGKONG = $03; { Chinese (Hong Kong) } SUBLANG_CHINESE_SINGAPORE = $04; { Chinese (Singapore) } SUBLANG_DUTCH = $01; { Dutch } SUBLANG_DUTCH_BELGIAN = $02; { Dutch (Belgian) } SUBLANG_ENGLISH_US = $01; { English (USA) } SUBLANG_ENGLISH_UK = $02; { English (UK) } SUBLANG_ENGLISH_AUS = $03; { English (Australian) } SUBLANG_ENGLISH_CAN = $04; { English (Canadian) } SUBLANG_ENGLISH_NZ = $05; { English (New Zealand) } SUBLANG_ENGLISH_EIRE = $06; { English (Irish) } SUBLANG_FRENCH = $01; { French } SUBLANG_FRENCH_BELGIAN = $02; { French (Belgian) } SUBLANG_FRENCH_CANADIAN = $03; { French (Canadian) } SUBLANG_FRENCH_SWISS = $04; { French (Swiss) } SUBLANG_GERMAN = $01; { German } SUBLANG_GERMAN_SWISS = $02; { German (Swiss) } SUBLANG_GERMAN_AUSTRIAN = $03; { German (Austrian) } SUBLANG_ITALIAN = $01; { Italian } SUBLANG_ITALIAN_SWISS = $02; { Italian (Swiss) } SUBLANG_NORWEGIAN_BOKMAL = $01; { Norwegian (Bokmal) } SUBLANG_NORWEGIAN_NYNORSK = $02; { Norwegian (Nynorsk) } SUBLANG_PORTUGUESE = $02; { Portuguese } SUBLANG_PORTUGUESE_BRAZILIAN = $01; { Portuguese (Brazilian) } SUBLANG_SPANISH = $01; { Spanish (Castilian) } SUBLANG_SPANISH_MEXICAN = $02; { Spanish (Mexican) } SUBLANG_SPANISH_MODERN = $03; { Spanish (Modern) } { Default System and User IDs for language and locale. } LANG_SYSTEM_DEFAULT = (SUBLANG_SYS_DEFAULT shl 10) or LANG_NEUTRAL; LANG_USER_DEFAULT = (SUBLANG_DEFAULT shl 10) or LANG_NEUTRAL; LOCALE_SYSTEM_DEFAULT = (0 shl 16) or LANG_SYSTEM_DEFAULT; LOCALE_USER_DEFAULT = (0 shl 16) or LANG_USER_DEFAULT; { OLE control status codes } CTL_E_ILLEGALFUNCTIONCALL = $800A0000 + 5; CTL_E_OVERFLOW = $800A0000 + 6; CTL_E_OUTOFMEMORY = $800A0000 + 7; CTL_E_DIVISIONBYZERO = $800A0000 + 11; CTL_E_OUTOFSTRINGSPACE = $800A0000 + 14; CTL_E_OUTOFSTACKSPACE = $800A0000 + 28; CTL_E_BADFILENAMEORNUMBER = $800A0000 + 52; CTL_E_FILENOTFOUND = $800A0000 + 53; CTL_E_BADFILEMODE = $800A0000 + 54; CTL_E_FILEALREADYOPEN = $800A0000 + 55; CTL_E_DEVICEIOERROR = $800A0000 + 57; CTL_E_FILEALREADYEXISTS = $800A0000 + 58; CTL_E_BADRECORDLENGTH = $800A0000 + 59; CTL_E_DISKFULL = $800A0000 + 61; CTL_E_BADRECORDNUMBER = $800A0000 + 63; CTL_E_BADFILENAME = $800A0000 + 64; CTL_E_TOOMANYFILES = $800A0000 + 67; CTL_E_DEVICEUNAVAILABLE = $800A0000 + 68; CTL_E_PERMISSIONDENIED = $800A0000 + 70; CTL_E_DISKNOTREADY = $800A0000 + 71; CTL_E_PATHFILEACCESSERROR = $800A0000 + 75; CTL_E_PATHNOTFOUND = $800A0000 + 76; CTL_E_INVALIDPATTERNSTRING = $800A0000 + 93; CTL_E_INVALIDUSEOFNULL = $800A0000 + 94; CTL_E_INVALIDFILEFORMAT = $800A0000 + 321; CTL_E_INVALIDPROPERTYVALUE = $800A0000 + 380; CTL_E_INVALIDPROPERTYARRAYINDEX = $800A0000 + 381; CTL_E_SETNOTSUPPORTEDATRUNTIME = $800A0000 + 382; CTL_E_SETNOTSUPPORTED = $800A0000 + 383; CTL_E_NEEDPROPERTYARRAYINDEX = $800A0000 + 385; CTL_E_SETNOTPERMITTED = $800A0000 + 387; CTL_E_GETNOTSUPPORTEDATRUNTIME = $800A0000 + 393; CTL_E_GETNOTSUPPORTED = $800A0000 + 394; CTL_E_PROPERTYNOTFOUND = $800A0000 + 422; CTL_E_INVALIDCLIPBOARDFORMAT = $800A0000 + 460; CTL_E_INVALIDPICTURE = $800A0000 + 481; CTL_E_PRINTERERROR = $800A0000 + 482; CTL_E_CANTSAVEFILETOTEMP = $800A0000 + 735; CTL_E_SEARCHTEXTNOTFOUND = $800A0000 + 744; CTL_E_REPLACEMENTSTOOLONG = $800A0000 + 746; CTL_E_CUSTOM_FIRST = $800A0000 + 600; {$ENDIF WIN32} type { OLE2 Automation Controller } TJvOleController = class(TObject) private FLocale: TLCID; FObject: Variant; FRetValue: Variant; function CallMethod(ID: TDispID; const Params: array of const; NeedResult: Boolean): PVariant; function CallMethodNamedParams(const IDs: TDispIDList; const Params: array of const; Cnt: Byte; NeedResult: Boolean): PVariant; function CallMethodNoParams(ID: TDispID; NeedResult: Boolean): PVariant; function Invoke(DispIdMember: TDispID; WFlags: Word; var DispParams: TDispParams; Res: PVariant): PVariant; function NameToDispID(const AName: string): TDispID; function NameToDispIDs(const AName: string; const AParams: array of string; Dest: PDispIDList): PDispIDList; protected procedure ClearObject; virtual; public constructor Create; destructor Destroy; override; { create or assign OLE objects } procedure CreateObject(const ClassName: string); virtual; procedure AssignIDispatch(V: Variant); virtual; procedure GetActiveObject(const ClassName: string); virtual; { get/set properties of OLE object by ID } function GetPropertyByID(ID: TDispID): PVariant; procedure SetPropertyByID(ID: TDispID; const Prop: array of const); { get/set properties of OLE object } function GetProperty(const AName: string): PVariant; procedure SetProperty(const AName: string; const Prop: array of const); { call OLE functions by IDs } function CallFunctionByID(ID: TDispID; const Params: array of const): PVariant; function CallFunctionByIDsNamedParams(const IDs: TDispIDList; const Params: array of const; Cnt: Byte): PVariant; function CallFunctionNoParamsByID(ID: TDispID): PVariant; { call OLE procedures by ID } procedure CallProcedureByID(ID: TDispID; const Params: array of const); procedure CallProcedureByIDsNamedParams(const IDs: TDispIDList; const Params: array of const; Cnt: Byte); procedure CallProcedureNoParamsByID(ID: TDispID); { call OLE functions } function CallFunction(const AName: string; const Params: array of const): PVariant; function CallFunctionNamedParams(const AName: string; const Params: array of const; const ParamNames: array of string): PVariant; function CallFunctionNoParams(const AName: string): PVariant; { call OLE procedures } procedure CallProcedure(const AName: string; const Params: array of const); procedure CallProcedureNamedParams(const AName: string; const Params: array of const; const ParamNames: array of string); procedure CallProcedureNoParams(const AName: string); { locale } procedure SetLocale(PrimaryLangID, SubLangID: Word); property Locale: TLCID read FLocale write FLocale; property OleObject: Variant read FObject; end; procedure InitOLE; procedure DoneOLE; function OleInitialized: Boolean; function MakeLangID(PrimaryLangID, SubLangID: Word): Word; function MakeLCID(LangID: Word): TLCID; function CreateLCID(PrimaryLangID, SubLangID: Word): TLCID; function ExtractLangID(LCID: TLCID): Word; function ExtractSubLangID(LCID: TLCID): Word; {$IFNDEF WIN32} procedure OleCheck(OleResult: HResult); { OLE string support } function OleStrToString(Source: BSTR): string; function StringToOleStr(const Source: string): BSTR; function StringToClassID(const S: string): CLSID; function ClassIDToString(const CLSID: CLSID): string; { Create or get active OLE object for a given a class name } function CreateOleObject(const ClassName: string): Variant; function GetActiveOleObject(const ClassName: string): Variant; {$ENDIF WIN32} implementation uses Forms; {$IFDEF COMPILER3_UP} resourcestring {$ELSE} const {$ENDIF} SOleInvalidVer = 'Invalid OLE library version'; SOleInitFailed = 'OLE Library initialization failed. Error code: %.8xH'; SOleNotInit = 'OLE2 Library not initialized'; SOleInvalidParam = 'Invalid parameter value'; SOleNotSupport = 'Method or property %s not supported by OLE object'; SOleNotReference = 'Variant does not reference an OLE automation object'; {$IFNDEF COMPILER3_UP} SOleError = 'OLE2 error occured. Error code: %.8xH'; {$ENDIF} // (rom) changed to var var FOleInitialized: Boolean = False; const { OLE2 Version } RMJ = 0; RMM = 23; RUP = 639; const DISPATCH_METHODNOPARAM = DISPATCH_METHOD or DISPATCH_PROPERTYGET; DISPATCH_METHODPARAMS = DISPATCH_METHOD {$IFDEF WIN32} or DISPATCH_PROPERTYGET {$ENDIF}; {$IFDEF WIN32} function FailedHR(hr: HResult): Boolean; begin Result := Failed(hr); end; {$ELSE WIN32} { Standard OLE class pathes } type IDispatch = class(IUnknown) function GetTypeInfoCount(var PctInfo: Integer): HResult; virtual; cdecl; export; abstract; function GetTypeInfo(ItInfo: Integer; TLCID: TLCID; var PTInfo: ITypeInfo): HResult; virtual; cdecl; export; abstract; function GetIDsOfNames(const Riid: IID; var rgszNames: PChar; CNames: Integer; TLCID: TLCID; RgDispId: PDispID): HResult; virtual; cdecl; export; abstract; function Invoke(DispIdMember: TDispID; const Riid: IID; TLCID: TLCID; WFlags: Word; var DispParams: TDispParams; PVarResult: PVariant; var ExcepInfo: TExcepInfo; var UArgErr: Integer): HResult; virtual; cdecl; export; abstract; end; function DispInvoke(_this: Pointer; PTInfo: ITypeInfo; DispIdMember: TDispID; WFlags: Word; var pparams: TDispParams; PVarResult: PVariant; var ExcepInfo: TExcepInfo; var UArgErr: Integer): HResult; far; external 'ole2disp'; function DispGetIDsOfNames(PTInfo: ITypeInfo; var rgszNames: PChar; CNames: Integer; RgDispId: PDispID): HResult; far; external 'ole2disp'; function GUID_NULL: GUID; begin Result := IID_NULL; end; {$ENDIF WIN32} { Standard OLE Library initialization code } procedure InitOLE; var dwVer: Longint; HRes: HResult; begin if FOleInitialized then Exit; dwVer := Longint(CoBuildVersion); if (RMM <> HiWord(dwVer)) or (RUP > LoWord(dwVer)) then raise EOleError.Create(SOleInvalidVer) else begin HRes := OleInitialize(nil); if FailedHR(HRes) then raise EOleError.CreateFmt(SOleInitFailed, [Longint(HRes)]) else FOleInitialized := True; end; end; { Standard OLE Library exit code } procedure DoneOLE; begin if FOleInitialized then OleUninitialize; FOleInitialized := False; end; function OleInitialized: Boolean; begin Result := FOleInitialized; end; procedure CheckOleInitialized; begin if not FOleInitialized then raise EOleError.Create(SOleNotInit); end; {$IFNDEF COMPILER3_UP} function OleErrorMsg(ErrorCode: HResult): string; begin FmtStr(Result, SOleError, [Longint(ErrorCode)]); end; {$ENDIF} {$IFNDEF WIN32} procedure OleError(ErrorCode: HResult); begin raise EOleError.Create(OleErrorMsg(ErrorCode)); end; { Raise EOleError exception if result code indicates an error } procedure OleCheck(OleResult: HResult); begin if FailedHR(OleResult) then OleError(OleResult); end; {$ENDIF WIN32} { Raise exception given an OLE return code and TExcepInfo structure } procedure DispInvokeError(Status: HResult; const ExcepInfo: TExcepInfo); {$IFDEF COMPILER3_UP} begin DispatchInvokeError(Status, ExcepInfo); end; {$ELSE} var EClass: ExceptClass; Msg: string; begin EClass := EOleError; if Longint(Status) <> DISP_E_EXCEPTION then Msg := OleErrorMsg(Status) else with ExcepInfo do begin try if (scode = CTL_E_SETNOTSUPPORTED) or (scode = CTL_E_SETNOTSUPPORTEDATRUNTIME) then EClass := EPropReadOnly else if (scode = CTL_E_GETNOTSUPPORTED) or (scode = CTL_E_GETNOTSUPPORTEDATRUNTIME) then EClass := EPropWriteOnly; if bstrDescription <> nil then begin Msg := OleStrToString(bstrDescription); while (Length(Msg) > 0) and (Msg[Length(Msg)] in [#0..#32, '.']) do Delete(Msg, Length(Msg), 1); end; finally if bstrSource <> nil then SysFreeString(bstrSource); if bstrDescription <> nil then SysFreeString(bstrDescription); if bstrHelpFile <> nil then SysFreeString(bstrHelpFile); end; end; if Msg = '' then Msg := OleErrorMsg(Status); raise EClass.Create(Msg); end; {$ENDIF COMPILER3_UP} {$IFNDEF WIN32} { Convert a string to a class ID } function StringToClassID(const S: string): CLSID; var CharBuf: array [0..64] of Char; begin OleCheck(CLSIDFromString(StrPLCopy(CharBuf, S, SizeOf(CharBuf) - 1), Result)); end; { Convert a class ID to a string } function ClassIDToString(const CLSID: CLSID): string; var P: PChar; Malloc: IMalloc; begin OleCheck(CoGetMalloc(MEMCTX_TASK, Malloc)); OleCheck(StringFromCLSID(CLSID, P)); Result := StrPas(P); Malloc.Free(P); end; { Create an OLE object variant given an IDispatch } function VarFromInterface(Unknown: IUnknown): Variant; var Disp: IDispatch; begin VariantClear(VARIANTARG(Result)); VariantInit(VARIANTARG(Result)); try if Unknown <> nil then begin OleCheck(Unknown.QueryInterface(IID_IDispatch, Disp)); Result.VT := VT_DISPATCH; Result.pdispVal := Dispatch.IDispatch(Disp); end; except VariantClear(VARIANTARG(Result)); raise; end; end; { Return OLE object stored in a variant } function VarToInterface(const V: Variant): IDispatch; begin Result := nil; if V.VT = VT_DISPATCH then Result := IDispatch(V.pdispVal) else if V.VT = (VT_DISPATCH or VT_BYREF) then Result := IDispatch(V.ppdispVal^); if Result = nil then raise EOleError.Create(SOleNotReference); end; { Create an OLE object variant given a class name } function CreateOleObject(const ClassName: string): Variant; var Unknown: IUnknown; ClassID: CLSID; CharBuf: array [0..127] of Char; begin StrPLCopy(CharBuf, ClassName, SizeOf(CharBuf) - 1); OleCheck(CLSIDFromProgID(@CharBuf, ClassID)); OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IID_IUnknown, Unknown)); try Result := VarFromInterface(Unknown); finally Unknown.Release; end; end; { Get active OLE object for a given class name } function GetActiveOleObject(const ClassName: string): Variant; var Unknown: IUnknown; ClassID: CLSID; CharBuf: array [0..127] of Char; begin StrPLCopy(CharBuf, ClassName, SizeOf(CharBuf) - 1); OleCheck(CLSIDFromProgID(@CharBuf, ClassID)); OleCheck(GetActiveObject(ClassID, nil, Unknown)); try Result := VarFromInterface(Unknown); finally Unknown.Release; end; end; { OLE string support } function OleStrToString(Source: BSTR): string; begin Result := StrPas(Source); end; function StringToOleStr(const Source: string): BSTR; var SourceLen: Integer; CharBuf: array [0..255] of Char; begin SourceLen := Length(Source); if SourceLen > 0 then begin StrPLCopy(CharBuf, Source, SizeOf(CharBuf) - 1); Result := SysAllocStringLen(CharBuf, SourceLen); end else Result := nil; end; {$ELSE} { Return OLE object stored in a variant } {$IFDEF COMPILER3_UP} function VarToInterface(const V: Variant): IDispatch; begin Result := nil; if TVarData(V).VType = varDispatch then Result := IDispatch(TVarData(V).VDispatch) else if TVarData(V).VType = (varDispatch or varByRef) then Result := IDispatch(Pointer(TVarData(V).VPointer^)); if Result = nil then raise EOleError.Create(SOleNotReference); end; {$ENDIF} {$ENDIF} { Assign Variant } procedure AssignVariant( var Dest: {$IFDEF WIN32} TVariantArg; {$ELSE} Variant; {$ENDIF} const Value: TVarRec); begin {$IFNDEF WIN32} VariantInit(VARIANTARG(Dest)); try {$ENDIF} with Value do case VType of vtInteger: begin Dest.vt := VT_I4; Dest.lVal := VInteger; end; vtBoolean: begin Dest.vt := VT_BOOL; Dest.vbool := VBoolean; end; vtChar: begin Dest.vt := VT_BSTR; Dest.bstrVal := StringToOleStr(VChar); end; vtExtended: begin Dest.vt := VT_R8; Dest.dblVal := VExtended^; end; vtString: begin Dest.vt := VT_BSTR; Dest.bstrVal := StringToOleStr(VString^); end; vtPointer: if VPointer = nil then begin Dest.vt := VT_NULL; Dest.byRef := nil; end else begin Dest.vt := VT_BYREF; Dest.byRef := VPointer; end; vtPChar: begin Dest.vt := VT_BSTR; Dest.bstrVal := StringToOleStr(StrPas(VPChar)); end; vtObject: begin Dest.vt := VT_BYREF; Dest.byRef := VObject; end; {$IFDEF WIN32} vtClass: begin Dest.vt := VT_BYREF; Dest.byRef := VClass; end; vtWideChar: begin Dest.vt := VT_BSTR; Dest.bstrVal := @VWideChar; end; vtPWideChar: begin Dest.vt := VT_BSTR; Dest.bstrVal := VPWideChar; end; vtAnsiString: begin Dest.vt := VT_BSTR; Dest.bstrVal := StringToOleStr(string(VAnsiString)); end; vtCurrency: begin Dest.vt := VT_CY; Dest.cyVal := VCurrency^; end; vtVariant: begin Dest.vt := VT_BYREF or VT_VARIANT; Dest.pvarVal := VVariant; end; {$ENDIF WIN32} {$IFDEF COMPILER4_UP} vtInterface: begin Dest.vt := VT_UNKNOWN or VT_BYREF; Dest.byRef := VInterface; end; vtInt64: begin Dest.vt := VT_I8 or VT_BYREF; Dest.byRef := VInt64; end; {$ENDIF COMPILER4_UP} else raise EOleError.Create(SOleInvalidParam); end; {$IFNDEF WIN32} except VariantClear(VARIANTARG(Dest)); raise; end; {$ENDIF} end; { TJvOleController } constructor TJvOleController.Create; begin inherited Create; {$IFDEF WIN32} FLocale := GetThreadLocale; {$ELSE} FLocale := LOCALE_SYSTEM_DEFAULT; {$ENDIF} try InitOLE; except Application.HandleException(Self); end; end; destructor TJvOleController.Destroy; begin if FOleInitialized then ClearObject; inherited Destroy; end; procedure TJvOleController.CreateObject(const ClassName: string); begin CheckOleInitialized; ClearObject; FObject := CreateOleObject(ClassName); end; procedure TJvOleController.GetActiveObject(const ClassName: string); begin CheckOleInitialized; ClearObject; FObject := GetActiveOleObject(ClassName); end; procedure TJvOleController.AssignIDispatch(V: Variant); begin CheckOleInitialized; ClearObject; VarToInterface(V); {$IFDEF WIN32} VarCopy(FObject, V); {$ELSE} VariantCopy(VARIANTARG(FObject), V); {$ENDIF} end; procedure TJvOleController.ClearObject; begin {$IFDEF WIN32} VarClear(FRetValue); VarClear(FObject); {$ELSE} VariantClear(VARIANTARG(FRetValue)); VariantClear(VARIANTARG(FObject)); {$ENDIF} end; function TJvOleController.NameToDispID(const AName: string): TDispID; var {$IFDEF WIN32} CharBuf: array [0..255] of WideChar; P: array [0..0] of PWideChar; {$ELSE} CharBuf: array [0..255] of Char; P: PChar; {$ENDIF} begin CheckOleInitialized; {$IFDEF WIN32} StringToWideChar(AName, @CharBuf[0], 256); P[0] := @CharBuf[0]; {$ELSE} StrPLCopy(CharBuf, AName, SizeOf(CharBuf) - 1); P := @CharBuf; {$ENDIF} if FailedHR(VarToInterface(FObject).GetIDsOfNames(GUID_NULL, {$IFDEF WIN32} @P, {$ELSE} P, {$ENDIF} 1, FLocale, @Result)) then raise EOleError.CreateFmt(SOleNotSupport, [AName]); end; function TJvOleController.NameToDispIDs(const AName: string; const AParams: array of string; Dest: PDispIDList): PDispIDList; var {$IFDEF WIN32} CharBuf: array [0..MaxDispArgs] of PWideChar; Size: Integer; {$ELSE} CharBuf: array [0..MaxDispArgs] of PChar; {$ENDIF} I: Byte; begin Result := Dest; CheckOleInitialized; {$IFDEF WIN32} Size := Length(AName) + 1; GetMem(CharBuf[0], Size * SizeOf(WideChar)); StringToWideChar(AName, CharBuf[0], Size); for I := 0 to High(AParams) do begin Size := Length(AParams[I]) + 1; GetMem(CharBuf[I + 1], Size * SizeOf(WideChar)); StringToWideChar(AParams[I], CharBuf[I + 1], Size); end; {$ELSE} CharBuf[0] := StrPCopy(StrAlloc(Length(AName) + 1), AName); for I := 0 to High(AParams) do CharBuf[I + 1] := StrPCopy(StrAlloc(Length(AParams[I]) + 1), AParams[I]); {$ENDIF} try if FailedHR(VarToInterface(FObject).GetIDsOfNames(GUID_NULL, {$IFDEF WIN32} @CharBuf, {$ELSE} CharBuf[0], {$ENDIF} High(AParams) + 2, FLocale, @Result^[0])) then raise EOleError.CreateFmt(SOleNotSupport, [AName]); finally {$IFDEF WIN32} for I := 0 to High(AParams) + 1 do FreeMem(CharBuf[I]); {$ELSE} for I := 0 to High(AParams) + 1 do StrDispose(CharBuf[I]); {$ENDIF} end; end; function TJvOleController.Invoke(DispIdMember: TDispID; WFlags: Word; var DispParams: TDispParams; Res: PVariant): PVariant; var ExcepInfo: TExcepInfo; UArgErr: Integer; HRes: HResult; begin {$IFDEF WIN32} if Res <> nil then VarClear(Res^); try HRes := VarToInterface(FObject).Invoke(DispIdMember, GUID_NULL, FLocale, WFlags, DispParams, Res, @ExcepInfo, @UArgErr); except if Res <> nil then VarClear(Res^); raise; end; {$ELSE} if Res <> nil then begin VariantClear(VARIANTARG(Res^)); VariantInit(VARIANTARG(Res^)); end; try HRes := VarToInterface(FObject).Invoke(DispIdMember, GUID_NULL, FLocale, WFlags, DispParams, Res, ExcepInfo, UArgErr); except if Res <> nil then VariantClear(VARIANTARG(Res^)); raise; end; {$ENDIF} if FailedHR(HRes) then DispInvokeError(HRes, ExcepInfo); Result := Res; end; function TJvOleController.CallMethodNoParams(ID: TDispID; NeedResult: Boolean): PVariant; var Disp: TDispParams; begin FillChar(Disp, SizeOf(Disp), #0); CheckOleInitialized; if NeedResult then Result := Invoke(ID, DISPATCH_METHODNOPARAM, Disp, @FRetValue) else Result := Invoke(ID, DISPATCH_METHODNOPARAM, Disp, nil); end; function TJvOleController.CallMethod(ID: TDispID; const Params: array of const; NeedResult: Boolean): PVariant; var Disp: TDispParams; ArgCnt, I: Integer; {$IFDEF WIN32} Args: array [0..MaxDispArgs - 1] of TVariantArg; {$ELSE} Args: array [0..MaxDispArgs - 1] of Variant; {$ENDIF} begin CheckOleInitialized; ArgCnt := 0; try for I := 0 to High(Params) do begin AssignVariant(Args[I], Params[I]); Inc(ArgCnt); if ArgCnt >= MaxDispArgs then Break; end; with Disp do begin if ArgCnt = 0 then rgvarg := nil else rgvarg := PVariantArgList(@Args[0]); rgdispidNamedArgs := nil; cArgs := ArgCnt; cNamedArgs := 0; end; if NeedResult then Result := Invoke(ID, DISPATCH_METHODPARAMS, Disp, @FRetValue) else Result := Invoke(ID, DISPATCH_METHODPARAMS, Disp, nil); finally {$IFNDEF WIN32} for I := 0 to ArgCnt - 1 do VariantClear(VARIANTARG(Args[I])); {$ENDIF} end; end; function TJvOleController.CallMethodNamedParams(const IDs: TDispIDList; const Params: array of const; Cnt: Byte; NeedResult: Boolean): PVariant; var Disp: TDispParams; ArgCnt, I: Integer; {$IFDEF WIN32} Args: array [0..MaxDispArgs - 1] of TVariantArg; {$ELSE} Args: array [0..MaxDispArgs - 1] of Variant; {$ENDIF} begin CheckOleInitialized; ArgCnt := 0; try for I := 0 to High(Params) do begin AssignVariant(Args[I], Params[I]); Inc(ArgCnt); if ArgCnt >= MaxDispArgs then Break; end; with Disp do begin if ArgCnt = 0 then rgvarg := nil else rgvarg := PVariantArgList(@Args); if Cnt = 0 then rgdispidNamedArgs := nil else rgdispidNamedArgs := PDispIDList(@IDs[1]); cArgs := ArgCnt; cNamedArgs := Cnt; end; if NeedResult then Result := Invoke(IDs[0], DISPATCH_METHODPARAMS, Disp, @FRetValue) else Result := Invoke(IDs[0], DISPATCH_METHODPARAMS, Disp, nil); finally {$IFNDEF WIN32} for I := 0 to ArgCnt - 1 do VariantClear(VARIANTARG(Args[I])); {$ENDIF} end; end; procedure TJvOleController.SetPropertyByID(ID: TDispID; const Prop: array of const); var Disp: TDispParams; ArgCnt, I: Integer; {$IFDEF WIN32} Args: array [0..MaxDispArgs - 1] of TVariantArg; {$ELSE} Args: array [0..MaxDispArgs - 1] of Variant; {$ENDIF} NameArg: TDispID; begin NameArg := DISPID_PROPERTYPUT; CheckOleInitialized; ArgCnt := 0; try for I := 0 to High(Prop) do begin AssignVariant(Args[I], Prop[I]); Inc(ArgCnt); if ArgCnt >= MaxDispArgs then Break; end; with Disp do begin rgvarg := PVariantArgList(@Args[0]); rgdispidNamedArgs := PDispIDList(@NameArg); cArgs := ArgCnt; cNamedArgs := 1; end; Invoke(ID, DISPATCH_PROPERTYPUT, Disp, nil); finally {$IFNDEF WIN32} for I := 0 to ArgCnt - 1 do VariantClear(VARIANTARG(Args[I])); {$ENDIF} end; end; function TJvOleController.GetPropertyByID(ID: TDispID): PVariant; var Disp: TDispParams; begin FillChar(Disp, SizeOf(Disp), #0); CheckOleInitialized; Result := Invoke(ID, DISPATCH_PROPERTYGET, Disp, @FRetValue); end; procedure TJvOleController.CallProcedureByID(ID: TDispID; const Params: array of const); begin CallMethod(ID, Params, False); end; function TJvOleController.CallFunctionByID(ID: TDispID; const Params: array of const): PVariant; begin Result := CallMethod(ID, Params, True); end; procedure TJvOleController.CallProcedureByIDsNamedParams(const IDs: TDispIDList; const Params: array of const; Cnt: Byte); begin CallMethodNamedParams(IDs, Params, Cnt, False); end; function TJvOleController.CallFunctionByIDsNamedParams(const IDs: TDispIDList; const Params: array of const; Cnt: Byte): PVariant; begin Result := CallMethodNamedParams(IDs, Params, Cnt, True); end; procedure TJvOleController.CallProcedureNoParamsByID(ID: TDispID); begin CallMethodNoParams(ID, False); end; function TJvOleController.CallFunctionNoParamsByID(ID: TDispID): PVariant; begin Result := CallMethodNoParams(ID, True); end; procedure TJvOleController.SetProperty(const AName: string; const Prop: array of const); begin SetPropertyByID(NameToDispID(AName), Prop); end; function TJvOleController.GetProperty(const AName: string): PVariant; begin Result := GetPropertyByID(NameToDispID(AName)); end; procedure TJvOleController.CallProcedure(const AName: string; const Params: array of const); begin CallProcedureByID(NameToDispID(AName), Params); end; function TJvOleController.CallFunction(const AName: string; const Params: array of const): PVariant; begin Result := CallFunctionByID(NameToDispID(AName), Params); end; procedure TJvOleController.CallProcedureNamedParams(const AName: string; const Params: array of const; const ParamNames: array of string); var DispIDs: array [0..MaxDispArgs] of TDispID; begin CallProcedureByIDsNamedParams(NameToDispIDs(AName, ParamNames, PDispIDList(@DispIDs[0]))^, Params, High(ParamNames) + 1); end; function TJvOleController.CallFunctionNamedParams(const AName: string; const Params: array of const; const ParamNames: array of string): PVariant; var DispIDs: array [0..MaxDispArgs] of TDispID; begin Result := CallFunctionByIDsNamedParams(NameToDispIDs(AName, ParamNames, PDispIDList(@DispIDs[0]))^, Params, High(ParamNames) + 1); end; procedure TJvOleController.CallProcedureNoParams(const AName: string); begin CallProcedureNoParamsByID(NameToDispID(AName)); end; function TJvOleController.CallFunctionNoParams(const AName: string): PVariant; begin Result := CallFunctionNoParamsByID(NameToDispID(AName)); end; procedure TJvOleController.SetLocale(PrimaryLangID, SubLangID: Word); begin FLocale := CreateLCID(PrimaryLangID, SubLangID); end; { Utility routines } function MakeLangID(PrimaryLangID, SubLangID: Word): Word; begin Result := (SubLangID shl 10) or PrimaryLangID; end; function MakeLCID(LangID: Word): TLCID; begin Result := TLCID(LangID or (Longint(0) shl 16)); end; function CreateLCID(PrimaryLangID, SubLangID: Word): TLCID; begin Result := MakeLCID(MakeLangID(PrimaryLangID, SubLangID)); end; function ExtractLangID(LCID: TLCID): Word; begin Result := LCID and $FF; end; function ExtractSubLangID(LCID: TLCID): Word; begin Result := LCID and ($FF shl 10) shr 10; end; {$IFDEF WIN32} initialization finalization DoneOLE; {$ELSE} initialization AddExitProc(DoneOLE); {$ENDIF} end.