Appendix B. OLE and ActiveX in SWT/JFace

In earlier chapters, we explored how SWT is built in layers: a small library of C code built with Java Native Interface (JNI) interacts with the underlying operating system to provide the necessary building blocks for more elaborate capabilities. One of the design goals of the native layer was for it to be very small, often providing simple wrappers around native APIs. Using this novel approach, the OTI/IBM team has been able to give programmers unprecedented access to the native capabilities of all supported platforms. In so doing, the team chose not limit itself to the features common to all platforms. Among these platforms, Microsoft Windows offers a unique capability that has appealed to Visual Basic programmers for many years: reusable binary objects, otherwise known as COM objects.

SWT/JFace programmers haven’t been left out; this appendix covers the nature and depth of COM support in SWT. Specifically, you’ll see how you can include ActiveX controls and OLE documents inside your applications in just a few SWT method calls. So that you can fully take advantage of this feature, we’ll first review some basic COM features and general principles.

B.1. COM simplified

Microsoft designed the Component Object Model (COM) to try to solve a simple problem: how to reuse binary objects. Previous solutions based solely on shared libraries (Dynamic Link Libraries [DLLs]) showed that they weren’t practical for C++ programming and that managing their proliferation on a given system was in itself a major cause of application problems. In the process of designing a replacement solution, Microsoft felt it should also address a new class of problems for the time: location transparency.

In the end, the new technology was built to provide a unique solution for situations using three distinct types of objects:

  • In-process objects share the same address space as the client code using them (the code is inside DLLs).
  • Local out-of-process objects are located on the same computer as the client code but reside in a separate address space (inside a separate EXE file).
  • Remote objects are located inside an EXE or DLL on a different machine and are accessible via remote procedure calls.

These are important concepts for anyone interested in doing COM with SWT/JFace.

B.1.1. IUnknown/IDispatch

COM is based on the notion of interfaces. Interfaces allow the logical grouping of functionalities as well as their discovery at runtime by querying the objects themselves. Each interface has an identifier (IID) that uniquely defines both the methods available and their physical placement relative to one another in memory. Physically, interfaces are organized into vtables (arrays of function pointers). The notion of physical ordering of these functions is crucial, as you’ll see when we investigate the details of SWT programming. COM makes widespread usage of Globally Unique Identifiers (GUIDs). Specific GUID uses include object identifiers, type library identifiers, and interface identifiers. The algorithm for generating these IDs is beyond the scope of this book, but it’s described on the Microsoft Developers Library web site (msdn.microsoft.com).

The way COM interfaces are versioned may surprise Java programmers: An interface that has been published can’t be modified. Instead, it must be extended via an entirely new interface. According to Microsoft’s best practices, the new interface should have the same base name followed by a version number that increases for each new version. For example, when Microsoft needed to give users more control over the web browser ActiveX control, it extended the original IWebBrowser interface with a richer IWebBrowser2.

Unlike the Java model, where class files contain enough metadata to allow the reflection API to return a complete description of objects and methods, the COM runtime discovery model is based on the existence of IUnknown, a core interface that all others extend. Given a specific GUID, QueryInterface returns a pointer to the interface implementation. The two other IUnknown functions, AddRef and Release, are responsible for tracking the number of references to the interface and returning all allocated resources to the operating system. Reference counting is an important aspect of COM programming and is the cause of many bugs that are difficult to identify.

SWT fully adheres to Microsoft’s guidelines for reference counting, but sometimes you’ll need to remember these simple rules: Clients are responsible for calling AddRef and Release on every interface they query; and both calls must be made on the same interface reference, to allow an object to track the references on a per-interface basis rather than for the whole object. These important functions are listed in table B.1.

Table B.1. Functions of the IUnknown COM interface

IUnknown function

Description

AddRef Increases the reference count for this interface
QueryInterface Returns pointers to supported interfaces
Release Decreases the reference count for this interface

Using this simple design, and with the aid of a small runtime library that provides support for registering, discovering, and instantiating objects, it’s possible to start creating powerful reusable binary entities using a language like C or C++. Objects can then be segregated into families based on their implementing predefined sets of interfaces, all deriving from the core IUnknown. Over the years, Microsoft has defined several such families: scriptable objects, ActiveX controls, active documents, and so on. Some of these definitions have gradually evolved toward fewer mandatory interfaces and more optional behaviors.

Although a powerful concept, this interface proved too complicated for high-level languages like VBScript, JScript, and the first versions of Visual Basic. To allow these interpreter-based languages (and other scripting languages) to access COM objects, Microsoft defined another key COM interface. IDispatch allows object capabilities to be queried by name rather than by interface identifier. Like all the other COM interfaces, IDispatch extends IUnknown. Each method that needs to be publicized is given a unique dispatch identifier that can be used to invoke it. COM automation (or just automation) is the process of letting client code interface with a COM object using the IDispatch-based discovery and invocation mechanism. The flexibility it provides comes at a price: the automation querying and invocation process is significantly slower that the default binary binding used for non-automation calls. The functions provided by this interface are shown in table B.2.

Table B.2. Functions of the IDispatch COM interface

IDispatch function

Description

AddRef Increases the reference count for this interface
GetIDsOfNames Maps a single member and an optional set of argument names to a corresponding set of integer dispatch IDs (DISPIDs)
GetTypeInfo Gets the type information for an object
GetTypeInfoCount Retrieves the number of type information interfaces that an object provides (either 0 or 1)
Invoke Provides access to properties and methods exposed by an object
QueryInterface Returns pointers to supported interfaces
Release Decreases the reference count for this interface

SWT provides methods for two of these functions. Provided with an optional list of method names, GetIDsOfNames() returns a list of matching dispatch IDs (DISPIDs). These can be used in subsequent calls to the Invoke() method to trigger their execution. Using this simple mechanism, objects can expose both methods and properties. COM recognizes four reasons to call Invoke():

  • To call a method (DISPATCH_METHOD)
  • To retrieve the value of a property (DISPATCH_PROPERTYGET)
  • To modify the value of a property (DISPATCH_PROPERTYPUT)
  • To modify the value of a property that is a reference to another object (DISPATCH_PROPERTYPUTREF)

 

Note

You may wonder why there’s no way to get the value of a property that is a reference to an object. This situation is covered by DISPATCH_PROPERTYGET via the fact that all automation methods manipulate a universal data type called Variant. A variant is a unique way to represent all the possible data types supported by automation-capable languages. You can find the nature of the content of a given variant by ORing predefined constants. Possible contents include a simple string (BSTR in COM parlance), a primitive type, or a reference to an object. Object references come in two flavors: a reference to an IUnknown and a reference to an IDispatch instance. Variants are a rich data type, and at this point SWT supports only a portion of the complete specification. Notably absent is support for Safe-Arrays (the Visual Basic way of dealing with arrays) and user-defined types (the Visual Basic types).

 

B.1.2. Object hosting

