Solved

window.external and IDocHostUIHandler

Posted on 2002-07-09
7
1,304 Views
Last Modified: 2012-05-04
Hi,

I want to extend DHTML by providing additionnal methods from my web host application. By javascript I'm able to call window.external.mymethod

At this time, it's working. My question is, how can take care of the namespace of the multiple COM interface provided?

I have 2 interfaces: Pages and Host with both a Load method.

Calling window.external.load imply a conflict between to the methods.

Can I do something like this?
- window.external.Pages.load
- window.external.Host.load

Thanks
0
Comment
Question by:pjroy
  • 4
  • 3
7 Comments
 

Author Comment

by:pjroy
ID: 7141700
Take a look at this code. It is mapping the external call to a specific interface, in this case TPages. Can it be possible to determine the interface from which window.external want to make a call and map it accordingly to the desired interface? How can I know the name of the method called?

//handle external method call from JavaScript
function TfrmMain.IEGetExternal(out ppDispatch: IDispatch): HRESULT;
var
     MyIDispatch : TPages;
begin
  MyIDispatch := TPages.Create;
  ppDispatch := MyIDispatch;
  result := S_OK; // Return we do have an IDispatch of interest to the browser.
     // IE will dispose of this interface object when it
  // is finished with it so we don't need MyIDpisatch.Free
  // etc etc anywhere.
end;
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 7141749

prjoy,

IE only allows for ONE external dispatch interface to be exposed when the script accesses "window.external.{name}". At this point you know nothing about what name may be requested, because IE is first trying to get a dispatch object. If you need to expose multiple objects to IE script then one solution would be to create a container interface (an interface that exposes both Pages and Host)

Example:
--------
(Container)
|
|--- Pages
|--- Host

The script would then access this by;

window.external.pages.load
or
window.external.host.load

If you want, I can write you a wrapper that does just that, and allows you to dynamically add name/interface pairs to it.

ie: DispContainer.Add('Pages', TPages.Create)

Hope this helps,
Russell


0
 

Author Comment

by:pjroy
ID: 7141761
Ok, you provided me exactly the answer i needed. I not exactly certain about the way to build a wrapper. It would help me if you send me one example. This will worth 200 points!

Thanks
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 26

Expert Comment

by:Russell Libby
ID: 7141764

prjoy,

I need about 30 minutes or so for this. Will post the full source code for this when done.

Russell
0
 
LVL 26

Accepted Solution

by:
Russell Libby earned 200 total points
ID: 7141903

prjoy,

I included the full source code for a seperate unit (call it whatever you want) that exposes the class TDispNamespace.

A little run down on the class

1.) You can add any dispatch based object to it, but the name you give it should not used to add another item. (ie don't do add('test', dispatch1) -> add('test', dispatch2))
2.) There is no Delete() or Remove(). This was intentional, because i have to hand out a dispatch ID (which i base upon the list position) which cannot change. This could be changed with a little coding. When the namespace object's ref count hits zero (or you free it), then all items get released.
3.) There is one event called GetDispatch() that allows you to hand back objects just-in-time (vs. having to add them before they are requested.) Once the name/object is in the list the GetDispatch procedure will NOT be called for that name again.


Example of usage

procedure TfrmMain.GetDispatch(Sender: TObject; Name: String; out Dispatch: IDispatch);
begin

 // Example of returning object on the fly
 if (LowerCase(Name) = 'word') then Dispatch:=CreateOleObject('Word.Application');

end;

function TfrmMain.IEGetExternal(out ppDispatch: IDispatch): HRESULT;
var
    MyIDispatch:  TDispNamespace;
begin

 MyIDispatch:=TDispNamespace.Create;

 // Add objects now
 MyIDispatch.Add('Host', THost.Create);
 MyIDispatch.Add('Pages', TPages.Create);
 // Example of binding the GetObject event which will
 // return a word object just-in-time
 MyIDispatch.GetObject:=TfrmMain.GetDispatch
 // Return the namespace dispatch
 ppDispatch:=MyIDispatch;

 result:=S_OK; // Return we do have an IDispatch of interest to the browser.
    // IE will dispose of this interface object when it
 // is finished with it so we don't need MyIDpisatch.Free
 // etc etc anywhere.

end;

Anyways, hope this is what your after. Let me know if you have problems or issues.

Russell





--------------------------------------------------------
unit whatever;

interface

uses
  Windows, SysUtils, Classes, ComObj, ActiveX;

type
  TDispItem      = class(TObject)
  private
     // Private declarations
     FDispatch:  IDispatch;
  protected
     // Protected declarations
  public
     // Public declarations
     constructor Create(Dispatch: IDispatch);
     destructor  Destroy; override;
     property    ObjDispatch: IDispatch read FDispatch;
  end;

type
  TGetDispatch   =  procedure(Sender: TObject; Name: String; out Dispatch: IDispatch) of object;
  TDispNamespace =  class(TObject, IDispatch)
  private
     // Private declarations
     FGetDispatch:TGetDispatch;
     FRefCount:  Integer;
     FList:      TStringList;
  protected
     // Protected declarations from IUnknown
     function    QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
     function    _AddRef: Integer; stdcall;
     function    _Release: Integer; stdcall;
     // Protected declarations from IDispatch
     function    GetTypeInfoCount(out Count: Integer): HResult; stdcall;
     function    GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
     function    GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
     function    Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
  public
     // Public declarations
     constructor Create;
     destructor  Destroy; override;
     function    Add(Name: String; Dispatch: IDispatch): Integer;
     property    GetDispatch: TGetDispatch read FGetDispatch write FGetDispatch;
  end;

