2008. november 18., kedd

Interfacing between Web and Applications


Problem/Question/Abstract:

Ever considered developing a web client which interacts with a windows application, or maybe a windows application which contains web pages within it’s confines, and allow them to seamlessly interact with one another.
The best way for me to explain this would be to give you a description of a current development and how I would like to introduce this interfacing.
Taking a database application which consists of a Contacts database and a Call logging system, and the requirement to introduce into the application a web front end or more specific, introduce a web plug-in and interact between the web pages and the application. Once this web interaction is introduced, the web front end(s) could make requests to application by calling any exposed routines. The Web page could call exposed functions from the application to fire off to a Contact record, or Call log view within the application.
A common example would be the idea of the Outlook Today page. A container within outlook which is or similar to a web page and interacts with the main application.
The definite advantage of this is the ease of handling large amounts of image and data information with Web technologies not just HTML but also Flash, XML which could then just plug into your application as controlling and interacting elements. Also the remote administrative and loading of these web interfaces, meaning less involvement with the client side, and entire areas of your main client application could be interacting web pages.
Developing such flexibility into a client side application using only Delphi would be a lot of work, “then again we’re Delphi developers, not VB developers and we thrive at a good challenge..”, but bringing this concept of a web plugin and interfacing it with my application sounds very powerful.

             "In this article I'll tell you how to do it...."


Answer:

“Now that I’ve got you in the mood lets start thinking how I might implement this …”

I have two main paths in which you could implement such a solution, and I will distinguish between the two :

  1.The more complicated and less desirable approach for my development would be for a web browser to load a page which creates a client side ActiveX object and for me to interface with an application through this object.  There is no reason why this method could not be adapted to implement my solution, and functionally work as I want it to.
            “In-fact I started my development using this method”
The main disadvantages of this method would be as follows :

The client would require an activex object installed on the machine along with the application which also must be digitally signed. Digital signing is required for Activex objects to be able to load securely for client assurance.
Clients rarely like the sound of active controls and objects when it comes to web pages.
The web page would require more information than I would desire and compared to my next solution is not so transparent.

  2. My preferred solution was to implement into the application the functionality to open and create instances of a Web Browser or TWebBrowser control and pass into the Web page an interface to an Automation object which exposes the application to the web page.
This then eliminates the requirement for the digitally signed Activex object which was to be called from the web page. Also I see an excellent advantage for Web pages having the capability to communicate with my application and not be affected if the application is not connected.
I could have a commercial web site, which my in-house system(s) could interact with, but is totally transparent to any client users of the site.
Also then I could then control which pages could connect to my (client)application more easily! Just a few ideas there !

Taking these two solutions into account, I am going to discuss and detail the second solution in the remainder of this article.

“So lets get down to the facts and implement this …”

Here are a few facts to help you understand how it can be done so easily.

A web page is filled with objects (which by the way all communicate pretty much through late binded interfaces) which you only usually have access to through client side scripting. All these interfaces would give us access control and use all of the objects within a web page.

Which leads me to the Window Object.

Each page or frame within a Web browser contains a window object. The window object exposes all of the objects and even javascript its self as it too is accessable through the window object interface.

“So all we need is to get the Interface for the window object into my application and then I have the access to all the objects within a web page ”

Weather I have an application which creates an instance of a web browser, or uses a TWebBrowser control I’ll always be using COM/Automation and Interfaces to control the browsers and they are mainly the same.

Note: If you do not have a TWebBrowser component already installed into your Delphi pallete then you just need to install the Internet Explorer type library and activex control. You will also want this installed for ease when creating instances of a web browser and not using the TWebBrowser control.

Basically under the Web Browser interface/TWebBrowser control you will find it contains many properties, methods and callbacks/events which are exposed for use. There is a property in particular which is important to me which is the Document object which I use to get the window object for a loading page.
The method I use is to force the web browser to notify my application every time a web page/frame loads and it then send with the notification the new Document object for that page/frame. Using the “DocumentComplete” callback/event I can setup this notification.

This is the event implementation which I use to obtain the window object

//-----

procedure TForm2.WebBrowser_V11DownloadComplete(Sender: TObject);
var
  WindowObject: Variant;
begin
  windowobject := Variant(WebBrowser_V11.Document).parentwindow;
end;

//-----

So now that I have the window object I can now (in Delphi) control the web page with statements like :

//-----

WindowObject.document.formname.editbox1.value := ‘Hey Cubud’;

