Michael T. McKeown
Developer Relations Group
Microsoft Corporation
May 1996
Contents
URL Monikers
Asynchronous Monikers
Progressive Downloading
ActiveX Hyperlinks
Hyperlink in Two Ways
Behind the Wizard's Curtain
URL monikers and ActiveX hyperlinks are two of the lower level ActiveX services that applications, active documents, and ActiveX controls can exploit to easily download data or to link to other documents on the Internet. Although URL monikers and ActiveX hyperlinks are different technologies, they are intimately related because ActiveX hyperlinks are based on URL monikers.
This paper has two main sections. In the first section we'll review the function of OLE version 2.0 monikers and then see how they have evolved into an asynchronous representation for use on the Internet. In the second part, we'll look at how ActiveX hyperlinking is extending the point-and-click browsing metaphor beyond HTML to create jump links to alternative types of objects and documents.
A URL (Universal Resource Locator) is a string representing a name and location for an object on the Internet. An example of such a string for a hypothetical Web page might be "http://www.myserver.com/myfolder/mypage.html" where "HTTP" identifies the protocol, and the remainder of the URL specifies the location of the Web page. You need protocol-specific code to find and download the object being pointed to (mypage.html). The URL scheme is extensible to other protocols such as FTP or Gopher. For each protocol, a protocol-specific handler must be used to point to and download the object pointed to by that URL.
Monikers are system-provided objects that were first introduced with the OLE 2.0 specification a few years ago, before interest in the Internet took off. They are persistent COM components that encapsulate both the ability to locate an object or data and to retrieve that object or data into memory. A file moniker points to a file on disk, and an item moniker points to a location within a file (for example, a group of cells in a spreadsheet). When combined, a file moniker and item moniker form a composite moniker (in the form of file!item!item ) that points to an object nested arbitrarily deep within a file. (For a detailed explanation of monikers, please refer to Chapter 9 in Inside OLE 2 by Kraig Brockschmidt [Microsoft Press, 1993]).
Because the IMoniker interface is derived from IPersistStream, moniker objects can read and write themselves persistently to or from a stream. The IMoniker interface has many methods, but its two primary ones are BindToStorage and BindToObject. BindToStorage tells the moniker to bring back the data to which the moniker is pointing. This process is referred to as "binding" the moniker. BindToObject instantiates the object the moniker points to and returns an interface pointer to the client. If BindToObject is passed a moniker that points to an object's persistent data, this will create an instance of the object and pass in the persistent data for the object to initialize itself. If BindToObject is passed a CLSID (class ID) of an object, it will create a brand new instance of that object in its default initialized state.
Under the previous OLE 2.0 specification, monikers carried out their binding operations in a synchronous fashion, that is, the thread initiating the bind operation would block until the operation was completed. Because the object making use of the moniker and the object being bound to were both on the same machine or on a very quick local area network, performance was not an issue. On the high-latency, slow-link Internet, binding operations must be carried out in an asynchronous fashion to be effective. The ActiveX client technology has therefore extended moniker capability to provide asynchronous binding functionality over the Internet via the Asynchronous Monikers specification (see \Specs\asyncmon.doc in the ActiveX Development Kit).
Monikers and URLs are very similar in that they both abstract the process of pointing to something. Microsoft has combined URLs and monikers into URL monikers to reference data and objects located at Internet locations referenced by URLs. URL monikers are currently the only implementation of the Asynchronous Monikers specification.
The OLE API's MkParseDisplayName function takes a string and creates a moniker from it. For instance, if it is passed a filename, it will return a file moniker. MkParseDisplayNameEx extends this function so that it also returns a URL moniker when passed in a string representation of a URL. If you need to extend this scheme beyond file, item, or URL monikers, you can use the IParseDisplayName function to define new moniker types via registry entries for custom moniker types.
To bind to an object, the system and the client cooperate to carry out the binding operation. The client requests the system to create a moniker via calls such as MkParseDisplayName, CreateFileMoniker, CreateItemMoniker, or CreateURLMoniker. The client asks the system to create a "bind context" to hold generic parameters and information associated with the bind operation that will be useful to all parties involved in the operation. The bind context can be created by calling the CreateBindCtx function directly. Alternatively, ActiveX controls can query the container for its IBindHost interface and call its GetBindCtx member to create the bind context. This bind context is passed into the moniker to assist it during the bind operation. For instance, it is used to tell the moniker to carry out the binding either synchronously or asynchronously.
This scheme fits well into getting the data or object that a URL points to. URL monikers encapsulate the protocol-specific WinINet (an extension of the Win32 API for the Internet) code to download the item to which they point. The WinINet API encapsulates the file, HTTP, FTP, and Gopher protocols upon which URL monikers are based. You can use URL monikers or the WinINet API interchangeably, but URL monikers handle the synchronization of the client process message queue transparently through a more simplified programming interface. In addition, URL moniker bind operations integrate more tightly with ActiveX containers such as Microsoft Internet Explorer version 3.0 than do WinINet bind operations. We recommend that you first try to use URL monikers, then move to WinINet if you need more control over the protocol processing. As a last option, you can write directly to Windows Sockets 2.0 if neither URL monikers nor WinINet meets your requirements for maximum protocol control.
Encapsulation performance should have no impact on your decision to use URL monikers vs. WinINet. Note that Internet Explorer 3.0, despite its demanding performance requirements, uses URL monikers for all HTTP, FTP, and Gopher downloads of HTML pages, OLE components, and associated property data.
URL monikers allow any client (an ActiveX control or any other application that needs to download bits from an Internet location) to bind asynchronously to a file on the Internet. Once the download process is started, the system updates the client with progress notifications via the new ActiveX client function IBindStatusCallback. The primary methods of this interface are OnDataAvailable (called many times to notify the client of new downloaded data), OnObjectAvailable (called once to notify the client of a downloaded code object), and OnProgress (called many times to notify the client of download progress).
To explain progressive downloading (as accomplished via asynchronous moniker binding) better, let's compare it to OLE 2.0 synchronous monikers on the desktop. OLE 2.0 synchronous monikers' calls to BindToStorage or BindToObject return immediately with a valid pointer to the data or object. With asynchronous monikers, calls to these methods return immediately as well, but the data or object pointer is not valid at the point the call is returned (because the object has not yet been completely downloaded from the Internet). BindToStorage receives a pointer to the progressively downloaded data via many calls to OnDataAvailable, and receives progress notifications via OnProgress. For asynchronous code bindings, BindToObject returns a valid object pointer in the one and only call to OnObjectAvailable.
In a fashion almost identical to synchronous OLE 2.0 moniker bindings, the client creates a moniker and a bind context. For a URL moniker, the client then optionally registers its IBindStatusCallback interface with the bind context so that the moniker can call back to the client with download notifications. (Note that failure to register the IBindStatusCallback interface will result in a synchronous return from BindToxxx.) The client calls either BindToStorage or BindToObject, passing in the bind context. The moniker figures out the protocol needed and creates a protocol-specific IBinding object to parse the protocol string, drive the download, and give progress notifications to the client as data becomes available. The IBinding object is then passed back to the client via the client's IBindStatusCallback::OnStartBinding method to control the binding operation (stop, start, suspend, change priority, and so on). The first callback occurs via the GetBindInfo call, which allows the client to specify the type of download (synchronous or asynchronous) and other information. Just before the download starts, the moniker calls OnStartBinding to notify the client that the download has begun. The binding operation ends with the last OnDataAvailable call for data, or with the single OnObjectAvailable call for code.
Today, HTML is the standard means of creating Web pages because all browsers support it and it provides a relatively simple programming interface. HTML, however, may not always be the best way to represent information to the user. With the ActiveX document specification, documents such as a Visio® drawing, for example, no longer have to be compromised into an HTML representation. Built on top of the OLE in-place activation model, a document can now be hosted in the frame of an ActiveX document container (such as Internet Explorer 3.0) in its much richer native format and be fully integrated with the navigation stack.
Active documents solve the representation compromise problem, but what about some of the great features of HTML, such as the ability to jump easily between locations within HTML documents by clicking a hyperlink? Users may also want to maintain their navigation history and their favorite Web page locations as they navigate in the single frame window of the browser.
To handle these issues, the ActiveX set of technologies has extended this point-and-click metaphor to non-HTML documents through ActiveX hyperlinks, which allows the definition of new document types that can be linked together easily for navigation purposes. ActiveX hyperlinks contain the information to jump from one location to the other and the ability to drive that navigation. This model is simple for Web authors to use and provides the features that users expect.
ActiveX hyperlinking functions and OLE interfaces are available to support both ActiveX document and non-ActiveX document applications. Additionally, you can create hyperlinks to jump from stand-alone, non-OLE applications to HTML documents and back again.
For example, if you want to add hyperlinking to a stand-alone, non-ActiveX document application such as Microsoft Notepad, you can use the new interfaces and functions to allow hyperlinking to and from that application. This would allow the user to click text displayed in Notepad and jump to the browser to view HTML. If the user then clicked on a text file in the browser (if the browser did not know how to automatically interpret textual data), the user could then hyperlink back to Notepad to view the page. An integrated global navigation and favorites stack is maintained as users jump between the browser and other non-ActiveX document applications throughout the navigation process.
There are two methods of dealing with hyperlinks, depending on the needed complexity of the hyperlink operation. The simple hyperlink navigation model is a helper API aimed at common navigation scenarios such as a control (like a pushbutton) embedded on a Web page that allows the user to jump to another page when it is clicked. The simple hyperlink APIs are an encapsulation of the more complex full hyperlinking interfaces, and we will discuss this method first. The alternative is a much richer set of OLE interfaces (not APIs) that do more refined navigation operations, which we will discuss second.
The simple hyperlink navigation API allows hyperlink navigation without knowledge of any other hyperlink interfaces or objects (basically saying, "here is where I want to jump to, now go there"). This API includes the following functions:
HlinkSimpleNavigateToString - Navigates to the location a URL string points to
HlinkSimpleNavigateToMoniker - Navigates to the location that a URL moniker points to
HlinkGoBack - Navigates to the previous location on the History list (if object pointed to is hosted in an ActiveX hyperlink frame)
HlinkGoForward - Navigates to the next location on the History list (if object pointed to is hosted in an ActiveX hyperlink frame)
These functions work within any ActiveX hyperlink frame application. (A hyperlink frame is the outer container that contains the document processing the user's request to navigate [when a navigation action occurs].) An application must expose the IHlinkFrame interface to be a hyperlink frame application.) For example, Microsoft Internet Explorer 3.0 is a hyperlink frame application that knows the user's current location in the navigation stack, and goes forward and backward properly.
The programmer is shielded from the architectural details of figuring out the History list to move forward and backward--an ActiveX control or ActiveX document hosted in a frame simply calls these functions to figure out where to go. The calling object needs to know where in the navigation stack the user currently is so they can go forward or backward properly. To do this, all that the calling object must pass in is the interface pointer to itself. In reality, this is the IUnknown pointer to the document or object that is initiating the hyperlink. (Note that this must be the pUnkOuter for an aggregated COM object.) If the pointer is NULL, the hyperlink is assumed to originate from an OLE-unaware application (an application that does not expose the IHlinkFrame interface), and the navigation History list will not be updated. Using this object pointer, the API travels through the interface hierarchy of that object to locate its outer container to calculate navigation history and do the in-frame navigation.
Some applications require a more granular means of navigation for which the simple hyperlinking APIs may not be sufficient. This richer model is what the ActiveX hyperlinking specification defines. This information is important to developers who are writing complex applications such as authoring tools, or hyperlink frames (such as browsers) that can host Active documents. For example, if a document within a frame calls one of these functions, the frame can take the proper navigation action.
This architecture centers around the standard system IHlink object, which encapsulates a pointer to the target document, the location string within the document, and the user-friendly name of that string. Also encapsulated within this object is the ability to complete the actual navigation on behalf of the container via its Navigate method. The helper hyperlink functions discussed in the previous section basically create an IHlink object from the parameters passed to them. Once created, the container uses the IHlink object to drive the navigation and update the browse context.
The most important part of an IHlink object is the moniker that points to the document that is the navigational target. For local navigation, this is a file moniker that points to a file on the user's machine or local network. On the Internet, this is a URL moniker that points to an HTTP or FTP location.
An item moniker is a string that points to a sublocation within that document, such as a range of cells inside a spreadsheet or a document located inside a database. When the target document is created during a hyperlink operation, it is passed this string so it can jump to the sublocation(s) within itself. In reality, you can use ActiveX hyperlinks to link to any object that can be represented by any type of moniker.
It is not uncommon for large HTML documents to contain an index of hyperlinks (serving as a table of contents) at the top of the document that permit the user to jump easily to those locations inside the document without having to scroll down manually. This is possible via ActiveX hyperlinks because they contain this information in the form of the item moniker string that specifies where it should link to within the document.
In addition to the moniker and the location string that only the target knows how to use, an ActiveX hyperlink can also contain the friendly name of the target location that is presented to the user. For example, the History list can contain the friendly name of the target location.
An IHlink object can be created from system calls such as CreateHlink, CreateFromString, CreateFromMoniker, or QueryFromData. These objects can be saved and loaded because they support persistence via IPersistStream. Thus, an IHlink object be created from an IDataObject object pointer and support creation via OLE drag and drop.
The target document is contained in the IHlink object's moniker. This document can be anything that a moniker points to (for example, an ActiveX document hosted in a frame, an object embedded inside another document, or a stand-alone application)--no special interface is required. (For OLE-unaware applications, this API will use ShellExecute to launch the necessary viewer to accomplish hyperlink navigation.)
If the target application containing the document being navigated to implements the IHlinkTarget interface, additional information can be passed to the target document during navigation (for example, the string for the sub-location within the document). If the IHlinkTarget interface is absent, the entire document is presented and the user has to manually navigate to the correct sub-location.
A browse context is a global system object that contains navigation information in the form of the navigation stack and history. This object is passed to the target through the IHlinkTarget interface. If the target document is not being hosted in the browser but as a stand-alone application, it can expose a History list or pair of back/forward buttons. An object (document) that wants to be a hyperlink target can choose to implement all or part of the IHlinkTarget interface to integrate tightly with ActiveX hyperlinks. If the object does not support IHlinkTarget, it can still act as a hyperlink target, but it won't be able to support internal navigation, and it will not have access to the common browse context that holds the navigation stack.
Semantically, the document being navigated from is called the hyperlink container. This object, unlike the target document, is not an actual interface but an entity that contains a hyperlink. In the scenario we described previously, where internal hyperlinks within an HTML page permit jumps to other locations within the same HTML page, the hyperlink container can also serve as a hyperlink target. In all cases, the hyperlink container is responsible for the visual representation of the hyperlink within itself. Traditionally, a hyperlink is represented by highlighted text, but a toolbar button or a menu item could also be used. In the future, Microsoft will produce user interface guidelines that explain exactly how this should be done in a consistent fashion.
Beyond displaying the hyperlink, the hyperlink container usually does little more than calling the hyperlink functions to execute the navigation. However, in complex cases, the container can expose a hyperlink site interface (IHlinkSite) that the IHlink object can use to give the container additional information. Using this site, a notification can be sent to the container's site when the navigation operation is complete. Alternatively, the hyperlink site interface allows the document to use relative URLs and call back to the container's site to find out the document's base URL to resolve them into absolute links. The hyperlink container can also be an ActiveX document, in which case it needs to understand how to sit within an ActiveX document frame so that if it navigates to a new ActiveX document it can be replaced by the new document.
When a document is navigated to, the outer container that contains it is called a hyperlink frame. IHlinkFrame is a simple interface that receives notifications about a navigation operation such as the URLs of both the new document being navigated to and the document being replaced. It usually maintains the navigation stack as the user browses as well as buttons to move forward, back, and to specific history and favorite locations.
In summary, URL monikers allow asynchronous downloading of code and data from Internet Web sites. ActiveX hyperlinks use monikers to establish hyperlinks for non-HTML Web pages. Here are a few key areas where you may want to use these ActiveX services in your development efforts.