implementation

function TDispNamespace.Add(Name: String; Dispatch: IDispatch): Integer;
begin

  // Add new item and object to the list
  result:=FList.AddObject(Name, TDispItem.Create(Dispatch));

end;

function TDispNamespace.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
var  pparams:    PDispParams;
begin

  // Set default result
  result:=S_OK;

  // Can't be a PUT
  if ((Flags and DISPATCH_PROPERTYPUT) > 0) then
  begin
     // Failure
     result:=DISP_E_MEMBERNOTFOUND;
     exit;
  end;

  // Param count has to be zero (we only expose other interfaces, no methods or props)
  pparams:=PDispParams(Params);
  if (pparams.cArgs > 0) then
  begin
     result:=DISP_E_BADPARAMCOUNT;
     exit;
  end;

  // Look up the object at the given index
  if Assigned(VarResult) and (DispID < FList.Count) then
  begin
     // Return the object
     PVariant(VarResult)^:=TDispItem(FList.Objects[DispID]).ObjDispatch;
     result:=S_OK;
  end
  else
     result:=DISP_E_EXCEPTION;

end;

function TDispNamespace.GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
var  lpName:     Array [0..1024] of Char;
     pNames:     POleStrList;
     pIDs:       PDispIDList;
     pdisp:      IDispatch;
     ID:         Integer;
begin

  // Set default result
  result:=DISP_E_UNKNOWNNAME;

  // Map the parameters to workable types
  pNames:=Names;
  pIDs:=DispIDs;

  // Fill the array of DISPIDs with DISPID_UNKNOWN
  FillMemory(DispIDs, (NameCount * 4), Byte(DISPID_UNKNOWN));

  // Copy the wide string to null terminated string
  StrPLCopy(@lpName, WideCharToString(pNames[0]), SizeOf(lpName));

  // Lookup name in the list
  ID:=FList.IndexOf(lpName);

  // Did we find it? If not, see if there is a handler for GetDispatch and
  // allow user to give us a dispatch
  if (ID < 0) and Assigned(FGetDispatch) then
  begin
     // Call GetDispatch
     FGetDispatch(Self, lpName, pdisp);
     // Were we given a dispatch?
     if Assigned(pdisp) then
        ID:=FList.AddObject(lpName, TDispItem.Create(pdisp))
     else
        exit;
  end;

  // Set the output dispatch id
  pIDs[0]:=ID;

  // Success
  result:=S_OK;

end;

function TDispNamespace.QueryInterface(const IID: TGUID; out Obj): HResult;
begin

  // Check for requested interface
  if GetInterface(IID, Obj) then
     result:=S_OK
  else
     result:=E_NOINTERFACE;

end;

function TDispNamespace._AddRef: Integer;
begin

  // Increment the ref count
  Inc(FRefCount);

  // Return new ref count
  result:=FRefCount;

end;

function TDispNamespace._Release: Integer;
begin

  // Decrement the ref count
  Dec(FRefCount);

  // Return ref count
  result:=FRefCount;

  // If ref count is zero then time to go bye-bye
  if (FRefCount <= 0) then Free;

end;

function TDispNamespace.GetTypeInfoCount(out Count: Integer): HResult;
begin

  // No type info
  Count:=0;

  // Return success
  result:=S_OK;

end;

function TDispNamespace.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult;
begin

  // No type info
  result:=DISP_E_BADINDEX;

end;

constructor TDispNamespace.Create;
begin

  // Perform inherited
  inherited Create;

  // Initialization
  FList:=TStringList.Create;
  FRefCount:=0;

end;

destructor TDispNamespace.Destroy;
var  pdi:        TDispItem;
     index:      Integer;
begin

  // Release all the dispatch items in the list
  for index:=FList.Count-1 downto 0 do
  begin
     pdi:=TDispItem(FList.Objects[index]);
     pdi.Free;
     FList.Delete(index);
  end;

  // Free the list
  FList.Free;

  // Perform inherited
  inherited Destroy;

end;

constructor TDispItem.Create(Dispatch: IDispatch);
begin

  // Perform inherited
  inherited Create;

  // Set dispatch
  FDispatch:=Dispatch;

end;

destructor TDispItem.Destroy;
begin

  // Release the interface
  FDispatch:=nil;

  // Perform inherited
  inherited Destroy;

end;

end.
0
 

Author Comment

by:pjroy
ID: 7142137
Your answer will be of great help!
0
 

Author Comment

by:pjroy
ID: 7180763
Hi,

Ok, everything is working except for a thing that bothering me.

In each window.external, the OnGetExternal event handler is called MyIDispatch:=TDispNamespace.Create is executed and that means that everything is reinitialzed each time. My COM interfaces builds a tree like data structure. I don't want to rebuild it everytime. It will be more effective to build the tree at startup, or on demand when its properties are accessed.

I have tried to hold globally the reference to the base Dispatch interface and then do something like if MyDisp = nil then Create, so every subsequent call do not recreate it. But I've got memory access error. Any guidelines on this?

Thanks
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Suggested Solutions

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now