Or even.

WindowObject.AScriptFunction(‘Shake that ass!’);

//-----

I’m sure your thinking, how can that work!

Remember I mentioned that the web page communicates as late binded interfaces. By assigning the window object to a variant we are effectively going to make blind access to the window object methods and properties and using late binding to invoke the methods and properties for use so when compiling, Delphi doesn’t kick up a fuss. Obviously late binding is slower than earlier binding, but then so is VB compared to Delphi. Really there is no real way around the late binding in this instance.

So that is basically how an application could control objects within a web page.

So next is how could I allow the web page to execute a javascript statement like :

//-----

<script language=&#8221;javascript&#8221;>
MyApplication.OpenAContact(ContactID);
</script>

//-----

&#8220;Well, I&#8217;m sure that a lot of you will have cracked the answer by now!&#8221;

The answer is to pass an interface from the application into the web page.

This is where I would simply add into my application an Interfaced Object, which I will pass the across to the web page.

Note: Because of the Web page will be using this interface to communicate with our application, and as the Web page will use Late Binding, the interface therefore needs to expose the methods for IDispatch.

Simpliest is to create an Automation object which already implements for IDispatch methods.

I&#8217;ll create the Automation object and add a method and an Identifier property into the Type library editor. Delphi will implement all the definitions and headings for the methods when the editor is saved, and all that is needed is to implement the code within the methods.

//-----
type
  TTMyAppInterfaceObject = class(TAutoObject, ITMyAppInterfaceObject)
  protected
    function OpenContact(ContactID: Integer): HResult; safecall;
    function Get_AppIdentifier: OleVariant; safecall; // Read Only Property
    { Protected declarations }
    &#8230;&#8230;&#8230;
      function TTMyAppInterfaceObject.OpenContact(ContactID: Integer): HResult;
    begin
      MainApplicationOpenContact(ContactID);
    end;

    function TTMyAppInterfaceObject.Get_AppIdentifier: OleVariant;
    begin
      Result := 'My Application';
    end;
    //-----

I now need to write a block of Javascript which can be included into any page which I would like this interfacing to be possible.
Note: I&#8217;ve implemented this method for web pages which contains multiple frames, and it is far simplier to make the parent of all frames, (the main page) the page that talks to the application and all other frames talk to that.

I have written this to a file called AppInterfacing.js so I can include it into any web page I would like interfacing.

//-----

<!--
var AppInterface
AppInterface = null;
OnAppAttached = null;

function AttachAppInterface ( AppIntf ) {
AppInterface = AppIntf;
if (OnAppAttached)
OnAppAttached()
}

function AppAssigned () {
return ((AppInterface != null)&&(AppInterface.AppIdentifier))
}
-->

//-----

Now with our web pages containing this block of Javascript, for the application to attach itself we just need to write the following statement.

//-----

function TForm2.AppInterface: ITMyAppInterfaceObject;
begin
  if FAppInterface = nil then
    FAppInterface := TTMyAppInterfaceObject.Create as ITMyAppInterfaceObject;
  result := FAppInterface;
end;

procedure TForm2.WebBrowser_V11DownloadComplete(Sender: TObject);
var
  WindowObject: Variant;
begin
  WindowObject := Variant(WebBrowser_V11.Document).parentwindow;
  try
    WindowObject.AttachAppInterface(AppInterface);
  except
    //-- &#8220;AttachAppInterface&#8221; not available
  end;
end;

//-----

So the entire page source could read as...

//-----

<script language=&#8221;javascript&#8221; src=&#8221;AppInterfacing.js&#8221;></script>
<script language=&#8221;javascript&#8221;>

function AppNotifyAttached() {
    alert(&#8216;Application has been attached&#8217;);
}
OnAppAttached = AppNotifyAttached;

function OpenContact(contactId) {
if (AppAssigned()) {
AppInterface.OpenContact(contactid);
} else {
               alert(&#8216;the contact id is &#8216;+contacted);
        }
}
-->
</script>

<HTML>
<BODY>
<form name="formname">
<input type="button" value="Request" onclick="OpenContact('123')">
</form>
</BODY>
</HTML>

//-----

Well that&#8217;s it for this article. I hope it reads well and gives a few of you some ideas. Any queries feel free to post me, I have tested this with IE 4/5 and Delphi 4/5 and worked really well.

Nincsenek megjegyzések:

Megjegyzés küldése