How to interop with TAutoIntfObject object from a TWebBrowser control?
delphi, twebbrowser
Solution
Use reference counting, ie. keep FExtObj as a reference to an interface, rather than object:
private
// the extension object
FExtObj: IDispatch;
...
constructor TMapBrowser.Create(AOwner: TComponent);
var
AExtObj: TOpenLayersExt;
begin
inherited Create(AOwner);
// create extension object
AExtObj := TOpenLayersExt.Create;
AExtObj.OnZoomChange := OnZoomChange;
FExtObj := AExtObj as IDispatch;
end;
destructor TMapBrowser.Destroy;
begin
FExtObj := nil;
inherited Destroy;
end;
Problem
I'm having a `TWebBrowser` control which implements `IDocHostUIHandler` to extend the control of a JavaScript interop through the `IDispatch` container. This works fine, except that I don't know, how to dispatch event from JavaScript back to the web browser control. The extension object is a container based on `TAutoIntfObject` like in `this example`. As you can see in the example, there is no interop with the web browser control. Ideally, I'd like to implement events on that extension object, but I don't know how to properly declare the `TAutoIntfObject` object in my web browser control. Let's say I'm having this extension object: ``` type TZoomChangeEvent = procedure(Sender: TObject; ZoomLevel: Integer) of object; TOpenLayersExt = class(TAutoIntfObject, IOpenLayers) private FOnZoomChange: TZoomChangeEvent; // the ZoomChange method is invoked from JavaScript procedure ZoomChange(ZoomLevel: Integer); safecall; public property OnZoomChange: TZoomChangeEvent read FOnZoomChange write FOnZoomChange; end; implementation procedure TOpenLayersExt.ZoomChange(ZoomLevel: Integer); begin if Assigned(FOnZoomChange) then FOnZoomChange(Self, ZoomLevel); end; ``` And a `TWebBrowser` control like this: ``` type TMapBrowser = class(TWebBrowser, IDocHostUIHandler) private // the extension object FExtObj: TOpenLayersExt; // IDocHostUIHandler::GetExternal method function GetExternal(out ppDispatch: IDispatch): HRESULT; stdcall; // this is the TOpenLayersExt.OnZoomChange event method implementation procedure OnZoomChange(Sender: TObject; Zoom: Integer); public // ordinary constructor constructor Create(AOwner: TComponent); override; end; implementation constructor TMapBrowser.Create(AOwner: TComponent); begin inherited Create(AOwner); // create extension object FExtObj := TOpenLayersExt.Create; // here the event method is properly binded; if I'd change the FExtObj type // to IDispatch with TOpenLayersExt(FExtObj) typecast, it wouldn't FExtObj.OnZoomChange := OnZoomChange; end; function TMapBrowser.GetExternal(out ppDispatch: IDispatch): HRESULT; begin // the problem is that I don't know how to properly pass this object to the // ppDispatch parameter; if this GetExternal method is called second time, // the FExtObj seems to be released, but I don't get why ppDispatch := FExtObj as IDispatch; Result := S_OK; end; ``` The problem is that if I declare the `FExtObj` object as `TOpenLayersExt`, the event method is binded but the `FExtObj` object reference seems to be released after the first extension object method invoke (from JavaScript). If I declare it as `IDispatch`, the reference is not released after the JavaScript function invoke, but the `OnZoomChange` event is not binded. It's hard to post the full code here as it's composed from more parts, `here is a complete project` made in Delphi 7. So my question is, how to consume events from `TAutoIntfObject` extension object in a web browser control; how to declare the extension object, so I'll be able to handle events from a web browser control and pass it to the `IDocHostUIHandler::GetExternal` method parameter still keeping the interface object reference ?