The ability to reuse the capabilities of an external object by embedding the object directly inside your application lies at the heart of client-side COM programming. COM provides two types of user experiences for interacting with embedded objects. In the first scenario, the embedded object retains its own interface that users see in a window that’s separate from the main application window. In the second scenario, known as in-place activation, the user can interact with the embedded object without leaving the container document or application. In this scenario, the container and the embedded object collaborate to provide a composite menu bar where commands and features from both applications are available at the same time. The richer of the two scenarios, in-process activation is also more complicated to program, because it requires (among other things) that mouse and keyboard events be routed properly. COM allows both local servers (standalone EXEs like Microsoft Word) and in-process servers (DLLs) to be in-place-activated.

In both scenarios, the container must implement a number of predefined COM interfaces in order for the embedded object to communicate with it. Microsoft refers to these mandatory interfaces as sites. The complete description of all the site interfaces and the features they provide is beyond the scope of this book. In most cases, the interfaces implemented by the SWT programmers are enough; however, in some situations you’ll need to extend one of the default site classes and implement additional site interfaces. Hosting an ActiveX control versus an OLE document requires the implementation of two sets of COM interfaces, described in tables B.3 and B.4. One of the examples later in this appendix shows how to create a custom container by extending an existing one.

Table B.3. Document site interfaces

Interface

Description

IAdviseSink Receives general notifications from the embedded object
IOleClientSite Manages general communication with the container
IOleInPlaceSite Manages in-place activation of the hosted control
IOleDocumentSite Provides a means to activate a hosted document
IUnknown The fundamental COM interface
Table B.4. Control site interfaces

Interface

Description

IAdviseSink Receives general notifications from the embedded object
IOleControlSite Manages general communication with the container
IOleInPlaceSite Manages in-place activation of the hosted control
IUnknown The fundamental COM interface

B.1.3. Object instantiation

For the most part, instantiating a COM object is a straightforward task. The simplest case involves a single call to CoCreateInstance(). However, more complex object-instantiation strategies involve calling OleCreate() and passing it an instance of IStorage that represents either a new document or one that you intend to edit; or calling CoGetClassObject() to obtain an instance of an ActiveX IObjectFactory or IObjectFactory2, and then calling their respective CreateInstance()or CreateInstanceLic() method to obtain the new object’s IUnknown instance.

You use the latter approach to instantiate ActiveX controls that need a license key to operate. Microsoft added licensing to ActiveX controls in order to prevent programmers from creating their own applications by reusing the controls redistributed with other applications; you had to purchase the development version of the controls to use them. SWT contains a default implementation of these instantiation strategies, but you may need to replicate them inside your code to address current limitations or bugs in SWT. The beauty of the SWT COM library layering is that you’ll be able to do so easily.

B.1.4. Event handling

The COM object embedding model includes a rich mechanism for dispatching and handling events. It’s based on several simple notions. A sink interface is an interface exposed by a COM client; it’s called by a COM server as a callback for the purpose of sinking (handling) event notifications. In order for a server to dispatch events to a client’s sink interface, the client code needs a process to pass a sink reference to the server. This is done via server connection points. A connection point is the server-based mechanism that handles notification to the client’s sink interface. A server exposes all of its connection points by implementing the IConnectionPointContainer interface. Clients call FindConnectionPoint(), passing it the GUID of an interface to get a reference to the IConnectionPoint exposed by the server for this sink interface. IConnectionPoint contains the Advise() and Unadvise() methods to start and stop the flow of incoming events on the sink interface, respectively.

B.1.5. Threading model

The COM threading model is based on the notion of apartments. An apartment is an arbitrary construct meant to help the COM runtime library make the right decisions about how to route method calls to a COM object. The simplest of all scenarios is the single-threaded apartment (STA) model where the runtime takes care of all concurrency problems (which happens when multiple threads in the client code call the same method on the same object at the same time) transparently. This is done by creating a hidden window and using Windows’ default message-passing mechanism to ensure that all the method calls are serialized.

In the multithreaded apartment (MTA) model, each COM object must be multi-thread-aware to ensure that no data corruption can arise as the result of two simultaneous calls from two distinct client threads. In recent versions of Windows, Microsoft has added more threading models that are beyond the scope of this book.

Like Swing, SWT doesn’t contain any synchronization code to guard against resource corruption due to concurrency. The rationale behind this decision is that in most cases, the performance trade-off is too great to be justifiable. Consequently, synchronization is entirely your responsibility. To avoid introducing synchronization code, the SWT team chose to support only the STA threading model where no special code is required; all synchronization issues are handled by the COM runtime.

B.2. The SWT COM library

True to the general philosophy adopted for SWT, support for COM comes in the form of a minimal amount of C code coupled with a series of Java classes building on these foundations. The interesting side effect is that SWT COM programming is very similar to C++ COM programming. This can be a double-edged sword. The bonus is that whenever you’re in doubt about how to use a specific SWT feature, you can look for help in the Microsoft documentation. The trade-off is that you’ll sometimes be looking at unusual and nonintuitive Java code. As you get more familiar with code from both languages, you’ll come to enjoy the outcome and forget about the means.

In the following sections, we’ll cover the parts of the SWT COM library shown in figure B.1. We’ll begin with a tour of the native language part of the library and then look at the user-oriented Java classes that use it. The OleEventTable, OleEventSink, and OlePropertyChangeSink classes are only visible inside the org.eclipse.swt.ole.win32 package.

Figure B.1. Core components of the SWT COM library

B.2.1. The native language support library

When you look at figure B.1, don’t be fooled by the names of the packages. The code inside org.eclipse.swt.internal.ole.win32 isn’t just for SWT developers, and you’ll often find yourself referring to it for advanced SWT COM applications.

The org.eclipse.swt.internal.ole.win32.COM class

As we discussed, the COM threading model supports several options. The COM specification mandates that every thread that wants to use COM must first call the runtime to specify a threading model. SWT performs this mandatory initialization for you inside a static block located in the COM class. The default threading model is apartment-threaded; therefore you don’t have to do anything special to ensure that all calls to embedded COM objects are serialized.

The class has no constructor or instance members, but it contains a long series of constants and static methods. The static methods are mostly native methods named after their C equivalents. Their role is to expose the COM runtime to Java. The method signature is often identical to the original Windows API. So, when in doubt, look at the original Microsoft documentation for explanations and examples.

Half the constants are instances of another class from the same package. SWT stores objects and interfaces unique identifiers using the GUID class For the most part you need not concern yourself with GUID because it contains no methods. However, it has an interesting public static final field named sizeof that contains the size of the structure in bytes. The COM SWT code uses this pattern in all classes that map to a native structure (more on this in section B.2.2).

Chances are, all the interfaces you’ll ever need to access are defined in COM as public constants. The following examples were taken from the source code and show how you should define any interface not already included:

public static final GUID IIDIUnknown =
            IIDFromString("{00000000-0000-0000-C000-000000000046}");
public static final GUID IIDIDispatch =
            IIDFromString("{00020400-0000-0000-C000-000000000046}");

IIDFromString() returns an instance of GUID based on the string representation of an interface identifier. Similarly, CLSIDFromProgID() and CLSIDFromString() let you find an object’s GUID from the same string representation or from the object’s program identifier. To instantiate a COM object, you need to find either of these two values. The IsEqualGUID() method lets you compare two GUIDs for identity, which you need to do to implement the QueryInterface() when creating custom COM interfaces.

