在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
Contents
IntroductionWhen writing programs that use the TWebBrowser control as part of the user interface I've sometimes needed to respond to user interaction with the control. The official way to do this is to extend the web browser's external object, and this is the technique we will use in this article. The external object is part of TWebBrowser's document object model. The object enables interaction with the environment surrounding the browser control – in this case our program. We can extend the external object by adding methods to it that are implemented in Delphi rather than in JavaScript or VBScript. We do this by creating a COM automation object that exposes the required methods and then notifying the TWebBrowser control that the COM object extends the external object. The new external object methods can then be called from JavaScript or VBScript running in the TWebBrowser. When these methods are called our Delphi code executes. The rest of this article examines how to use Delphi to create and manipulate the external object. OverviewThe solution divides neatly into three main activities:
The next sections discuss each of the above activities in turn. Finally a case study will be presented that puts the techniques we have learned into practise. We make a start in the next section by discussing how to extend the external object. Implementing the external objectAs already noted we extend the external object by creating a COM automation object – i.e. one that implements an interface that derives from IDispatch. To create a new type library using the Type Library Editor in Delphi 7, display the New Items dialog by selecting the File | New | Other menu option. Then choose Type Library from the dialog's ActiveX tab.
An easy way to do this is to derive the new class from TAutoIntfObject. This class implements the methods of IDispatch and so saves us from having to do this ourselves. However, TAutoIntfObject needs a type library to work with. Consequently we will use Delphi's Type Library editor to create a suitable type library that defines our new interface. Once we have defined the required interface in the Type Library Editor we create the type library by pressing Ctrl+S. This will do four things:
When creating the interface in the Type Library Editor we must abide by the following rules:
Once we have our new type library and interface we create a new class that descends from TAutoIntfObject. Then we implement the methods of our interface in the class. This can be done by copying the method prototypes from the interface declaration in the Note that Delphi creates the method prototypes using the safecall calling convention which means that any [out,retval] parameters become function results. For example, suppose we use the Type Library Editor to create an interface called IMyIntf that has two methods, Foo and Bar. Assume the method parameters are defined as in Table 1.
The type We would therefore include the following methods in our class declaration: type These methods would then be implemented as required. Remember that we don't declare or implement any methods of IDispatch since they are already implemented by TAutoIntfObject. Now TAutoIntfObject's implementation of the IDispatch methods depends on having access to the type library that describes the methods of the interfaces implemented by descendent classes. This is achieved by passing an object that implements ITypeLib as a parameter to TAutoIntfObject's constructor. It is our job to create such an ITypeLib object that "knows about" our type library. Remember the
{$R *.tlb} directive mentioned earlier ensures that the .tlb file generated by the type library editor is included in our program's resources. We do this by declaring a parameter-less constructor for the derived class. In the constructor we call the LoadTypeLib API function, passing the name of our application as a parameter. LoadTypeLib accesses the type library information that is embedded in the application's resources and creates the required ITypeLib object based on this information. The object is then passed to the inherited constructor. Assuming our derived class is named TMyExternal, Listing 3 shows the constructor's implementation. constructor TMyExternal.Create; We have now seen how to implement the external object. In the next section we will examine how to register the object with the web browser control. Registering the external object with TWebBrowserHaving implemented the COM object that extends the external object how do we tell the web browser about it? For detailed information about creating a container for TWebBrowser and implementing IOleClientSite and IDocHostUIHandler please see "How to customise the TWebBrowser user interface".
The answer is by creating a container object that hosts the web browser control and implements the IDocHostUIHandler interface. Any web browser control container object must also implement IOleClientSite. IDocHostUIHandler has a GetExternal method. In our implementation of this method we will pass a reference to our custom external object to the browser. There are numerous other methods of IDocHostUIHandler that we have to implement, but we can get away with stubbing them out. In a previous article (see the "Further reading" box) I discussed IDocHostUIHandler in detail and presented a do nothing implementation – TNulWBContainer. I won't repeat that presentation here, so please check out the earlier article if you need to review how this is done. Doing all the hard work in TNulWBContainer means that our implementation of IDocHostUIHandler and IOleClientSite, which we will call TExternalContainer, can be quite simple if we descend it from TNulWBContainer. Listing 4 has the declaration of the class, while Listing 5 shows its implementation. type constructor TExternalContainer.Create( Notice that we create an instance of our external object extension in the constructor and store it in a field of type IDispatch. We then implement GetExternal to pass back a reference to the external object in ppDispatch and return S_OK to indicate we have provided the object. We pass a reference to the web browser control we are hosting to the constructor. This reference is simply passed on to the inherited constructor where it is recorded and notified that our object is its container. See the implementation of TNulWBContainer.Create for details of how this is done. We have now completed the code necessary to register the external object with the web browser. In the next section we look at how to call into the external object from JavaScript. Calling into Delphi from JavaScriptHaving implemented our external object extension and registered it with the browser control, it is now time to look at how we call the object's methods from JavaScript. A similar approach is taken if you wish to use VBScript rather than JavaScript, but the precise details are, as they say, left as an exercise! All we have to do to call into our Delphi code from JavaScript is to reference the relevant methods of the external object. For example to access external method Foo from JavaScript we would use the code presented in Listing 6. external.Foo(); external object methods can be called anywhere that a JavaScript method call is valid – either from event handlers of HTML elements or from blocks of JavaScript code. For example, say we have implemented the interface shown in Listing 7 in Delphi, and have registered it as an extension of the external object: type Assume that SetLabel sets the caption of a TLabel to the given text and GetEMail looks up the name of the given person in a database and returns their email address. We can ensure the label's caption is set when a user clicks a link by using HTML such as that presented in Listing 8. <body> We can also write someone's email address into the HTML document using a block of JavaScript similar to that given in Listing 9. <body> As can be seen, once the hard work of setting up and registering the external object has been done, it is very easy to call the required methods. That concludes our discussion of the techniques involved in calling Delphi code from JavaScript. In the next section we will work through a case study that draws all the strands together. Case studyOverviewOur case study is a simple application that illustrates the techniques discussed in this article. The program we will develop lists some of the programs available on the DelphiDabbler website. Clicking one of the program names will display a brief description (precis) of the program in a box that follows list of programs. When the mouse is moved over a program name the URL of the program's web page will be displayed in the status bar. The status bar will clear when there is no program name under the mouse. The precis of each program is stored in a data file that is read by our application. Here is a screenshot of the completed program:
Figure 1: Screenshot of case study program
We will develop the program in the following order:
Designing the main formTo begin the project, start a new Delphi GUI application and set up the main form as follows:
That is all there is to the main form. Defining the external objectLet us consider the methods we need to add to the browser's external object. From the specification above we see that we need methods to perform the following actions:
We can now create an interface that contains each of the required methods. Start Delphi's Type Library Editor and use it to create a new interface named IMyExternal. Now add three methods using the information in Table 2.
Press Ctrl+S to save the type library and name it type Implementing the external objectNow that we have created the IMyExternal interface, we implement it in a class named TMyExternal. Listing 11 has the class declaration. type As expected, the methods of IMyExternal are specified in the class's protected section. We also have a private helper method – ShowSBMsg – that displays a given message in the status bar. This method is used by both ShowURL and HideURL as we will see in a moment. The fData string list is used to store the precis of the different programs. This field is accessed by GetPrecis. Let us look at the implementation of the class. We will start with the constructor and destructor shown in Listing 12. constructor TMyExternal.Create; The first three executable lines in the constructor are boilerplate code that has been explained earlier in the article. Following the call to the inherited constructor we create the TStringList object that is to store the precis data. We then read the string list's contents from a data file named Now move on to examine the implementation of the three IMyExternal methods shown in Listing 13. function TMyExternal.GetPrecis( GetPrecis looks up the given ProgID in the Values property of fData and returns the value found there. The data file that was loaded into fData in the constructor contains a line for each program. Each line has the format HideURL uses ShowSBMsg to display an empty string in the status bar, which has the effect of clearing any previous message. ShowURL simply appends its ProgID parameter to a literal string to produce the URL of the program's web page. It then calls ShowSBMsg to display the URL in the status bar. All that remains is to look at Listing 14, which shows the implementation of ShowSBMsg. procedure TMyExternal.ShowSBMsg(const Msg: string); Hmm – no mention of the status bar! What's happening here is that we're creating an instance of the VCL's THintAction action class, storing the message we want to display in its Hint property then executing the action. A magical feature of THintAction is that it automatically displays its hint in any TStatusBar that has its AutoHint property set to True. This let's us decouple our external object implementation quite nicely from the program's form. Implementing IDocHostUIHandlerUnits containing IDocHostUIHandler & TNulWBContainer are included with this article's source code.
As already noted, we are re-using code from an earlier article for our declaration of IDocHostUIHandler and for the do-nothing implementation of the interface, TNulWBContainer. So, to begin with, we must add the We now create our custom container class, TExternalContainer, by descending from TNulWBContainer and overriding the GetExternal method to get the functionality we need. We will use exactly the same code as we developed in Listings 4 & 5. Registering the external objectOur final piece of Delphi code registers our TExternalContainer object as a client site (container) for the browser control. This is done in the main form simply by instantiating a TExternalContainer object and passing a reference to the browser control to its constructor. Recall that TExternalContainer's inherited constructor automatically registers the object as a client site of the contained web browser control. We will use the form's OnShow event handler to create TExternalContainer. We will also use this event handler to load the required HTML file, as Listing 15 shows. procedure TForm1.FormShow(Sender: TObject); Notice that we have stored a reference to the container object in a field named fContainer. Add such a field, with type TExternalContainer to the form's declaration. Having created the container object we must also ensure it gets freed. This is done in the form's OnHide event handler as Listing 16 illustrates. procedure TForm1.FormHide(Sender: TObject); Creating the HTML fileAll that remains to be done is to create the HTML file that we loaded in Listing 15. This file is named <?xml version="1.0"?> To ensure that a link does nothing when clicked we call javascript:void(0) in the a-link tag's href attribute.
Looking at the body section of the file we see that it contains a list of four program names, each defined as links ( The a-links' onclick events call the ShowPrecis JavaScript routine passing in the id of the relevant program as a parameter. ShowPrecis is defined in the HTML head section. The function first finds the Returning to the link tags, note that the onmouseover events directly call external.ShowURL with the id of the required program while the onmouseout events call external.HideURL. These JavaScript methods execute methods of the same name in TMyExternal, which in turn show and hide the program's URL in the status bar. The only other item of note in the HTML file is that the head section contains an embedded style sheet that styles the This description of the HTML file completes the discussion of the case study. Source codeThe case study is available for download. Delphi 7 was used to create the program and it was tested on Windows XP Pro SP2 using Internet Explorer 6. A ReadMe file that describes how to use the source is included in the download. This source code is merely a proof of concept and is intended only to illustrate this article. It is not designed for use in its current form in finished applications. The code is provided on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. The source code is released under the same Creative Commons license as this article – see the bottom of this page for details. If you agree to all this then please download the code using the following link.
That completes the substantive part of the article. The final section summarises what we've achieved and provides links to some reference material. |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论