comobj.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2006 by Florian Klaempfl
  4. member of the Free Pascal development team.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$mode objfpc}
  12. {$H+}
  13. {$inline on}
  14. unit comobj;
  15. interface
  16. uses
  17. sysutils,activex;
  18. type
  19. EOleError = class(Exception);
  20. EOleSysError = class(EOleError)
  21. private
  22. FErrorCode: HRESULT;
  23. public
  24. constructor Create(const Msg: string; aErrorCode: HRESULT;aHelpContext: Integer);
  25. property ErrorCode: HRESULT read FErrorCode write FErrorCode;
  26. end;
  27. EOleException = class(EOleSysError)
  28. private
  29. FHelpFile: string;
  30. FSource: string;
  31. public
  32. constructor Create(const Msg: string; aErrorCode: HRESULT;const aSource,aHelpFile: string;aHelpContext: Integer);
  33. property HelpFile: string read FHelpFile write FHelpFile;
  34. property Source: string read FSource write FSource;
  35. end;
  36. EOleRegistrationError = class(EOleError);
  37. TComServerObject = class(TObject)
  38. protected
  39. function CountObject(Created: Boolean): Integer; virtual; abstract;
  40. function CountFactory(Created: Boolean): Integer; virtual; abstract;
  41. function GetHelpFileName: string; virtual; abstract;
  42. function GetServerFileName: string; virtual; abstract;
  43. function GetServerKey: string; virtual; abstract;
  44. function GetServerName: string; virtual; abstract;
  45. function GetStartSuspended: Boolean; virtual; abstract;
  46. function GetTypeLib: ITypeLib; virtual; abstract;
  47. procedure SetHelpFileName(const Value: string); virtual; abstract;
  48. public
  49. property HelpFileName: string read GetHelpFileName write SetHelpFileName;
  50. property ServerFileName: string read GetServerFileName;
  51. property ServerKey: string read GetServerKey;
  52. property ServerName: string read GetServerName;
  53. property TypeLib: ITypeLib read GetTypeLib;
  54. property StartSuspended: Boolean read GetStartSuspended;
  55. end;
  56. {
  57. TComObject = class(TObject, IUnknown, ISupportErrorInfo)
  58. protected
  59. { IUnknown }
  60. function IUnknown.QueryInterface = ObjQueryInterface;
  61. function IUnknown._AddRef = ObjAddRef;
  62. function IUnknown._Release = ObjRelease;
  63. { IUnknown methods for other interfaces }
  64. function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  65. function _AddRef: Integer; stdcall;
  66. function _Release: Integer; stdcall;
  67. { ISupportErrorInfo }
  68. function InterfaceSupportsErrorInfo(const iid: TIID): HResult; stdcall;
  69. public
  70. constructor Create;
  71. constructor CreateAggregated(const Controller: IUnknown);
  72. constructor CreateFromFactory(Factory: TComObjectFactory; const Controller: IUnknown);
  73. destructor Destroy; override;
  74. procedure Initialize; virtual;
  75. function ObjAddRef: Integer; virtual; stdcall;
  76. function ObjQueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
  77. function ObjRelease: Integer; virtual; stdcall;
  78. function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult; override;
  79. property Controller: IUnknown;
  80. property Factory: TComObjectFactory;
  81. property RefCount: Integer;
  82. property ServerExceptionHandler: IServerExceptionHandler;
  83. end;
  84. }
  85. function CreateClassID : ansistring;
  86. function CreateComObject(const ClassID: TGUID) : IUnknown;
  87. function CreateRemoteComObject(const MachineName : WideString;const ClassID : TGUID) : IUnknown;
  88. function CreateOleObject(const ClassName : string) : IDispatch;
  89. function GetActiveOleObject(const ClassName: string) : IDispatch;
  90. procedure OleCheck(Value : HResult);inline;
  91. procedure OleError(Code: HResult);
  92. function ProgIDToClassID(const id : string) : TGUID;
  93. procedure DispatchInvoke(const Dispatch: IDispatch; CallDesc: PCallDesc;
  94. DispIDs: PDispIDList; Params: Pointer; Result: PVariant);
  95. procedure DispatchInvokeError(Status: HRESULT; const ExceptInfo: TExcepInfo);
  96. implementation
  97. uses
  98. Windows,Types,Variants,ComConst;
  99. constructor EOleSysError.Create(const Msg: string; aErrorCode: HRESULT; aHelpContext: Integer);
  100. var
  101. m : string;
  102. begin
  103. if Msg='' then
  104. m:=SysErrorMessage(aErrorCode)
  105. else
  106. m:=Msg;
  107. inherited CreateHelp(m,HelpContext);
  108. FErrorCode:=aErrorCode;
  109. end;
  110. constructor EOleException.Create(const Msg: string; aErrorCode: HRESULT;const aSource,aHelpFile: string; aHelpContext: Integer);
  111. begin
  112. inherited Create(Msg,aErrorCode,aHelpContext);
  113. FHelpFile:=aHelpFile;
  114. FSource:=aSource;
  115. end;
  116. {$define FPC_COMOBJ_HAS_CREATE_CLASS_ID}
  117. function CreateClassID : ansistring;
  118. var
  119. ClassID : TCLSID;
  120. p : PWideChar;
  121. begin
  122. CoCreateGuid(ClassID);
  123. StringFromCLSID(ClassID,p);
  124. result:=p;
  125. CoTaskMemFree(p);
  126. end;
  127. function CreateComObject(const ClassID : TGUID) : IUnknown;
  128. begin
  129. OleCheck(CoCreateInstance(ClassID,nil,CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER,IUnknown,result));
  130. end;
  131. function CreateRemoteComObject(const MachineName : WideString;const ClassID : TGUID) : IUnknown;
  132. begin
  133. {!!!!!!!}
  134. runerror(211);
  135. end;
  136. function CreateOleObject(const ClassName : string) : IDispatch;
  137. var
  138. id : TCLSID;
  139. begin
  140. id:=ProgIDToClassID(ClassName);
  141. OleCheck(CoCreateInstance(id,nil,CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER,IDispatch,result));
  142. end;
  143. function GetActiveOleObject(const ClassName : string) : IDispatch;
  144. begin
  145. {!!!!!!!}
  146. runerror(211);
  147. end;
  148. procedure OleError(Code: HResult);
  149. begin
  150. raise EOleSysError.Create('',Code,0);
  151. end;
  152. procedure OleCheck(Value : HResult);inline;
  153. begin
  154. if not(Succeeded(Value)) then
  155. OleError(Value);
  156. end;
  157. function ProgIDToClassID(const id : string) : TGUID;
  158. begin
  159. OleCheck(CLSIDFromProgID(PWideChar(WideString(id)),result));
  160. end;
  161. procedure SafeCallErrorHandler(err : HResult;addr : pointer);
  162. var
  163. info : IErrorInfo;
  164. descr,src,helpfile : widestring;
  165. helpctx : DWORD;
  166. begin
  167. if GetErrorInfo(0,info)=S_OK then
  168. begin
  169. info.GetDescription(descr);
  170. info.GetSource(src);
  171. info.GetHelpFile(helpfile);
  172. info.GetHelpContext(helpctx);
  173. raise EOleException.Create(descr,err,src,helpfile,helpctx) at addr;
  174. end
  175. else
  176. raise EOleException.Create('',err,'','',0) at addr;
  177. end;
  178. procedure DispatchInvokeError(Status: HRESULT; const ExceptInfo: TExcepInfo);
  179. begin
  180. if Status=DISP_E_EXCEPTION then
  181. raise EOleException.Create(ExceptInfo.Description,ExceptInfo.scode,ExceptInfo.Source,
  182. ExceptInfo.HelpFile,ExceptInfo.dwHelpContext)
  183. else
  184. raise EOleSysError.Create('',Status,0);
  185. end;
  186. {$define DEBUG_COMDISPATCH}
  187. procedure DispatchInvoke(const Dispatch: IDispatch; CallDesc: PCallDesc;
  188. DispIDs: PDispIDList; Params: Pointer; Result: PVariant);
  189. var
  190. { we can't pass pascal ansistrings to COM routines so we've to convert them
  191. to/from widestring. This array contains the mapping to do so
  192. }
  193. StringMap : array[0..255] of record passtr : pansistring; comstr : pwidechar; end;
  194. invokekind,
  195. i : longint;
  196. invokeresult : HResult;
  197. exceptioninfo : TExcepInfo;
  198. dispparams : TDispParams;
  199. NextString : SizeInt;
  200. Arguments : array[0..255] of TVarData;
  201. CurrType : byte;
  202. MethodID : TDispID;
  203. begin
  204. NextString:=0;
  205. fillchar(dispparams,sizeof(dispparams),0);
  206. try
  207. {$ifdef DEBUG_COMDISPATCH}
  208. writeln('Got ',CallDesc^.ArgCount,' arguments');
  209. {$endif DEBUG_COMDISPATCH}
  210. { copy and prepare arguments }
  211. for i:=0 to CallDesc^.ArgCount-1 do
  212. begin
  213. { get plain type }
  214. CurrType:=CallDesc^.ArgTypes[i] and $3f;
  215. { by reference? }
  216. if (CallDesc^.ArgTypes[i] and $80)<>0 then
  217. begin
  218. case CurrType of
  219. varStrArg:
  220. begin
  221. {$ifdef DEBUG_COMDISPATCH}
  222. writeln('Translating var ansistring argument ',PString(Params^)^);
  223. {$endif DEBUG_COMDISPATCH}
  224. StringMap[NextString].ComStr:=StringToOleStr(PString(Params^)^);
  225. StringMap[NextString].PasStr:=PString(Params^);
  226. Arguments[i].VType:=varOleStr or varByRef;
  227. Arguments[i].VPointer:=StringMap[NextString].ComStr;
  228. inc(NextString);
  229. inc(PPointer(Params));
  230. end;
  231. varVariant:
  232. {$ifdef DEBUG_COMDISPATCH}
  233. writeln('Unimplemented ref variant dispatch');
  234. {$endif DEBUG_COMDISPATCH}
  235. else
  236. begin
  237. writeln('Got ref argument with type ',CurrType);
  238. Arguments[i].VType:=CurrType or VarByRef;
  239. Arguments[i].VPointer:=PPointer(Params)^;
  240. inc(PPointer(Params));
  241. end;
  242. end
  243. end
  244. else
  245. case CurrType of
  246. varStrArg:
  247. begin
  248. {$ifdef DEBUG_COMDISPATCH}
  249. writeln('Translating ansistring argument ',PString(Params)^);
  250. {$endif DEBUG_COMDISPATCH}
  251. StringMap[NextString].ComStr:=StringToOleStr(PString(Params)^);
  252. StringMap[NextString].PasStr:=nil;
  253. Arguments[i].VType:=varOleStr;
  254. Arguments[i].VPointer:=StringMap[NextString].ComStr;
  255. inc(NextString);
  256. inc(PPointer(Params));
  257. end;
  258. varVariant:
  259. begin
  260. {$ifdef DEBUG_COMDISPATCH}
  261. writeln('Unimplemented variant dispatch');
  262. {$endif DEBUG_COMDISPATCH}
  263. end;
  264. varCurrency,
  265. varDouble,
  266. VarDate:
  267. begin
  268. {$ifdef DEBUG_COMDISPATCH}
  269. writeln('Got 8 byte float argument');
  270. {$endif DEBUG_COMDISPATCH}
  271. Arguments[i].VType:=CurrType;
  272. move(PPointer(Params)^,Arguments[i].VDouble,sizeof(Double));
  273. inc(PDouble(Params));
  274. end;
  275. else
  276. begin
  277. {$ifdef DEBUG_COMDISPATCH}
  278. writeln('Got argument with type ',CurrType);
  279. {$endif DEBUG_COMDISPATCH}
  280. Arguments[i].VType:=CurrType;
  281. Arguments[i].VPointer:=PPointer(Params)^;
  282. inc(PPointer(Params));
  283. end;
  284. end;
  285. end;
  286. { finally prepare the call }
  287. with DispParams do
  288. begin
  289. rgvarg:=@Arguments;
  290. rgdispidNamedArgs:=@DispIDs[1];
  291. cArgs:=CallDesc^.ArgCount;
  292. cNamedArgs:=CallDesc^.NamedArgCount;
  293. end;
  294. InvokeKind:=CallDesc^.CallType;
  295. MethodID:=DispIDs^[0];
  296. {$ifdef DEBUG_COMDISPATCH}
  297. writeln('MethodID: ',MethodID);
  298. {$endif DEBUG_COMDISPATCH}
  299. { do the call and check the result }
  300. invokeresult:=Dispatch.Invoke(MethodID,GUID_NULL,0,InvokeKind,DispParams,result,@exceptioninfo,nil);;
  301. if invokeresult<>0 then
  302. DispatchInvokeError(invokeresult,exceptioninfo);
  303. { translate strings back }
  304. for i:=0 to NextString-1 do
  305. if assigned(StringMap[i].passtr) then
  306. OleStrToStrVar(StringMap[i].comstr,StringMap[i].passtr^);
  307. finally
  308. for i:=0 to NextString-1 do
  309. SysFreeString(StringMap[i].ComStr);
  310. end;
  311. end;
  312. procedure SearchIDs(const DispatchInterface : IDispatch; Names: PChar;
  313. Count: Integer; IDs: PDispIDList);
  314. var
  315. res : HRESULT;
  316. NamesArray : ^PWideChar;
  317. NamesData : PWideChar;
  318. NameCount,
  319. NameLen,
  320. NewNameLen,
  321. CurrentNameDataUsed,
  322. CurrentNameDataSize : SizeInt;
  323. i : longint;
  324. begin
  325. getmem(NamesArray,Count*sizeof(PWideChar));
  326. CurrentNameDataSize:=256;
  327. CurrentNameDataUsed:=0;
  328. getmem(NamesData,CurrentNameDataSize*2);
  329. NameCount:=0;
  330. {$ifdef DEBUG_COMDISPATCH}
  331. writeln('SearchIDs: Searching ',Count,' IDs');
  332. {$endif DEBUG_COMDISPATCH}
  333. for i:=1 to Count do
  334. begin
  335. NameLen:=strlen(Names);
  336. {$ifdef DEBUG_COMDISPATCH}
  337. writeln('SearchIDs: Original name: ',Names,' Len: ',NameLen);
  338. {$endif DEBUG_COMDISPATCH}
  339. NewNameLen:=MultiByteToWideChar(0,0,Names,NameLen,nil,0)+1;
  340. if CurrentNameDataUsed+NewNameLen*2>CurrentNameDataSize then
  341. begin
  342. inc(CurrentNameDataSize,256);
  343. reallocmem(NamesData,CurrentNameDataSize*2);
  344. end;
  345. NamesArray[i-1]:=@NamesData[CurrentNameDataUsed];
  346. MultiByteToWideChar(0,0,Names,NameLen,@NamesData[CurrentNameDataUsed],NewNameLen);
  347. NamesData[CurrentNameDataUsed+NewNameLen-1]:=#0;
  348. {$ifdef DEBUG_COMDISPATCH}
  349. { we should write a widestring here writeln('SearchIDs: Translated name: ',NamesData[CurrentNameDataUsed]); }
  350. {$endif DEBUG_COMDISPATCH}
  351. inc(CurrentNameDataUsed,NewNameLen);
  352. inc(Names,NameLen+1);
  353. inc(NameCount);
  354. end;
  355. res:=DispatchInterface.GetIDsOfNames(GUID_NULL,NamesArray,NameCount,GetThreadLocale,IDs);
  356. if res=DISP_E_UNKNOWNNAME then
  357. raise EOleError.createresfmt(@snomethod,[names])
  358. else
  359. OleCheck(res);
  360. freemem(NamesArray);
  361. freemem(NamesData);
  362. end;
  363. procedure ComObjDispatchInvoke(dest : PVariant;const source : Variant;
  364. calldesc : pcalldesc;params : pointer);cdecl;
  365. var
  366. dispatchinterface : pointer;
  367. ids : array[0..255] of longint;
  368. begin
  369. {$ifdef DEBUG_COMDISPATCH}
  370. writeln('ComObjDispatchInvoke called');
  371. writeln('ComObjDispatchInvoke: CallDesc^.ArgCount = ',CallDesc^.ArgCount);
  372. {$endif DEBUG_COMDISPATCH}
  373. if tvardata(source).vtype=VarDispatch then
  374. dispatchinterface:=tvardata(source).vdispatch
  375. else if tvardata(source).vtype=(VarDispatch or VarByRef) then
  376. dispatchinterface:=pvardata(tvardata(source).vpointer)^.vdispatch
  377. else
  378. raise eoleerror.createres(@SVarNotObject);
  379. SearchIDs(IDispatch(dispatchinterface),@CallDesc^.ArgTypes[CallDesc^.ArgCount],
  380. CallDesc^.NamedArgCount+1,@ids);
  381. if assigned(dest) then
  382. VarClear(dest^);
  383. DispatchInvoke(IDispatch(dispatchinterface),calldesc,@ids,params,dest);
  384. end;
  385. const
  386. Initialized : boolean = false;
  387. initialization
  388. if not(IsLibrary) then
  389. Initialized:=Succeeded(CoInitialize(nil));
  390. SafeCallErrorProc:=@SafeCallErrorHandler;
  391. VarDispProc:=@ComObjDispatchInvoke;
  392. finalization
  393. VarDispProc:=nil;
  394. SafeCallErrorProc:=nil;
  395. if Initialized then
  396. CoUninitialize;
  397. end.