The COM class also contains the low-level SWT code to access native drag and drop: the RegisterDragDrop(), RevokeDragDrop(), and DoDragDrop() methods. The latest version of SWT introduced a new cross-platform level of abstraction that uses this code. More interestingly, the class lets you manipulate OLE storage files. StgCreateDocfile() allows you to create a new file; StgIsStorageFile() lets you test if a file is an OLE storage file; a call to ReleaseStgMediums() is necessary to release the memory allocated by COM to an open storage file; and StgOpenStorage() is used to open a storage file.

If you’re familiar with Visual Basic, you know how simple it is to deal with strings. This simplicity comes at a price for people programming with lower-level languages. OLE strings, otherwise known as BSTR, can be manipulated using the SysAllocString(), SysFreeString(), and SysStringByteLen() methods. Sometimes COM requires that newly allocated memory be placed under the control of its runtime (often the case when dealing with automation) to allow sharing of that memory between different process address spaces. You can use CoTaskMemAlloc() and CoTaskMemFree(), respectively, to allocate or free blocks of memory compatible with COM.

Several of the methods from the COM class come with multiple prototypes, due to the strongly typed nature of Java. For example, the MoveMemory() method comes in 16 different flavors, one for each of the main types of COM structures you may have to manipulate. Keep in mind that using them takes you one step closer to function pointers (and therefore dangerously closer to memory leaks) than the makers of Java intended.

The most unconventional part of the COM class is a series of VtblCall() native static methods with different parameter combinations. These cover the method signatures for all the COM interfaces supported by SWT. The first two parameters are the index of the method that needs to be called in the vtable followed by the address of the table. The native code uses the index to find the address of the method to call inside the vtable and calls it with the remaining parameters. In the following example, int COM.VtblCall(int fnNumber, int ppVtbl, GUID arg0, int[] arg1) is called to implement the QueryInterface() method from IUnknown:

public int QueryInterface(GUID riid, int ppvObject[]) {
  return COM.VtblCall(0, address, riid, ppvObject);
}
The org.eclipse.swt.internal.ole.win32.COMObject class

Although it isn’t composed of native methods, the COMObject class belongs to the lower level of the COM library. Its purpose is to provide a Java representation of the vtable at the heart of every COM object. The class contains 80 public methods with the same signature—public int methodXX(int[] args)—and an equal number of matching callbacks prototyped static int callbackXX(int[] callbackArgs). Each method is a placeholder for the matching method inside the vtable of the COM interface.

By default, the 80 methodXX() methods return a constant called COM.E_NOTIMPL that tells the caller that the method isn’t implemented. This avoids COM errors and gives you room to implement complex COM interfaces. All COM interfaces extend one another to form a hierarchy, and each level of inheritance translates into an extension of the methods in the vtable. Provided that some of the standard COM interfaces are two or three levels down from IUnknown, the creators of SWT have tried to anticipate future growth.

The class constructor is an array of int. Its size is the number of methods in the vtable, and its content is the number of parameters each vtable method takes. Be sure you don’t make any mistakes when you create this array, or you’ll be in for difficult debugging and crashes. Internally, the constructor uses this information to create an array of callbacks, one for each of the methods in the vtable. The native layer uses these callbacks to invoke Java code when COM needs to invoke a method from your interface.

Program identifiers revisited

Table B.5 contains the program identifiers for several common applications you may encounter in your exploration of the SWT COM library.

Table B.5. Common program identifiers

Program identifier

Description

Shell.Explorer Internet Explorer
Word.Application Microsoft Office Word application (as an out-of-process server)
Word.Document Microsoft Office Word document
Excel.Application Microsoft Office Excel application (as an out-of-process server)
Excel.Sheet Microsoft Office Excel document
Excel.Chart Microsoft Office Excel chart
PowerPoint.Show Microsoft Office PowerPoint presentation
Visio.Drawing Visio document
PDF.PdfCtrl.5 Adobe Acrobat PDF Reader 5.0
MediaPlayer.MediaPlayer Windows Media Player
Agent.Control Microsoft Agent control
DHTMLEdit.DHTMLEdit DHTML Edit control for IE5
InternetShortcut Internet shortcut

If you don’t find the application you’re looking for, open the standard Microsoft Registry Editor that comes with Windows and look under the key My ComputerHKEY_CLASSES_ROOT. It contains a list of IDs for all the applications installed on your machine. Figure B.2 shows the program ID for the Web Browser control (the reusable part of Internet Explorer). Unless you have a specific reason not to do so, it’s good practice to leave the terminating version number out of the name. COM uses the CurVer key to find out which version is current and uses it automatically.

Figure B.2. The program ID for the reusable Web Browser control

Microsoft created this mechanism to allow the transparent migration of applications. However, some vendors don’t follow this guideline; in this case you’ll have to keep the version number as an integral part of the name (see Adobe Acrobat in the table B.5).

B.2.2. The Java COM library

The org.eclipse.swt.internal.ole.win32 and org.eclipse.swt.ole.win32 packages sit directly above the native library. The second package contains all the high-level code necessary to write COM client code with SWT. As shown in figure B.1, three classes consist of implementation details that aren’t exposed outside of the package boundaries. OleEventTable() is a lookup mechanism that maps an event type to a specific listener, and OleEventSink() is the heart of SWT’s ability to receive and dispatch COM events to your code; it contains a partial IDispatch implementation that can be a source of ideas for how to implement one yourself. Note that the OLE class contains mostly constants and a utility method to convert COM errors into SWT exceptions.

The org.eclipse.swt.internal.ole.win32.IUnknown class

By now you’re familiar with the role played by the IUnknown COM interface in the discovery process of COM features. Even though it’s described as a COM interface, the SWT team chose to implement it as a Java class. Its constructor takes one parameter: int address. Its value is the address of the vtable containing the implementation of the interface. All the methods of the COM IUnknown interface are implemented as normal Java methods with parameters similar to those of the native COM counterpart. These methods are implemented by calling COM.Vtbl-Call() with all the parameters and the index of the native method in the vtable. The following snippets show the implementation of QueryInterface() and AddRef(), which are the first two physical methods in the IUnknown COM interface:

public int QueryInterface(GUID riid, int ppvObject[]) {
  return COM.VtblCall(0, address, riid, ppvObject);
}
public int AddRef() {
  return COM.VtblCall(1, address);
}

Figure B.3 is a class diagram showing all the COM interfaces declared in the SWT COM library. In section B.3.3, we explain how to add new interface declarations when these aren’t enough.

Figure B.3. COM interfaces derived from IUnknown

The org.eclipse.swt.ole.win32.OleFrame class

To embed a COM object (ActiveX control or OLE document) in your SWT application, you need to create a container window. As you’ve seen, COM mandates that this container implement certain interfaces in order for the embedded object to dialog with it. SWT provides the OleFrame class for that purpose. Although the class is derived from Composite, it makes no sense to assign it a layout or try to add Control children. The only reason for inheriting from Composite is to access the window-management capabilities offered by Composite, in order to implement the IOleInPlaceFrame COM interfaces.

