kihor 0 Posted September 19 Hello, I am trying to implement COM object in Delphi. This is test COM object and its main module looks like this: unit MyCOM; {$WARN SYMBOL_PLATFORM OFF} interface uses Windows, ActiveX, Classes, ComObj, TestCOM_TLB, StdVcl; type TMyCOM = class(TAutoObject, IMyCOM) public function DoIt: WideString; stdcall; end; TMyComObjectFactory = class(TAutoObjectFactory) protected function GetProgID: string; override; end; const SValue: WideString = 'It works!'; implementation uses ComServ; function TMyCOM.DoIt: WideString; stdcall; begin Result := SValue; end; { TMyComObjectFactory } function TMyComObjectFactory.GetProgID: string; begin Result := 'DeNovo.MyCOM'; end; initialization TMyComObjectFactory.Create(ComServer, TMyCOM, Class_MyCOM, ciMultiInstance, tmApartment); end. I register this DLL with regsvr32 without problem. Then I try to invoke COM function via the following code in separate application: procedure TForm1.btnInvokeMyCOMClick(Sender: TObject); var MyCOM: Variant; begin MyCOM := CreateOleObject('DeNovo.MyCOM'); ShowMessage(MyCOM.DoIt); end; I contantly get the memorry access error. Meanwhile when I am trying to exchange integer data - everything works fine. Can you give me some clues how to cope with this WideString memory issue? Share this post Link to post
Guest Posted September 19 Just an idea. Shouldn't the calling convention for an automation object be safecall rather than stdcall? Share this post Link to post
DelphiUdIT 176 Posted September 19 How is your RIDL definition of COM class ... may be there is the issue ... Share this post Link to post
DelphiUdIT 176 Posted September 19 5 minutes ago, Ondrej Kelle said: Just an idea. Shouldn't the calling convention for an automation object be safecall rather than stdcall? May be you're right, the return function as string should be a safearray. 17 minutes ago, kihor said: Can you give me some clues how to cope with this WideString memory issue? Instead to use return value, use that as parameter: procedure TMyCOM.DoIt(Value: WideString); stdcall; begin Value := SValue; end; Share this post Link to post
kihor 0 Posted September 19 33 minutes ago, DelphiUdIT said: How is your RIDL definition of COM class ... may be there is the issue ... Thanks for your reply. RIDL definition is following: [ uuid(F59E7744-DBC5-4544-A3DA-FF71C41A7DEE), version(1.0) ] library TestCOM { importlib("stdole2.tlb"); interface IMyCOM; coclass MyCOM; [ uuid(6C0484FA-26EE-4945-913A-78146C8449C2), helpstring("Interface for MyCOM Object"), oleautomation ] interface IMyCOM: IDispatch { [id(0x00000065)] BSTR _stdcall DoIt(void); }; [ uuid(B64A68CB-EBAC-42C7-B753-9BCBD87EC57E), helpstring("MyCOM") ] coclass MyCOM { [default] interface IMyCOM; }; }; Share this post Link to post
kihor 0 Posted September 19 40 minutes ago, Ondrej Kelle said: Just an idea. Shouldn't the calling convention for an automation object be safecall rather than stdcall? T tried your approach. Unfortunately, it didn't help. But thanks for your reply. Share this post Link to post
DelphiUdIT 176 Posted September 19 May be with the use of property, (remeber something with my very old use of COM) I can exchange widestring. I remeber to never expose directly string as return function. Let I try to modify your COM ... Share this post Link to post
Anders Melander 1783 Posted September 19 1 hour ago, Ondrej Kelle said: Shouldn't the calling convention for an automation object be safecall rather than stdcall? safecall is a Delphi concept not a COM concept and safecall just "wraps" stdcall. On the server side safecall traps exceptions and convert them to HRESULT error codes and on the client side it convert HRESULT back to exceptions (for error code only, of course). In order for a method to be declared safecall the underlying COM method must return a HRESULT. Thus: procedure HelloWorld(Value: WORD); safecall; is the same as function HelloWorld(Value: WORD): HResult; stdcall; Here endeth the lesson. Share this post Link to post
Anders Melander 1783 Posted September 19 59 minutes ago, kihor said: RIDL definition is following: I think your COM declarations are incorrect. For a method returning a WideString I would have expected something like this: HRESULT _stdcall GetStringValue([out, retval] BSTR* Result); Note that the result is a HResult so I can use safecall; Do yourself a big favor and use that instead of stdcall. So you need to specify that the string: Is being returned from the method Is the function return value (mostly for use by VB and such). And the Delphi implementation will then look like this: function TMyCOM.GetStringValue(out _Result: WideString): HRESULT; stdcall; begin _Result := 'Hello world'; Result := S_OK; end; or this: function TMyCOM.GetStringValue: WideString; safecall; begin Result := 'Hello world'; end; Delphi is actually excellent for writing COM servers and clients. You just need to learn the basics - and if you will be doing a lot of COM (or just a lot of Windows development), learn the low level stuff too. Share this post Link to post
Guest Posted September 19 27 minutes ago, Anders Melander said: safecall is a Delphi concept Oh really. Right. Thanks for reminding me. This is a Delphi forum. Returning WideString with safecall should work, IIRC. In Delphi code. Oh, and compiled with Delphi. Thanks for the lesson! Share this post Link to post
Anders Melander 1783 Posted September 19 1 minute ago, Ondrej Kelle said: Oh really. Right. Thanks for reminding me. This is a Delphi forum. Um... You asked "Shouldn't the calling convention for an automation object be safecall" which to me reads as "the calling convention for an automation object must be safecall". Or was there another reason you mentioned safecall...? Share this post Link to post
DelphiUdIT 176 Posted September 19 (edited) If you want this is the global project (DLL_COM + APP) with WIDESTRING result use .... It's only a basic skeleton of course. Bye Demo COM.zip Edited September 19 by DelphiUdIT 1 Share this post Link to post
DelphiUdIT 176 Posted September 19 (edited) Another thing about COM objects ... look at thread mission (ciMultiIstance, tmApartment) and how they work. Also be very carefull to use global variable for working COM. I suggest you don't use anyglobal variable (except const). Edited September 19 by DelphiUdIT Share this post Link to post
Anders Melander 1783 Posted September 19 9 minutes ago, DelphiUdIT said: this is the global project (DLL_COM + APP) with WIDESTRING result use Not that it matter much but it isn't necessary to use a dispatch property to return the value. Apart from that I agree with your implementation. 1 Share this post Link to post
kihor 0 Posted September 19 41 minutes ago, DelphiUdIT said: If you want this is the global project (DLL_COM + APP) with WIDESTRING result use .... It's only a basic skeleton of course. Bye Demo COM.zip That is wonderful! It really works! Thanks a lot. Share this post Link to post
DelphiUdIT 176 Posted September 19 (edited) 6 hours ago, kihor said: That is wonderful! It really works! Thanks a lot. If you want, you can try this. Is the same COM but with new application: you don't need to register the COM Try to unregsiter the old one. After that build the projetc group (it is setting for WIn64 but you can change for WIn32) and run ... all is working without register the COM thanks to SxS technology (SideBySide). The EXE and DLL will be put in Bin32 and Bin64 (and there are already the manifests). There is a ReadMe and some Manifests (they are all equal excpet for the manifest that is included in the Project1 options). Bye Demo COM SxS.zip Edited September 19 by DelphiUdIT 1 Share this post Link to post
Vincent Parrett 750 Posted September 19 (edited) If you are using the type library editor to create your RIDL - delphi should be mapping those methods as safecall. Check your options Edit : the default is only dual interfaces - this is something I change when ever I install a new version of delphi. Edited September 19 by Vincent Parrett additional info 1 Share this post Link to post
kihor 0 Posted September 20 9 hours ago, DelphiUdIT said: If you want, you can try this. Is the same COM but with new application: you don't need to register the COM Try to unregsiter the old one. After that build the projetc group (it is setting for WIn64 but you can change for WIn32) and run ... all is working without register the COM thanks to SxS technology (SideBySide). The EXE and DLL will be put in Bin32 and Bin64 (and there are already the manifests). There is a ReadMe and some Manifests (they are all equal excpet for the manifest that is included in the Project1 options). Bye Demo COM SxS.zip Thanks a lot! I will try this. Share this post Link to post