Internally, OleFrame handles sizing, menu management, and window placement. Looking at figure B.3, you’ll notice that IOleInPlaceFrame isn’t on the diagram. SWT doesn’t require that you declare a COM interface (create a class derived from IUnknown that lists all its methods) in order to implement it. All that is required is that you create an instance of COMObject with public methods mapping the methods exposed by the COM interface.

When activated, OLE documents negotiate with the container to display their menu bar. Using public methods from OleFrame, your application can contribute menu items to the final menu bar. When adding menu items, you can choose between three locations on the menu bar—File (on the far left), Container (in the middle), or Window (far right, before Help)—by calling SetFileMenus(), SetContainerMenus(), and SetWindowMenus(), respectively. All three methods take an array of SWT MenuItem. Just before displaying the menu bar, the embedded object calls IOleInPlaceFrame.InsertMenus(); at that point OleFrame merges your items with those from the embedded object.

The org.eclipse.swt.ole.win32.OleClientSite class

The OleClientSite class implements a complete COM site. Aside from implementing the mandatory IUnknown, IOleClientSite, and IAdviseSink, the class also implements in-place activation via the optional IOleInPlaceSite and IOleDocumentSite.

This class contains several useful public methods. doVerb() plays an important role in the activation process, as you’ll see later. SaveFile() can be used to save the current OLE document to a normal file (includeOleInfo = false) or to a storage file (includeOleInfo = true). showProperties() lets you display all the properties of the embedded COM object in a separate window, provided the object implements the ISpecifyPropertyPages interface. The only control you have over the window is its title, because it’s created by the COM runtime via a call to the standard COM.OleCreatePropertyFrame().

queryStatus() is a helper method that accepts the ID of a command and returns a bitwise OR’d combination of OLECMDF_SUPPORTED, OLECMDF_ENABLED, and OLECMDF_LATCHED indicating the status of the specified command. This method is usually called before a call to exec() to verify that the command is available before executing it. Both functionalities are based on querying the embedded object for its IOleCommandTarget interface and subsequently calling the QueryStatus() or Exec() method on this interface.

You may be faced with situations where the site doesn’t implement some interfaces that are necessary to provide more control over the embedded COM object. Fortunately, the SWT team structured the code in a way that makes it easy to extend. You need to follow three rules:

1.  The constructor of your derived class must call the parent constructor before anything else.

2.  You need to override the protected createCOMInterfaces (where you create one COMObject instance for each COM interface you want your site to support) and disposeCOMInterfaces (where you call the dispose() method for each of the COMObject instances previously created).

3.  You must ensure that your new QueryInterface() method properly delegates to its parent (we present a complete example in section B.3.3).

The org.eclipse.swt.ole.win32.OleControlSite class

The OleControlSite class inherits from OleClientSite and provides the extended capabilities necessary to host an ActiveX control. The visible differences from the parent class are full support for events and property change notifications from the ActiveX control, simplified access to well-known control properties (straight method calls instead of COM automation calls), and direct access to ambient properties.

You can access the container’s ambient properties via calls to the setSiteProperty() and getSiteProperty() methods. For example, the Web Browser control has properties that determine whether the browser should support embedded ActiveX controls or JavaScript scripts. The font as well as color of the ActiveX control are accessible via setFont(), getFont(), setBackground(), getBackground(), setForeground(), and getForeground().

addEventListener() allows you to register instances of OleListener in order to receive specific events. You can register a single listener instance for several event types, in which case your handler will probably contain an if statement based on the value of OleEvent.type. Alternatively, you can register different listeners for the various events your application needs to handle. When you’re no longer interested in receiving an event, don’t forget to call the removeEventListener() method. Similar methods exist for receiving notifications about changes in the value of a property.

The org.eclipse.swt.ole.win32.OleAutomation class

Dealing with dispatch interfaces (COM interfaces that extend IDispatch) can lead to code that’s hard to read, involving many Variant instances copied to and from arrays. OleAutomation provides a wrapper to manipulate the functionality delivered by the IDispatch COM interface. Two constructors are available. The first takes an instance of OleClientSite (remember that OleControlSite extends OleClientSite, which guarantees that the class works for both ActiveX controls and OLE documents) and uses package-level methods to obtain the client’s private instance of the org.eclipse.swt.internal.ole.win32.IDispatch class. The second constructor directly takes an instance of IDispatch as its unique parameter. Both constructors acquire an ITypeInfo reference via a call to QueryInterface() on the object.

Internally, this reference is used in the implementation of all the public methods involved in describing the dispatch interface. This gives you access to the complete type library for the interface. The first method, getTypeInfoAttributes(), returns a structure of type TYPEATTR that contains a high-level description of the interface. The description includes such information as the interface GUID, the number of variables it exposes, and its total number of methods. The following code snippet shows how to access all the variables of an automation interface:

  • Based on a program ID similar to those listed in table B.5, we create a control site and then use it to create an OleAutomation wrapper.
  • The cVars field contains the number of variables exposed by the interface. Successive calls to getPropertyDescription() return OlePropertyDescription instances. The class contains the description for an Automation property. Its type field contains an OR’ed combination of the different variant types described in OLE.java (OLE.VT_XXXX). Similarly, we could enumerate all the public methods of an interface by calling the getFunctionDescription() method with successive integer values, provided this index remains inferior to typeattr.cFuncs.

Besides providing a description of the interface, OleAutomation also contains methods to simplify accessing the properties of the COM object (setProperty() and getProperty()). As you may recall from the general discussion on COM and automation, properties are exposed via public getters and setters rather than data. Consequently, setProperty() and getProperty() are convenience wrappers around the more general invoke() method. All automation calls into the object ultimately translate into a call to IDispatch.Invoke() using one of the four standard dispatch codes (DISPATCH_XXXX constants from COM) as the fourth parameter. Finally, getIDsOfNames() is a thin wrapper that simplifies calling GetIDsOfNames() from the underlying IDispatch interface.

B.3. Doing COM with SWT

You should now have an idea about how to embed a COM object in an SWT-based application. In this section, we’ll put this understanding to the test and write some examples, as well as explore patterns that will help you write more complex code.

B.3.1. A simple example

Before we dive further into the full life cycle of a COM object embedded inside an SWT container, we’ll write the simplest possible example. Despite its simplicity, listing B.1 shows several important aspects of SWT COM programming. Its purpose is to embed an instance of Internet Explorer inside a SWT frame and display the home page of the Manning Publications web site.

Listing B.1. SimpleBrowser.java
package com.swtjface.AppB;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.ole.win32.*;
public class SimpleBrowser {
  private Shell shell;
  private OleAutomation automation;
  public Shell open(Display display) {
    this.shell = new Shell(display);
    shell.setLayout(new FillLayout());
    OleFrame frame = new OleFrame(shell, SWT.NONE);
    OleControlSite controlSite =
        new OleControlSite(frame, SWT.NONE, "Shell.Explorer");
    automation = new OleAutomation(controlSite);
    boolean activated =
        (controlSite.doVerb(OLE.OLEIVERB_INPLACEACTIVATE) == OLE.S_OK);
    this.openURL("html://www.manning.com/");
    shell.open();
    return shell;
  }
  public void openURL(String url) {
    int[] rgdispid =
         automation.getIDsOfNames(new String[]{"Navigate", "URL"});
    int dispIdMember = rgdispid[0];
    Variant[] rgvarg = new Variant[1];
    rgvarg[0] = new Variant(url);
    int[] rgdispidNamedArgs = new int[1];
    rgdispidNamedArgs[0] = rgdispid[1];
    Variant pVarResult =
        automation.invoke(dispIdMember, rgvarg, rgdispidNamedArgs);
  }
  public static void main(String[] args) {
    Display display = new Display();
    Shell shell = (new SimpleBrowser()).open(display);
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch()) {
        display.sleep();
      }
    }
    display.dispose();
  }
}
Creating a COM object

The first step creates the container where the COM object (ActiveX control or OLE document) will be embedded. For that we instantiate an OleFrame, as follows:

Display display = new Display();
Shell shell = new Shell(display);
OleFrame frame = new OleFrame(shell, SWT.NONE);

SWT provides two site implementations, one for OLE documents (OleClientSite) and one for ActiveX controls (OleControlSite). When in doubt about which to use for a COM object you want to embed, look at its type library to see if it implements either the IOleDocument or IOleControl interface. To do so, you can use Microsoft’s OLE View, which is distributed with all major development tools and presented by msdn.microsoft.com (see figure B.4).

Figure B.4. The type library for Internet Explorer displayed in Microsoft OLE View

The constructor for OleClientSite lets you create a site based on a program ID (see table B.5 for a list of program IDs for known applications) or a filename. In the latter case, the shell does all the work of matching it to the corresponding program ID based on the file extension. Behind the scenes, OleClientSite transparently creates an instance of IStorage that it then passes to the object:

OleClientSite clientSite =
      new OleClientSite(frame, SWT.NONE, "Word.Document");
File file = new File ("C:\file1.doc");
OleClientSite clientSite =
      new OleClientSite(frame, SWT.NONE, file);

Embedding an ActiveX control is much simpler and only involves creating an instance of OleControlSite by passing its constructor the control’s program ID:

OleControlSite controlSite =
      new OleControlSite(frame, SWT.NONE, "Shell.Explorer");
Activating an object

Creating an object isn’t enough to make it become visible inside its container. It needs to be activated by the container. Activation is controlled by the doVerb() method. Table B.6 lists all the possible values for the method’s unique parameter. In most cases, you’ll us OLE.OLEIVERB_INPLACEACTIVATE to activate an ActiveX control in place. OLE documents work differently; the COM runtime must first start the associated application and ask it on behalf of your container to open the document in the site you supply.

Table B.6. Standard verbs for manipulating OLE objects

ProgID

Description

OLE.OLEIVERB_DISCARDUNDOSTATE Closes the OLE object and discards the undo state
OLE.OLEIVERB_HIDE Hides the OLE object
OLE.OLEIVERB_INPLACEACTIVATE Opens the OLE object for editing in place
OLE.OLEIVERB_OPEN Opens the OLE object for editing in a separate window
OLE.OLEIVERB_PRIMARY Opens the OLE object for editing (the action that occurs when a user double-clicks on the object inside the container)
OLE.OLEIVERB_PROPERTIES Requests the OLE object properties dialog
OLE.OLEIVERB_SHOW Shows the OLE object
OLE.OLEIVERB_UIACTIVATE Activates the UI for the OLE object

People have reported trouble activating certain documents, particularly Microsoft Office documents. In these cases, better results have been reported using OLE.OLEIVERB_SHOW as the activation verb.

Also remember that OLE documents are complete applications hosted inside your container. It’s a common mistake to activate an OLE document in a container that doesn’t have a menu bar—some applications will forgive you, others won’t. Remember: always create your application’s menu bar before activating an OLE document, as in the following snippet.

Saving changes to an OLE document

At this point, we’ve successfully activated an OLE document inside an SWT container, and the user would like to save her changes. The first step is to call isDirty() from the OleClientSite to test that the file needs saving. When it is the case, we can proceed to call the save() method, passing it a standard java.io.File instance and a boolean indicating whether to save the file as a standard file (includeOleInfo = false) or a storage file (includeOleInfo = true). It’s a good practice to save the document to a temporary file and replace the original file only if the operation is successful. The following snippet illustrates the process:

if (clientSite.isDirty()) {
  File tempFile = new File(file.getAbsolutePath() + ".tmp");
  file.renameTo(tempFile);
  if (clientSite.save(file, true)){
    // save was successful so discard the backup
    tempFile.delete();
  } else {
    // save failed so restore the backup
    tempFile.renameTo(file);
  }
}
Disposing of an object

The examples we’ve looked at so far have been simple in the sense that the container was only dealing with a single embedded object. However, nothing prevents your container from embedding several objects. OLE documents negotiate with their container about the content of the main menu toolbar. Consequently, your container must implement a strict control over the activation process. You should activate each COM object individually when doing so makes the most sense—for example, upon receiving a mouse double-click event—and deactivate them before activating the next one. To deactivate an object, call the deactivateInPlaceClient() method from its OleClientSite.

When you’re done working with an embedded COM object, you must remember to perform one last task. The class diagram in figure B.1 showed that OleFrame, OleClientSite, and OleControlSite are all subclasses of org.eclipse.swt.widgets.Composite. As such, it’s imperative that you dispose of them by calling dispose(). Unless you’ve saved all changes made to an OLE document prior to calling dispose(), they will be lost.

Handling properties

To access automation properties, you must first get their dispatch identifier (dispid) by calling getIDsOfNames() on the OleAutomation wrapper. You can use the resulting int in subsequent calls to getProperty() or setProperty(). Also keep in mind that all property values have the Variant type, which is the standard automation data type. A common source of mistakes is improperly initializing a variant by selecting the wrong type attributes, especially when dealing with variants that are references rather than contain data. The following snippet reads the Document property from an automation server:

OleAutomation server = new OleAutomation(...);
int[]documentID = server.getIDsOfNames(new String[]{"Document"});
if (documentID == null) return null;
Variant pVarResult = server.getProperty(documentID[0]);
Interacting with an object

One of the compelling reasons for embedding COM objects (ActiveX controls or OLE documents) in your application is the ability to inherit portions of the object’s native user interface inside your own. However, this is only a small part of the possible interactions.

Using the invoke() method from OleAutomation gives you access to the entire object model of a COM component. Using it is a simple extension of what we did to access a property. A call to getIDsOfNames() with the name of a command returns its dispid, and once again all values have the Variant type. (Note that dealing with Microsoft Word presents some unique problems: When you’re invoking a command that doesn’t return a value, it’s preferable to use the invokeNoReply() method.)

SWT also gives you access to the event model of an embedded COM object by registering instances of OleListener with an OleControlSite. You’ll have to dig inside each type library to find the specific int value for the event you wish to subscribe to. As we’ve discussed, you can either register the same listener for multiple events or use distinct listeners for each event.

All handlers implement one unique method: void handleEvent(OleEvent event). By reading the type library, you’ll find the number and type of parameters associated with the event. SWT copies them into an array that’s publicly accessible from the event as public Variant[] arguments. The parameters are ordered from left to right in the prototype from the type library. For example, the following information is from the Excel 10.0 type library. In this scenario, SeriesIndex would be arguments[0] and PointIndex would be stored in arguments[1]:

dispinterface ChartEvents {
    ...
    [id(0x00000602), helpcontext(0x00010602)]
        void SeriesChange(
                        [in] long SeriesIndex,
                        [in] long PointIndex);
    ...
}

B.3.2. SWT COM programming patterns

What seems at first like a disconcerting way of writing Java code has identifiable patterns that are repeated throughout. Understanding them is the key to writing more serious COM-related code with SWT. Let’s review some of these patterns.

Null-terminated strings

In some instances, you’ll be forced to deal with the fact that C/C++ strings are terminated by the null-end-of-string character (). This code snippet shows how to create a null-terminated string from a Java String:

String lpsz = "This is a string";
char[] buffer = (lpsz +"").toCharArray();

The next snippet shows a way to turn a null-terminated string back into a Java String. We hope that future versions of SWT will include a way to find the length of a native string directly:

Pointer to structure

A common situation in SWT Windows/COM programming has to do with handling pointers. Typically you’ll deal with a pointer to a native Windows structure, a pointer to a string, or a pointer to a native type. Many of the Windows APIs manipulate 32-bit quantities that are pointers to complex data structures. The creators of SWT mapped these structures to Java classes with public data members and no methods. The many versions of MoveMemory() contain the necessary JNI code to initialize the public data members of all the supported structures one field at a time. As we previously discussed, each structure includes the following line of code, where XXX is the size of the structure in bytes:

public static final int sizeof = XXX;

The pointers are mapped to Java int. In the following snippet, we use an int as a pointer to a GUID and copy the data into an already allocated GUID object:

GUID guid = new GUID();
COM.MoveMemory(guid, riid, GUID.sizeof);

A typical misconception could lead you to declare only the guid variable and not allocate the memory for the structure, which would cause a memory violation error.

Pointer to pointer to an interface

Another recognizable pattern in COM programming with SWT has to do with the notion of pointers to pointers. Many COM methods take one or more parameters that are pointers to interface pointers. In the following code snippet, we’re looking at this coding pattern in the context of obtaining a temporary instance of IStorage (similar code is invoked every time you create a new automation document with SWT):

int[] tempStg = new int[1];    1 Allocate int array
int grfMode = COM.STGM_READWRITE |
              COM.STGM_SHARE_EXCLUSIVE |
              COM.STGM_DELETEONRELEASE;
int result = CO .StgCreateDocfile(null, grfMode, 0, tmpStg);  2 Method call

if (result != COM.S_OK)    3 Status check
   OLE.error(OLE.ERROR_CANNOT_CREATE_FILE, result);
IStorage st = new IStorage(tmpStg [0])    4 Create reference

  • An int array with a size of 1 is created. This is a pointer (the reference to the array) to a pointer (the first value of the array).
  • The array is passed to a method that expects a pointer to a pointer to a COM interface.
  • All COM methods return an HRESULT: a 4-byte value where the sign bit is used to communicate the presence/absence of an error. Errors are reported using the static method error() defined in the org.eclipse.swt.internal.win32.OS class. The method builds an error message based on the error code and throws an SWTException. Note that SWTException extends RuntimeException; consequently, the exception isn’t declared in any method signature, and handling it is left to your discretion.
  • If there are no errors, the method initializes the first value of the array with the address of the reference. All that’s left is to instantiate the proper IUnknown derived class, which we do by passing the address to the constructor.

B.3.3. Advanced topics

Now that we’ve discussed the standard methods for incorporating COM inside SWT code, it’s time to go further. This section will explain how to customize applications with your own OLE/COM interfaces and QueryInterfaces. Then, we’ll investigate more advanced data structures such as IDispatch parameters and SafeArrays.

Adding new interfaces to the org.eclipse.swt.internal.ole.win32 package

Despite the long list of interfaces supplied in the org.eclipse.swt.internal.ole.win32 package, you may need an interface that isn’t defined. Listing B.2 shows how to create a Java class that describes a COM interface. In the simpler cases, your interface will inherit directly from IUnknown and add one or more methods; our next example will show a slightly more complicated scenario.

Listing B.2. IPersistFile.java
package org.eclipse.swt.internal.ole.win32;
public class IPersistFile extends IPersist {
  public IPersistFile(int address) {
    super(address);
  }
  public int IsDirty() {
    return COM.VtblCall(4, address);
  }
  public int Load(String szFileName, int dwMode) {
    char[] fileName = (szFileName + "").toCharArray();
    int pszFileName = 0;
    COM.MoveMemory(pszFileName, fileName, 4);
    return COM.VtblCall(5, address, pszFileName, dwMode);
  }
  public int Save(String szFileName, boolean fRemember) {
    char[] fileName = (szFileName + "").toCharArray();
    int pszFileName = 0;
    COM.MoveMemory(pszFileName, fileName, 4);
    return COM.VtblCall(6, address, pszFileName, fRemember);
  }
  public int SaveCompleted(int pszFileName) {
    return COM.VtblCall(7, address, pszFileName);
  }
  public int GetCurFile() {
    return COM.VtblCall(8, address);
  }
}

According to the Microsoft documentation, IPersistFile extends IPersist, which inherits from IUnknown. Before starting the implementation, we must count the number of methods already implemented by the complete chain of parent interfaces. The index of the first new method is the total plus one: in this case, 4. IUnknown contains three methods (index 0 to 2), and IPersist contains a single method (index 3). For the sake of simplifying the code, it directly accesses the address field from the IUnknown class. Unfortunately at this point address has package-only visibility, which is the reason for the package declaration on the first line of the listing. Alternatively, we could have replaced every reference to address with a call to the public method getAddress(). Finally, notice that the body of the Save() and Load() methods uses the null-terminated strings pattern we just described.

Implementing a known COM interface

Let’s walk through the steps of the complete implementation of a standard COM interface. We’ll implement IDocHostUIHandler; the reason for this choice will become evident at the end of this appendix.

COMObject contains a long list of empty methods. This is our first stop: We need to create an instance that maps to the methods of IDocHostUIHandler. The following snippet shows how to do this. Each of the interface’s methods is mapped to a locally defined method of the Java class taking the same number of parameters as its COM counterpart:

  • The Microsoft specification shows that IDocHostUIHandler contains 18 methods. The array contains the number of input parameters for each of these 18 methods.
  • Method0() is mapped to a privately defined QueryInterface method that takes two parameters. The rest of the code maps the other methods to other private methods while preserving the same ordering as in the COM interface definition.
Rolling your own QueryInterface

Of all the methods that you’ll implement when designing custom interface implementations, QueryInterface() presents the most difficulties. The following code demonstrates several useful patterns for implementing your own:

  • In general, it’s a good idea to make all your COM interface implementation methods protected or with limited visibility, because these methods exist only so you can call them in the context of your COM object. QueryInterface takes only two parameters. In C++, riid is a reference to an interface ID that translates into a Java int.
  • This implementation of QueryInterface was taken from a class extending OleClientSite, for the purpose of creating an extended client site. This new site implements one more interface than the default OleClientSite. First, the code attempts to delegate to the parent QueryInterface. If the parent returns S_OK, then the interface was found and its address is already inside ppvObject. All that’s left is to return the status code.
  • Next we must check that the GUID of the requested interface matches an interface supported by this object. In this case, the new client site adds support for COM.IIDIDocHostUIHandler. If the two GUIDs are identical, then we need to copy the address of the private instance of the class that implements IDocHostUIHandler to the result pointer.
Passing IDispatch references as parameters

A common situation may require that you pass a reference to an IDispatch as a parameter to a method exposed by the COM object you host inside your application. The following is the Interface Definition Language (IDL) description for a method from the Microsoft Excel object model. The _Chart interface contains the SetSourceData() method, which takes a Range pointer as its first parameter. Looking at the object model also shows that the Range interface is derived from IDispatch:

[id(0x00000585), helpcontext(0x00010585)]
void SetSourceData(
                 [in] Range* Source,
                 [in, optional] VARIANT PlotBy);

The proper way to invoke SetSourceData() consists of passing the range reference as a byRef Variant, as follows:

  • As usual, we must get the method’s dispid from its name.
  • We create a pointer to the Range reference by allocating 4 bytes of memory and copying the address of Range to it.
  • Using our new pointer to the Range, we can create a new Variant using the OLE.VT_BYREF | OLE.VT_DISPATCH style. VT_BYREF indicates that the variant contains a pointer to something, and VT_DISPATCH indicates that the something is an IDispatch reference.
Anatomy of a storage file

The example in listing B.3 shows how to explore the content of a storage file. The code also shows how to use a class derived from IEnum to perform an enumeration.

Listing B.3. Storage.java

Looking inside a SafeArray

When Microsoft needed to add support for arrays to automation, it created the notion of SafeArray: a structure that describes the characteristics of an array and holds its data. Dealing with SafeArrays was deemed complicated enough that Microsoft created a specific API for that purpose. For memory, the anatomy of a SafeArray is as follows (the offset and size information will help you understand the code that follows):

typedef struct tagSAFEARRAYBOUND {
    ULONG cElements;                 // offset 0,  size 4
    LONG lLbound;                    // offset 4,  size 4
} SAFEARRAYBOUND;

typedef struct tagSAFEARRAY {
    USHORT cDims;                    // offset 0,  size 2
    USHORT fFeatures;                // offset 2,  size 2
    ULONG cbElements;                // offset 4,  size 4
    ULONG cLocks;                    // offset 8,  size 4
    PVOID pvData;                    // offset 12, size 4
    SAFEARRAYBOUND rgsabound[ ... ]; // offset 16
} SAFEARRAY;

Unfortunately there is currently no special support for SafeArray in SWT. However, based on the C definition of the SafeArray structure, it’s possible to display their content. The following is an extract from the type library for the Web Browser ActiveX control:

interface DWebBrowserEvents : IUnknown {
    ...
    [helpstring("Fired when a new hyperlink is being navigated to.")]
    HRESULT _stdcall BeforeNavigate(
                [in] BSTR URL, long Flags, BSTR TargetFrameName,
                VARIANT* PostData,
                BSTR Headers, [in, out] VARIANT_BOOL* Cancel);
    ...
}

The Microsoft web site states that PostData (parameter number 4) is a Variant that contains a reference to a Variant that is itself a SafeArray. The following code uses some of the simpler patterns we reviewed to parse the content of an array:

B.3.4. A final example

Let’s work on something more elaborate that brings together everything we’ve covered so far. The enhanced version of our SimpleBrowser from listing B.1, shown in listing B.4, is now capable of displaying a personalized context menu as well as saving the content of the page to a file.

Listing B.4. BetterBrowser.java
package com.swtjface.AppB;
import org.eclipse.swt.SWT;
import org.eclipse.swt.internal.ole.win32.*;
import org.eclipse.swt.internal.win32.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.ole.win32.*;
import org.eclipse.swt.widgets.*;

public class BetterBrowser {
  private Shell shell;
  private OleAutomation automation;
  static final int DocumentComplete = 104;
  static final int NavigateComplete = 101;
  private int globalDispatch;

  public Shell open(Display display) {
    this.shell = new Shell(display);
    shell.setLayout(new FillLayout());
    OleFrame frame = new OleFrame(shell, SWT.NONE);
    OleControlSite controlSite = new ExtWebBrowser(frame, SWT.NONE,
                                                   "Shell.Explorer");
    automation = new OleAutomation(controlSite);
    boolean activated =
         (controlSite.doVerb(OLE.OLEIVERB_INPLACEACTIVATE) == OLE.S_OK);
    OleListener listener = new OleListener() {
      public void handleEvent(OleEvent event) {
        switch (event.type) {
          case DocumentComplete : {
                  Variant varResult = event.arguments[0];
                  IDispatch dispatch = varResult.getDispatch();
                  Variant variant = new Variant(automation);
                  IDispatch top = variant.getDispatch();
                  varResult = event.arguments[1];
                  String url = varResult.getString();
            if (globalDispatch != 0 && dispatch.getAddress()
              == globalDispatch) {
              /* final document complete */
              globalDispatch = 0;
            }
            System.out.println("DocComplete");
            break;
          }
          case NavigateComplete : {
            Variant varResult = event.arguments[0];
            IDispatch dispatch = varResult.getDispatch();
            if (globalDispatch == 0)
              globalDispatch = dispatch.getAddress();
            System.out.println("NavComplete");
            break;
          }
        }
        Variant[] arguments = event.arguments;
        for (int i = 0; i < arguments.length; i++)
          arguments[i].dispose();
      }
    };
    controlSite.addEventListener(DocumentComplete, listener);
    controlSite.addEventListener(NavigateComplete, listener);

    this.openURL("http://www.manning.com");
    shell.open();
    return shell;
  }
  public void openURL(String url) {
    int[] rgdispid = automation.getIDsOfNames
      (new String[]{"Navigate", "URL"});
    int dispIdMember = rgdispid[0];
    Variant[] rgvarg = new Variant[1];
    rgvarg[0] = new Variant(url);
    int[] rgdispidNamedArgs = new int[1];
    rgdispidNamedArgs[0] = rgdispid[1];
    Variant pVarResult = automation.invoke(dispIdMember, rgvarg,
        rgdispidNamedArgs);
  }
public static void main(String[] args) {
  Display display = new Display();
  Shell shell = (new BetterBrowser()).open(display);
  while (!shell.isDisposed()) {
    if (!display.readAndDispatch()) {
      display.sleep();
    }
  }
  display.dispose();
}

public OleAutomation getHtmlDocument(OleAutomation browser) {
    int[] htmlDocumentID =
      browser.getIDsOfNames(new String[]{"Document"});
    if (htmlDocumentID == null) return null;
    Variant pVarResult = browser.getProperty(htmlDocumentID[0]);
    if (pVarResult == null || pVarResult.getType() == 0) return null;
    //IHTMLDocument2
    OleAutomation htmlDocument = pVarResult.getAutomation();
    return htmlDocument;
}

class ExtWebBrowser extends OleControlSite {

    COMObject iDocHostUIHandler;

  public ExtWebBrowser(Composite parent, int style, String progId) {
        super(parent, style, progId);
  }

  protected void createCOMInterfaces () {

    super.createCOMInterfaces();
      iDocHostUIHandler =
        new COMObject(new int[]{2, 0, 0, 4, 1, 5, 0, 0, 1,
                                1, 1, 3, 3, 2, 2, 1, 3, 2}){
            public int method0(int[] args)
              {return QueryInterface(args[0], args[1]);}
            public int method1(int[] args) {return AddRef();}
            public int method2(int[] args) {return Release();}
            public int method3(int[] args)
              {return ShowContextMenu(args[0], args[1], args[2],
                  args[3]);}
            public int method4(int[] args)
              {return GetHostInfo(args[0]);}
            public int method5(int[] args)
              {return ShowUI(args[0], args[1], args[2], args[3],
                  args[4]);}
            public int method6(int[] args) {return HideUI();}
            public int method7(int[] args) {return UpdateUI();}
            public int method8(int[] args)
              {return EnableModeless(args[0]);}
            public int method9(int[] args)
              {return OnDocWindowActivate(args[0]);}
            public int method10(int[] args)
              {return OnFrameWindowActivate(args[0]);}
            public int method11(int[] args)
              {return ResizeBorder(args[0], args[1], args[2]);}
            public int method12(int[] args)
              {return TranslateAccelerator(args[0], args[1], args[2]);}
            public int method13(int[] args)
              {return GetOptionKeyPath(args[0], args[1]);}
            public int method14(int[] args)
              {return GetDropTarget(args[0], args[1]);}
            public int method15(int[] args)
              {return GetExternal(args[0]);}
            public int method16(int[] args)
              {return TranslateUrl(args[0], args[1], args[2]);}
            public int method17(int[] args)
              {return FilterDataObject(args[0], args[1]);}
       };
  }
  protected void disposeCOMInterfaces() {
    super.disposeCOMInterfaces();
    if (iDocHostUIHandler != null)
      iDocHostUIHandler.dispose();
    iDocHostUIHandler = null;
  }
  protected int QueryInterface(int riid, int ppvObject) {
    int result = super.QueryInterface(riid, ppvObject);
    if (result == COM.S_OK)
      return result;
    if (riid == 0 || ppvObject == 0)
      return COM.E_INVALIDARG;
    GUID guid = new GUID();
    COM.MoveMemory(guid, riid, GUID.sizeof);
    if (COM.IsEqualGUID(guid, COM.IIDIDocHostUIHandler)) {
      COM.MoveMemory(ppvObject, new int[]
       {iDocHostUIHandler.getAddress()}, 4);
      AddRef();
      return COM.S_OK;
    }
    COM.MoveMemory(ppvObject, new int[]{0}, 4);
    return COM.E_NOINTERFACE;
  }

  //~- IDocHostUIHandler -----------------------------
  int EnableModeless(int EnableModeless) {
    return COM.E_NOTIMPL;
  }
  int FilterDataObject(int pDO, int ppDORet) {
    return COM.E_NOTIMPL;
  }
  int GetDropTarget(int pDropTarget, int ppDropTarget) {
    return COM.E_NOTIMPL;
  }
  int GetExternal(int ppDispatch) {
    return COM.E_NOTIMPL;
  }
  int GetHostInfo(int pInfo) {
    return COM.E_NOTIMPL;
  }
  int GetOptionKeyPath(int pchKey, int dw) {
    return COM.E_NOTIMPL;
  }
  int HideUI() {
    return COM.E_NOTIMPL;
  }
  int OnDocWindowActivate(int fActivate) {
    return COM.E_NOTIMPL;
  }
  int OnFrameWindowActivate(int fActivate) {
    return COM.E_NOTIMPL;
  }
  int ResizeBorder(int prcBorder, int pUIWindow, int fFrameWindow) {
    return COM.E_NOTIMPL;
  }
  int ShowContextMenu(int dwID, int ppt,
    int pcmdtReserved, int pdispReserved) {
    int [] pts = new int[2];
    OS.MoveMemory(pts, ppt, 8);

    System.out.println(dwID);
      Menu menu = new Menu (shell, SWT.POP_UP);
      MenuItem item = new MenuItem (menu, SWT.PUSH);
      item.setText ("Save Source");
      item.addListener (SWT.Selection, new Listener () {
        public void handleEvent (Event e) {
            System.out.println ("Save Selected");
            if (globalDispatch != 0) {
              IUnknown iuk = new IUnknown(globalDispatch);
          int[] ppvObject = new int[1];
          if (iuk.QueryInterface(COM.IIDIPersistFile, ppvObject)
            == COM.S_OK) {
            IPersistFile pf = new IPersistFile(ppvObject[0]);
            pf.Save("c:\test.html", false);
            pf.Release();
          }
            }
      }
      });
      item = new MenuItem (menu, SWT.PUSH);
      item.setText ("View Source");
      item.addListener (SWT.Selection, new Listener () {
        public void handleEvent (Event e) {
              System.out.println ("View Selected");
        }
        });
        menu.setLocation (pts[0], pts[1]);
        menu.setVisible (true);
        Display display = getShell().getDisplay();
        while (!menu.isDisposed () && menu.isVisible ()) {
          if (!display.readAndDispatch ()) display.sleep ();
        }
        menu.dispose ();
      return COM.S_OK;
    }
    int ShowUI(int dwID, int pActiveObject, int pCommandTarget,
      int pFrame,int pDoc) {
      return COM.E_NOTIMPL;
    }
    int TranslateAccelerator(int lpMsg, int pguidCmdGroup,
      int nCmdID) {
      Menu menubar = getShell().getMenuBar();
      if (menubar != null && !menubar.isDisposed()
        && menubar.isEnabled()) {
        Shell shell = menubar.getShell();
        int hwnd = shell.handle;
        int hAccel = OS.SendMessage(hwnd, OS.WM_APP + 1, 0, 0);
        if (hAccel != 0) {
          MSG msg = new MSG();
          OS.MoveMemory(msg, lpMsg, MSG.sizeof);
          if (OS.TranslateAccelerator(hwnd, hAccel, msg) != 0)
            return COM.S_OK;
        }
      }
      return COM.S_FALSE;
    }
    int TranslateUrl(int dwTranslate, int pchURLIn, int ppchURLOut) {
      return COM.E_NOTIMPL;
    }
    int UpdateUI() {
      return COM.E_NOTIMPL;
    }
  }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.176.228