Chad Verbowski
Software Design Engineer
Microsoft Corporation
January 1999
Introduction
Overview of COM
Java-Callable Wrappers
Apartment Threading
Creating an Instance of a COM Object
Invoking COM Interface Methods
Threading of COM Object Method Parameters
Garbage Collection of COM Objects
Generating JCWs for COM Objects
COM Coclasses in Java
COM Interfaces in Java
VTable, Dispatch, and Dual Interfaces
COM Interface Methods in Java
COM Method Parameters in Java
Java-Callable Data Wrappers as Parameters
Using JCWs in Java Applications
Using JCWs with AWT Controls
Distributed COM
Sources for Additional Information
This tutorial describes how the Microsoft virtual machine (Microsoft VM) creates and exposes Component Object Model (COM) objects to the Java developer. The reader should have a general understanding of what COM is and some experience programming in Java. This tutorial also has samples that are based on COM objects exposed by the Microsoft® Internet Explorer 4.0 COM object model. They demonstrate how COM objects can be used in Java applications. Additional Java/COM information is available in the Microsoft SDK for Java.
COM is a popular object model for creating software applications from independent components. The binary interfaces exposed by COM objects are platform-independent. Therefore, various facets of applications can be written in any COM-compliant language. COM objects also promote encapsulation and object reuse. For example, earlier functionality can be contained in COM wrappers and reused in Microsoft® Windows® Distributed interNet Applications Architecture (Windows DNA) and other Web environments.
By using Java/COM wrappers, the Microsoft VM presents COM objects as regular Java objects. These Java objects contain extra information that the Microsoft VM uses to create and maintain the underlying COM object. The Microsoft VM automatically handles reference counting, garbage collection, marshaling, and threading for every Java/COM object. It does so by combining internal manipulation of COM interfaces with basic Java functionality.
COM defines a language-independent binary standard for component interoperability. Each COM component can potentially implement several standard or custom interfaces to expose its functionality. These interfaces are the binary standards through which clients and component objects communicate. COM interfaces define related groups of methods. Every COM interface and class has its own globally unique identifier (GUID). The GUID is a 128-bit identification number that uniquely represents the COM interface or class across platforms, computers, and applications. The GUID of a COM interface is an interface identifier (IID), and the GUID of a COM class is a class identifier (CLSID).
COM interfaces can be defined with the Interface Definition Language (IDL). These definitions can be converted into binary form by the Microsoft Interface Definition Language (MIDL) compiler.
Binary interface definitions permit runtime reuse of classes, and permit the actual implementation of COM component object classes (coclasses) and their interfaces to occur in the most appropriate languages. All COM interfaces must extend the root interface IUnknown, and every COM object must implement IUnknown. The IUnknown methods are as follows:
Microsoft has defined COM interfaces that provide common functionality. For example, IPersist and interfaces that inherit from IPersist contain methods that allow a client to ask an object to load its persistent data, thus initializing itself. COM objects can implement the IDispatch interface to make themselves accessible from high-level languages such as Microsoft® Visual Basic® and scripting languages such as JavaScript, Microsoft® Visual Basic® Scripting Edition (VBScript), and Active Server Pages.
COM objects are implemented by creating an object in a COM-compliant language, and implementing IUnknown along with other Microsoft- or custom-defined COM interfaces. The MSDN Library provides detailed information on the Microsoft-defined COM interfaces.
For more information about COM interfaces, see the COM Interface Reference .
For information about CoCreateInstanceEx( ), see the COM Functions Reference .
Java-Callable Wrappers (JCW) are simple Java objects with additional class file attributes. The Microsoft VM uses Microsoft-defined attributes in Java classes to expose COM objects to Java applications. These attributes are typically generated by the Microsoft Java compiler, jvc, from @com directives in Java source files. The Java representation of COM coclasses and interfaces looks exactly like other Java classes and interfaces, except for the addition of @com directive comments above the class, interface, and method prototypes.
A coclass might look like this:
/** * @com.class(classid=0002DF01-0000-0000-C000-000000000046, DynamicCasts) */ public class InternetExplorer implements IUnknown, … {
An interface definition could look like this:
/** * @com.interface(iid=0002DF05-0000-0000-C000-000000000046, thread=AUTO, type=DUAL) */ public interface IWebBrowserApp {
Interface methods may look like this:
/** * @com.method(vtoffset=48, dispid=407, type=PROPPUT, name="FullScreen", addFlagsVtable=4) * @com.parameters([in,type=BOOLEAN] pbFullScreen) */ public void setFullScreen(boolean pbFullScreen);
Because the @com directives appear as comments in Java source code, compilers that do not support them ignore the directives and compile the class as a regular Java class file. Similarly, virtual machines that do not support the COM attributes in class files ignore them.
The following example shows how the Microsoft VM exposes COM objects to Java runtime code as regular Java objects using a generic Java object (Figure 1) and an exposed COM object (Figure 2). Figure 1 shows how the Java instance is represented in memory, including its references to various components from the object's class file. A typical Java class is represented inside the Microsoft VM as follows:
package test; public class MyClass{ public int nField1 = 0; public String strField2 = 0; public char chField3 = ' '; }
Figure 1: Abstract representation of a generic Java object at runtime in the Microsoft VM
The Microsoft VM internally uses a Java-Callable Wrapper (JCW) to represent a COM object in Java. JCWs appear to Java developers as generic Java objects. JCWs are programmatically manipulated in exactly the same way as any regular Java object. The garbage collector handles any cleanup required if no Java references to the Java/COM object exist, and interfaces are automatically queried whenever a cast operation occurs. Figure 2 is a high-level diagram of the JCW coclass.
Figure 2: Abstract representation of a Java-Callable Wrapper in the Microsoft VM
Notice that the JCW representation is an extension of the regular Java object internal structure. (The shaded boxes indicate COM extensions to the structure.) The JCW contains all the information necessary for the Microsoft VM to manipulate the underlying COM object. The threading model is used with the thread context (apartment) for marshaling to the correct thread when the COM object is not callable from any thread (see the Apartment Threading section of this topic for more details). The class file attributes created from the @com directives in the Java source file provide the necessary information for the Microsoft VM to create the COM object, call vtable or dispatch methods, and marshal parameters between Java and COM. The cache of interfaces associated with each JCW optimizes interface queries by storing references to previously queried interfaces.
CreateInstance( )
Object creation is accomplished in COM through a standard creation method. COM-compliant languages typically expose a function to create a new instance of a COM class. Typically, this function takes a CLSID or human-readable programmatic identifier (ProgID) as a parameter and returns a reference to the new COM object's IUnknown interface. IUnknown::QueryInterface( ) obtains a pointer to the required interface implemented by the COM object. The Microsoft Guidgen tool generates new GUIDs by using the CoCreateGuid( ) function from the COM API. For more details about GUIDs and Guidgen, see the COM specification on http://www.microsoft.com/com .
The CreateInstance( ) method must obtain the class factory for the specified COM class before it can create a new instance and return it. CreateInstance( ) will internally use the function CoGetClassObject( ) for accomplishing this. CoGetClassObject( ) looks in the Windows registry under the HKEY_CLASSES_ROOT\CLSID key for a subkey with the same name as the GUID for the class of the COM object it is trying to create. This key will have subkeys that contain the name of the dynamic-link library (DLL) or application that implements the class factory for creating instances of that COM class. CoGetClassObject( ) returns this class factory to CreateInstance( ), which uses it to create a new instance of the object, and then returns it to the caller. The class factories returned by CoGetClassObject( ) are COM objects, which implement the IClassFactory interface or one of its derivatives.
After an application is finished using a COM object, it calls the Release( ) method from the COM object’s IUnknown interface. When all the clients using the COM object have released their references, the COM object removes itself from memory, and then unloads the associated library or application if it was the last object being used from that COM object provider.
COM objects can be used in the same manner as regular Java objects because the Microsoft VM exposes COM objects as Java objects. A COM object is created by applying the new operator to the JCW class representing the COM object’s class. The Microsoft VM checks if the class used to create the instance is a JCW. If so, it performs the necessary COM operations to create the underlying COM object. The Microsoft VM reads the CLSID for the underlying COM object from the class file's COM_CLASS_TYPE attribute as specified in the @com.class(clsid=…) directive, and uses this in a call to CoCreateInstance( ). If this call succeeds, the resulting interface is stored inside the Microsoft VM's representation of the JCW (see Figure 2).
Some COM objects can only be used from the thread that they were created from. As a result, the Microsoft VM will store information about the thread used to create the COM object in the JCW. That thread information allows the Microsoft VM to handle the underlying COM object from the correct thread context when performing operations on the underlying COM object, such as querying for interfaces.
Microsoft® J/Direct can also be used to access the Microsoft® Win32® API's COM functions to create new instances of COM classes. The com.ms.win32.Ole32 class contains J/Direct method mappings for many COM functions, including CoCreateInstance( ) and CoCreateInstanceEx( ). CoCreateInstanceEx( ) is particularly useful: It takes a remote computer’s name and security credentials as parameters to facilitate remote instantiation of COM objects. CoCreateInstanceEx( ) is the best way to access the functionality of Distributed COM (DCOM).
The J/Direct CoCreateInstance[Ex]( ) function calls can be used to create new instances of COM objects, even if no JCW has been created for the COM object. When the Microsoft VM attempts to marshal a COM object between native code and Java, it coerces the result into the expected Java type, which would typically be the JCW for the COM object or interface. When using a generic CreateInstance[Ex]( ) function through J/Direct, however, the return value will be typed as a generic java.lang.Object. The Microsoft VM has a generic JCW (com.ms.com.CUnknown) that it uses to represent an unknown COM object in Java. CUnknown can be cast to any JCW representing a COM interface supported by the underlying COM object.
It is occasionally useful to create and use an arbitrary COM object from Java without having to first generate JCWs. Script programmers will be familiar with the CreateObject function that takes a COM object's ProgID as a parameter. Create a COM object using Active Server Pages (ASP) as follows:
Set Obj = Server.CreateObject("IISSample.HelloWorld")
Although there isn't a single method that wraps this up as neatly as the IIS service, the following code sample uses J/Direct to get the same effect. The com.ms.com.Dispatch class can be used for late-bound IDispatch access to an object.
import com.ms.com.*; public class CreateObject { private static native _Guid CLSIDFromProgID(String str); private static _Guid IID_IUnknown = new _Guid("{00000000-0000-0000-C000-000000000046}"); private static Object createObject(String str) { return com.ms.win32.Ole32.CoCreateInstance( com.ms.win32.Ole32.CLSIDFromProgID(str), null, ComContext.INPROC_SERVER | com.ms.win32.win.CLSCTX_LOCAL_SERVER, IID_IUnknown); } public static void main(String args[]) { // Load Excel and print its name property. Object excel = createObject("Excel.Application"); System.out.println(Dispatch.get(excel, "name")); com.ms.com.ComLib.release(excel); }
QueryInterface( )
IUnknown is the base interface in COM that every COM object must implement. Its QueryInterface( ) method is used to query a COM object for any of its implemented interfaces. The Java/COM integration provided by the Microsoft VM provides QueryInterface( ) functionality through the Java cast operation. When it performs the casting operation, the Microsoft VM checks if the object being cast is a JCW, and if the type being cast to is a Java class representing a COM interface. If both these conditions are met, the Microsoft VM queries the underlying COM object for the required interface and continues. The Microsoft VM supports the ability to cast the COM object to any of its COM interfaces, even if its JCW class doesn’t explicitly implement a Java class representing those COM interfaces. This is specified in the JCW for the coclass by adding the DynamicCasts parameter to the @com.class directive. The QueryInterface( ) operation is described in Figure 3.
Figure 3: How the Microsoft VM performs a QueryInterface( ) operation on a JCW
Exceptions
COM functions and interface methods typically indicate error conditions by returning HRESULTs. Java methods, on the other hand, typically indicate error conditions by throwing Java exceptions. To make working with COM objects more like operating on Java objects, HRESULTs are mapped by the Microsoft VM to Java exceptions. The Microsoft VM will automatically throw an instance of a derivative from the abstract exception class com.ms.com.ComException, such as com.ms.com.ComFailException or com.ms.com.ComSuccessException, if the HRESULT returned from a JCW method call is other than S_OK. For more information on HRESULTs, see the MSDN Library. For details on the ComException classes, see the Microsoft SDK for Java.
COM objects are sensitive about which threads they can be created on and which threads their methods can be invoked from. By exposing COM objects to Java through JCWs, the Microsoft VM handles the threading issues related to using COM objects for the Java developer. As a result, invoking methods and initializing threads makes working with COM from Java easier and more productive.
The threading contexts that COM objects are useable from are called apartments. COM objects that can’t be used in a multi-threaded environment require a single-threaded apartment (STA). A Microsoft® Windows® thread prepares itself to host an STA COM object by calling the COM initialization function CoInitialize( ), and then starting a message pump to forward the COM-related Win32 messages that it will receive. Access to the hosted COM object can only occur within the STA’s thread context. The process of preparing to operate on the COM object from the correct thread (marshaling to the thread) will generate Win32 messages posted to the STA, which effectively synchronizes access to the hosted COM object.
The Microsoft VM internally maintains an STA thread for creating COM objects. This STA will be used whenever a Java application creates a new instance of a COM object from a regular Java thread. For performance reasons, it is sometimes desirable to create a Java STA thread for the COM object to be created and hosted on1.
1 The performance enhancements of creating an STA versus using the default VM STA are that method calls don't need to be marshaled to the VM’s STA, and the fact that having many COM objects in an STA will queue access to them, which could cause delays.
To create a Java thread capable of directly creating and hosting COM objects, call the ComLib.declareMessagePumpThread( ) method at the start of the Java thread's run( ) method. This tells the Microsoft VM that the Java thread will implement a message pump, if necessary, to service incoming COM calls. An example of this is shown as follows:
package tutorial.sample; public class STA extends Thread { /** *Hosted COM object */ private Object comObject = null; /** * The returned object will be a proxy to the hosted COM object. * The Microsoft VM will automatically take care of any thread * marshaling requirements, allowing the returned object to be * used like any other Java object. */ public Object getHostedCOMObject( ){ return com.ms.com.ComLib.makeProxyRef(comObject); } public void run(){ com.ms.com.ComLib.declareMessagePumpThread(); comObject = new SomeJCWClass(); com.ms.win32.MSG msg = new com.ms.win32.MSG(); while (com.ms.win32.User32.GetMessage(msg, 0, 0, 0)) { com.ms.win32.User32.TranslateMessage(msg); com.ms.win32.User32.DispatchMessage(msg); } } }
COM objects that support multi-threaded apartments (MTAs) are capable of receiving method calls from other threads at any time, and must implement synchronization in their interface method implementations as required. A process contains only one MTA in which all threads reside that are initialized as part of the MTA. However, that same process may also contain many STAs. Method calls on a COM object that resides in the MTA can be serviced from any MTA thread in the process. This means that a caller doesn’t need to marshal to a different thread before invoking a method on an MTA COM object on any MTA thread.
com.ms.com.ComLib.startMTAThread(Thread) automatically starts a Java thread and initializes the thread as an MTA thread by calling CoInitializeEx( ) with the necessary parameters. java.lang.Thread.start( ) always calls CoInitialize( ), which initializes a thread as an STA thread.
The Windows registry contains a list of all COM classes registered on the local system. It has information on how to create new instances and their supported threading models. In particular, the ThreadingModel value under the InprocServer32 and LocalServer32 keys for each COM class entry specify the supported threading models. The following table shows their possible values.
ThreadingModel | Description |
Absence of ThreadingModel
or None |
Originally, COM components didn’t support threading models; new instances had to be created on the thread that called CoInitialize( ) to initialize COM operations on that thread. This is denoted in the registry through the absence of a ThreadingModel value in the InprocServer32 or LocalServer32 keys or by setting ThreadingModel=None. |
Apartment | Apartment-threaded components can only be created on threads capable of hosting such objects. A thread prepares to host apartment-threaded COM objects by calling CoInitialize( ) to initialize itself as an STA, and then starts a message pump to forward COM-related Win32 messages it will receive. STA COM objects can only have their methods invoked from the STA they reside in. In the Microsoft VM, Thread.start( ) automatically calls CoInitialize( ). |
Free | Indicates that the COM class supports free-threading, and is hosted in an MTA. Method invocations can occur from any thread in the MTA’s thread pool. A thread joins an MTA thread pool by calling CoInitializeEx(COINIT_MULTITHREADED). In Java, com.ms.com.ComLib.startMTAThread(Thread) automatically calls CoInitializeEx(COINIT_MULTITHREADED). |
Both | Specifies that the COM object can be hosted on both STA and MTA threads. |
STA COM objects created (by default) by the Java new operator on a JCW class are created and serviced on a separate Microsoft VM-implemented STA thread. A Java thread that wants to directly create and manipulate STA COM objects, without marshaling to the Microsoft VM's internal STA thread, can call com.ms.com.ComLib.declareMessagePumpThread( ) and run a message pump. The Microsoft VM treats all COM classes implemented by local servers as STA COM classes, and uses the default STA rules for handling any Java new operations. J/Direct CoCreateInstance( ) calls return a proxy owned by the calling thread's COM apartment.
The Microsoft VM handles the marshaling for CoCreateInstance. This eliminates resorting to awkward pointer routines (as defined in com.ms.win32.Ole32.CoCreateInstance) as shown in the following example:
/** @dll.import("ole32", ole) */ private static native com.ms.com.IUnknown CoCreateInstance (com.ms.com._Guid rclsid, com.ms.com.IUnknown outer, int dwClsContext, com.ms.com._Guid riid);
CoCreateInstance( ) returns an interface reference that the Java developer can cast to any required COM interface. The resulting JCW contains the current threading context and will be automatically marshaled (Thread=AUTO behavior, discussed in Invoking COM Interface Methods).
Generally in COM, interface methods cannot be invoked on arbitrary threads. Whenever the Microsoft VM manipulates an external COM interface pointer underlying a Java interface instance (for example, IUnknown), it must perform any required marshaling of that interface pointer. The Microsoft VM stores the threading model type of the underlying COM object in the JCW instance used to expose the COM object to Java. If the JCW is marked free-threaded, the call happens on that thread. If the wrapper is marked apartment-threaded and the current thread context is not the wrapper's home context, the Microsoft VM switches to the correct context and executes the call there. As in standard COM STA marshaling, internal windows determine thread switches, but MTA threads have no obligation to pump messages. Once the Microsoft VM has marshaled to the correct thread context for performing the work on the interface, the Microsoft VM may need to marshal some of the parameters for the invocation. If any of these parameters are not on the right context (based on the same decision as previously mentioned), standard COM marshaling passes out a compatible interface pointer to the external code.
The Microsoft VM automatically determines (by default) the threading model for COM objects used as parameters in Java. It also performs the necessary marshaling to ensure method invocations occur from the correct thread. This behavior can be explicitly controlled on COM objects passed as COM interface method parameters by specifying the thread= AUTO | NO parameter in the method's @com.parameters directive. A default value for all COM objects used as parameters for all methods in the interface can be specified in the @com.interface directive. The Microsoft VM first checks the @com.parameters directive for a thread=<XXX> value for the particular parameter. If no value is specified, the Microsoft VM checks the @com.interface thread=<XXX> value. If none is found, the Microsoft VM uses the default thread=AUTO setting.
Possible values for the thread=<XXX> parameter are as follows:
Thread=AUTO- Indicates that the Microsoft VM should handle the threading of the COM object and take care of marshaling to the correct thread to operate on it. This value indicates that the COM object is either MTA or STA. The Microsoft VM checks any COM object marked with thread=AUTO to see if the object has aggregated the free-threaded marshaler (FTM), and if it has, the Microsoft VM treats the COM object as thread=NO. If the COM object has not aggregated the FTM, its JCW contains the current thread's context.
Thread=NO- Indicates that the Microsoft VM should not perform any threading operations and assumes that any threading issues have been taken care of. The COM object is dealt with as if it has been aggregated with the FTM. The COM object's methods can be invoked from any thread.
The garbage collection thread will not do the final release on a COM object. Every COM object wrapped as a Java object contains the thread context where it is safe to use the object. This could potentially be any thread context if the COM object aggregates the FTM. When the Java wrapper is garbage-collected, the underlying COM object's Release method will be called on the appropriate thread context. This is similar to the Microsoft VM’s marshaling calls on arbitrary Java threads to the appropriate thread context (when necessary).
When a JCW is garbage-collected, each COM interface cached in the JCW will have its Release( ) method called by posting a cleanup message to the thread owning the JCW. The Microsoft VM does not block waiting for the Release( ) calls to actually happen. If the thread no longer exists, the post fails, orphaning the interface pointers. The JCW itself is still garbage-collected, however, because the thread doing the garbage collection times out the request to the owning thread, and continues.
com.ms.com.ComLib.release(Object) is a shortcut for immediately releasing the underlying COM object, rendering it immediately unusable. Some COM objects tie their reference count to the release of resources without an explicit dispose( ) or close( ) call. As a result, a mechanism for immediately disconnecting the Microsoft VM's reference was required. Once garbage collection has run and the Microsoft VM has found unused Java wrappers around COM objects, it queues the Release( ) method calls to the appropriate thread contexts based on the JCWs' thread information.
JCWs can be generated using the jactivex tool provided in the Microsoft SDK for Java. Wrappers can be generated for existing COM components by running jactivex against the type library that represents the COM component and its associated interfaces. Type libraries are cross-platform binary representations of COM coclasses and COM interface definitions. They are usually2 created by compiling Interface Definition Language (IDL) code with the Microsoft Interface Definition Language (MIDL) compiler, which is available in the Platform SDK section of the MSDN Library.
2 JCWs can be created in other ways, too. The VM may be asked to create a JCW, which is how Javareg.exe and Vjreg.exe create type libraries.
Type libraries are used by COM-compliant languages to determine how to represent these coclasses and interfaces in those languages. For instance, Microsoft® Visual Basic® developers typically import a type library into the Visual Basic development environment to use the COM objects and interfaces the type library describes.
Type libraries generated by the MIDL compiler are binary files that have a .tlb extension. It is possible to add type library information to a dynamic-link library (DLL) or .exe file as a resource. Common file extensions of files that may contain type library information are as shown in the following table.
Module extension | Description |
DLL | Dynamic-link libraries may contain type library information. |
EXE | Applications may contain type library information. |
OCX | DLLs that contain Microsoft® ActiveX® Controls. |
OLB | Microsoft® Office components store their type libraries in this format. |
TLB | Denotes a type library. This will be the output from MIDL. |
OleView is a COM tool that ships with the Platform SDK in the MSDN Library and Microsoft® Visual Studio® products. OleView is a simple application for examining the COM objects on your system. It presents a treeview of COM components installed on the system, a list of registered COM interfaces, and the installed type libraries. Typically, registered COM objects and COM interfaces contain the location of the file that contains the type library describing it. It is instructive to use OleView to browse through the COM objects, interfaces, and type libraries installed on your system. You can use OleView to examine a file that might contain type library information. For example, look at the shdocvw.dll file like this:
oleview c:\winnt\system32\shdocvw.dll
The type libraries registered on your system describe some of the COM object models exposed by the installed applications. Examine these files to learn how to use the COM objects and interfaces for integration with their associated applications. Jactivex is a tool available in the Microsoft SDK for Java that generates JCWs from any type library. This tool provides instant access to Java classes for integrating with Win32 applications. The shdocvw.dll file contains the type library for the Microsoft® Internet Explorer 4.0 COM object model, and is used here to demonstrate how JCWs are generated. Then, a simple application is presented that uses the generated wrappers.
To generate the JCWs for shdocvw.dll, use the jactivex tool like this:
jactivex /d . /p:b- /p tutorial.ie c:\winnt\system32\shdocvw.dll
Jactivex creates a tutorial\ie directory structure that contains about 50 Java classes, all defined in the tutorial.ie package. These Java classes contain the @com directives that generate the COM attributes in the compiled class files to make the classes JCWs.
The following is the generated Java wrapper for the Microsoft® Internet Explorer coclass :
package tutorial.ie; import com.ms.com.*; import com.ms.com.IUnknown; import com.ms.com.Variant; /** @com.class(classid=0002DF01-0000-0000-C000-000000000046, DynamicCasts) */ public class InternetExplorer implements IUnknown, com.ms.com.NoAutoScripting, tutorial.ie.IWebBrowser2, tutorial.ie.IWebBrowserApp { /** @com.method() @hidden */ public native void GoBack(); /** @com.method() @hidden */ public native void GoForward(); /** @com.method() @hidden */ public native void GoHome(); /** @com.method() @hidden */ public native void GoSearch(); /** @com.method() @hidden */ public native void Navigate(String URL, Variant Flags, Variant TargetFrameName, Variant PostData, Variant Headers); /** @com.method() @hidden */ public native void Refresh(); [Code removed for brevity.] // getFullScreen UNMAPPABLE: Name is a keyword or conflicts with another member. // /** @com.method() // @hidden */ // public native boolean getFullScreen(); // setFullScreen UNMAPPABLE: Name is a keyword or conflicts with another member. // /** @com.method() // @hidden */ // public native void setFullScreen(boolean pbFullScreen); public static final com.ms.com._Guid clsid = new com.ms.com._Guid((int)0x2df01, (short)0x0, (short)0x0, (byte)0xc0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x46); }
@com.class
The comments at the start of the source file were generated by jactivex, which contains the exact command line parameters used. The Java comment block above the class definition contains an @com.class directive, which specifies the GUID for the represented COM class and indicates that this class supports dynamic casts. A full description of the @com.class directive is presented here.
The @com.class directive is applied at the class level to indicate that the Java class definition represents a COM component object class (coclass).
@com.class(classid=<COM CLSID>, DynamicCasts, safe, safeAddFlags=<integer>)
Parameter | Description |
classid=<COM CLSID>
This parameter is required. Example: classid=0BE35203-8F91-11CE-9DE3-00AA004BB851 |
The CLSID associated with the Java-implemented COM class. |
DynamicCasts | Indicates that this class supports dynamic casts, which allow the Java developer to cast it to COM interfaces not directly implemented by the Java class. The Microsoft VM supports this by calling QueryInterface( ) for the target interface of the cast on the underlying COM object. |
safe | Specifies that the class may be safely used by untrusted classes. A COM_Safety attribute is attached to the class. |
safeAddFlags=<int> | Specifies a word (2 bytes) of information to be put in the COM_Safety attribute.
Note The Microsoft VM currently ignores this data when reading the COM_Safety attribute. |
Attribute | Scope |
COM_ClassType | Class scope. |
COM_GuidPool | Class scope. |
COM_Safety | Class scope. |
The tutorial\ie\InternetExplorer.java file contains a class definition for the InternetExplorer coclass, which implements four interfaces. Three of these interfaces are JCWs that represent the COM interfaces IUnknown, IWebBrowser2, and IWebBrowserApp. The com.ms.com.NoAutoScripting interface is used as a flag by the Microsoft VM to indicate that this COM object is not accessible from scripting languages.
The methods presented in the source file are designated as hidden; they exist only to satisfy the Java requirement that all methods from all implemented interfaces are represented. The native designation on each method in the coclass tells the Microsoft VM that the implementation of the method is contained in the underlying COM object.
The static final CLSID field contains a com.ms.com._Guid initialized with the coclass's GUID.
The following is the jactivex-generated source file for the COM interface IWebBrowserApp:
package tutorial.ie; import com.ms.com.*; import com.ms.com.IUnknown; import com.ms.com.Variant; // Dual interface IWebBrowserApp /** @com.interface(iid=0002DF05-0000-0000-C000-000000000046, thread=AUTO, type=DUAL) */ public interface IWebBrowserApp { /** @com.method(vtoffset=4, dispid=100, type=METHOD, name="GoBack", addFlagsVtable=4) @com.parameters() */ public void GoBack(); /** @com.method(vtoffset=5, dispid=101, type=METHOD, name="GoForward", addFlagsVtable=4) @com.parameters() */ public void GoForward(); /** @com.method(vtoffset=6, dispid=102, type=METHOD, name="GoHome", addFlagsVtable=4) @com.parameters() */ public void GoHome(); /** @com.method(vtoffset=7, dispid=103, type=METHOD, name="GoSearch", addFlagsVtable=4) @com.parameters() */ public void GoSearch(); /** @com.method(vtoffset=8, dispid=104, type=METHOD, name="Navigate", addFlagsVtable=4) @com.parameters([in,type=STRING] URL, [in,elementType=VARIANT,type=PTR] Flags, [in,elementType=VARIANT,type=PTR] TargetFrameName, [in,elementType=VARIANT,type=PTR] PostData, [in,elementType=VARIANT,type=PTR] Headers) */ public void Navigate(String URL, Variant Flags, Variant TargetFrameName, Variant PostData, Variant Headers); /** @com.method(vtoffset=9, dispid=4294966746, type=METHOD, name="Refresh", addFlagsVtable=4) @com.parameters() */ public void Refresh(); [Code removed for brevity.] /** @com.method(vtoffset=48, dispid=407, type=PROPPUT, name="FullScreen", addFlagsVtable=4) @com.parameters([in,type=BOOLEAN] pbFullScreen) */ public void setFullScreen(boolean pbFullScreen); public static final com.ms.com._Guid iid = new com.ms.com._Guid((int)0x2df05, (short)0x0, (short)0x0, (byte)0xc0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x46); }
@com.interface
The tutorial\ie\IWebBrowserApp.java source file contains a Java interface definition that has been marked as a COM interface with a class-scope @com.interface directive. This directive contains the IID and specifies the type of COM interface that is represented (dual).
@com.interface(iid=<GUID>, thread=AUTO | NO, type=DISPATCH | DUAL | VTABLE)
Parameter | Description |
iid=<GUID> | Required. This GUID represents the IID for this interface. |
thread=AUTO | NO | Specifies the default Microsoft VM marshaling behavior to be applied to the COM object method parameters in this interface's methods. If AUTO is specified, the Microsoft VM automatically marshals any calls from Java on this object to the thread that the object was created in. This is the safest option, and the default.
If NO is specified, the Microsoft VM assumes that the object is callable on any Java thread. |
type=DUAL | DISPATCH | VTABLE | VTABLE indicates that this interface can be used only through a vtable.
DISPATCH indicates that this interface can be used only through IDispatch. DUAL indicates that this interface is accessible through both its vtable and through IDispatch. If the type is unspecified, vtable access is assumed. |
The following table shows the attributes indirectly affected by @com.interface directive.
Attribute | Scope |
COM_ExposedAs_Group | Method scope. |
COM_GuidPool | Class scope. |
COM_MethodPool | Class scope. |
COM_ProxiesTo | Method scope. |
By default, COM interfaces use early binding through a virtual table (vtable) of function pointers. This means that references to COM interface methods are determined at compile time, and are therefore subject to all compile-time syntax checking rules. Each interface method is placed in the interface's vtable in a specific order. At compile time, the compiler can call an interface method through the code pointer at the method's offset in the interface's vtable. For example, looking at the interface definition for the base COM interface IUnknown, it contains three methods and has a specific vtable order for these methods. All COM interfaces extend IUnknown, have vtables that contain the three IUnknown methods in exactly the same vtable positions, and append their own methods to the base IUnknown vtable.
Visual Basic® and Visual Basic® for Applications(VBA) always use the vtable-interface by default when the object-type is declared. Only variables declared as Objects or invoked methods or properties not declared in the interface definition go through IDispatch. Visual Basic® and Visual Basic® for Applications(VBA) also work with IUnknown-derived interfaces.
Automation is a mechanism that facilitates late binding to a COM object's interface methods by providing a generic Invoke( ) method. This is similar to using the reflection APIs in Java where the String form of the method name can be used to obtain a reference to the method. The generic Invoke( ) method is then called by passing the method identifier and all its required parameters. These late binding facilities are provided by the IDispatch interface, which contains methods for looking up the method ID (called the dispatch ID or DISPID) for a given method name, and for invoking the actual method. For this process of late binding to work correctly, it is necessary that only known data types be used as parameters to automation interfaces. The acceptable types are called automation data types, and are discussed in the COM Method Parameters in Java section of this topic.
Dispinterfaces are custom interfaces that extend IDispatch. By creating a dispinterface, a developer allows the interface's methods to be accessible through automation using the IDispatch interface. Dual interfaces are implemented as both dispinterfaces and vtable interfaces. This means that the functions they contain are accessible both from IDispatch and directly from the vtable. The implemented interface is available to C++ and Java programmers through early binding, and to Visual Basic® and scripting programmers through the late binding of the IDispatch interface. Dual interfaces are the preferred way of implementing dispinterfaces.
Each of the methods in the tutorial.ie.IWebBrowserApp interface definition has an associated @com.method directive that indicates how the COM method is mapped to Java.
@com.method
The @com.method directive indicates the method dispatch ID for dispatch interfaces, the vtable offset for vtable interfaces, or both for dual interfaces. It includes a set of parameters that indicate the mapping to the equivalent IDL form. Because COM interfaces support properties, the method can be a typical method or an accessor method for an interface property.
@com.method(vtoffset=<offset>, dispid=<dispid>, name=<COM method name>, name2=<COM method name>, type=METHOD | PROPGET | PROPPUT | PROPPUTREF, nodispatch, returntype=VOID | HRESULT, addFlagsVtable=<int>
Parameter | Description |
vtoffset=<offset> | This specifies the vtable offset for this method. Not required if the method is only being called through an IDispatch interface. |
dispid=<dispid> | Indicates the dispatch ID for this method. Not required if this method is only being called through a vtable. |
name="<COM method name>"
Example: name="<myMethod1>" |
Specifies how to expose this Java method to COM by means of IDispatch. The Microsoft VM implements an IDispatch interface that maps the name given by this parameter to the DISPID given by the dispid parameter. If unspecified, the default is the method name. |
name2="<COM method name>" | Specifies a secondary mapping from this Java method to COM by means of IDispatch. The Microsoft VM implements an IDispatch interface that maps the name given by this parameter to the DISPID given by the dispid parameter. |
type=METHOD | PROPGET | PROPPUT | PROPPUTREF | Used for dispatch interfaces. Indicates the type of COM method represented by this Java method. The method may manipulate COM object properties or it may be a COM object method. |
nodispatch | Specifies that this method should not be accessible through IDispatch. |
returntype=VOID | HRESULT | Specifies the return type for this method. Generally, the default, HRESULT, is used. If HRESULT is specified, the COM method associated with this Java method returns an HRESULT. A com.ms.com.ComException will be thrown if the method returns a non-S_OK HRESULT. If the Java method returns a value, it is mapped to the last parameter in the COM method, which is assumed to be a return value.
If VOID is specified, the Java and COM method return values are directly related. That is, if the COM method returns an integer, its value is returned by the Java method. |
addFlagsVtable=<int>
Example: addFlagsVtable=4 |
A low-level parameter that allows modification of created class attributes, specifically, the Flags field of the MCFuncDesc structure created in the MethodPool for this method. Specify the value 4 to cause the Microsoft VM to ignore non-failing HRESULTs. The Microsoft VM will still throw a ComFailException for failing HRESULTs, but will not throw a ComSuccessException for successful HRESULTs. |
The following table shows class file attributes generated by the @com.method directive.
Attribute | Scope |
COM_ProxiesTo | Method scope. |
COM_ExposedAs_Group | Method scope. |
COM_GuidPool | Class scope. |
COM_MethodPool | Class scope. |
The following is the Refresh( ) method from the tutorial.ie.WebBrowserApp COM interface:
/** * @com.method(vtoffset=9, dispid=4294966746, type=METHOD, name="Refresh", * addFlagsVtable=4) * @com.parameters() */ public void Refresh();
This method is a COM method that is part of a dual interface, because it has both a vtoffset and a DISPID. The value 4 for the addFlagsVtable parameter indicates that successful HRESULT error codes should not be thrown as com.ms.com.ComExceptions. For more information on successful and failed HRESULTs, see the MSDN Library.
The following method is an accessor method for setting a property in the dual interface. type=PROPPUT indicates that this method sets the property named "FullScreen" in the COM interface IWebBrowserApp:
/** * @com.method(vtoffset=48, dispid=407, type=PROPPUT, name="FullScreen", addFlagsVtable=4) * @com.parameters([in,type=BOOLEAN] pbFullScreen) */ public void setFullScreen(boolean pbFullScreen);
Each of the methods in the tutorial.ie.IWebBrowserApp interface definition has an associated @com.parameters directive that indicates how the COM method parameters are mapped to Java.
The @com.parameters directive denotes the mapping of each Java parameter's data type to the underlying COM data type and vice versa. Because COM interfaces support properties, the method may be a typical method or an accessor method for an interface property.
@com.parameters is used with @com.method to specify how Java parameters and return types are mapped to the underlying COM method. It is also possible to specify a custom marshaler that handles how the parameters are represented in the COM and Java representations. Each @com.parameters directive contains a group of directive parameters, which are inside brackets ([]), for each parameter in the method and for the return value (if it is not void).
For example, in previous description of the setFullScreen( ) COM method, the pbFullScreen Java parameter is represented as the automation type BOOLEAN. It need only be marshaled into the COM method.
/**
@com.parameters([directive parameters] param1,
[directive parameters] param2,
…,
[directive parameters] return)
*/
public int myMethod1(IUnknown param1, Variant param2) {…}
The COM.parameters directive [directive parameters] describes each variable in the method prototype. This is accomplished by specifying the directive parameters for a variable inside brackets, followed by the parameter name (param1, param2, and so on). The keyword return is used as the variable name for the method return type when describing the directive parameters for the return value.
Parameter | Description |
type=I1 | I2 | I4 | I8 | U1 | U2 | U4 | U8 | R4 | R8 | STRUCT | OBJECT | STRING | CUSTOM | CUSTOMBYREF | CUSTOMBYVAL | CURRENCY | DATE | DISPATCH | BOOLEAN | VARIANT | PTR | ARRAY | SAFEARRAY | STRUCT means byval struct. Unsupported for dispatch classes.
OBJECT means a pointer to IUnknown for vtable calls, and VT_UNKNOWN for dispatch calls. CUSTOM specifies that this parameter will be custom marshaled by the Java class specified in the customMarshaler parameter. CUSTOMBYREF specifies that this parameter will be custom marshaled by the Java class specified in the customMarshaler parameter as a by reference value. This means that the custom marshaler's toExternal( ) method will be called to marshal the parameter to native code, and its toJava( ) method will be called to marshal the parameter from native code to Java. CUSTOMBYVAL specifies that this parameter will be custom marshaled by the Java class specified in the customMarshaler directive parameter as a by-value parameter. This means that the custom marshaler's copyToExternal( ) method will be called to marshal the parameter to native code, and its copyToJava( ) method will be called to marshal the value from native code to Java. STRING means BSTR for vtable call, and VT_BSTR for dispatch call. CURRENCY means I8 for vtable call, and VT_CY for dispatch call. DATE means R8 for vtable call, and VT_DATE for dispatch call. DISPATCH means IUnknown * for vtable call, and VT_DISPATCH for dispatch call. SAFEARRAY specifies that this parameter should be marshaled as a COM SafeArray. This parameter must be typed as com.ms.com.SafeArray in the method prototype. BOOLEAN means U2 for vtable call, and VT_BOOL for dispatch call. VARIANT means byval VARIANT, and VT_VARIANT for dispatch call. PTR for string parameters means pointer to a task-allocated Unicode string. For struct parameters, PTR means pointer to struct. For dispatch calls, PTR generally means VT_BYREF | elementType. ARRAY for a boolean array means a VARIANT_BOOL array. For an integer or float array, ARRAY means an integer or float array, respectively. For a string array, ARRAY means a BSTR array. For an array of structs, ARRAY means an array of pointers to structs. For an array of other objects, ARRAY means an array of IUnknown *s. For dispatch calls, ARRAY generally means VT_BYREF | elementType. |
in | Specifies that the parameter will be marshaled to the COM method call. Specify [in,out] to marshal the parameter both to and from the COM method call. |
out | Specifies that the parameter will be marshaled from the COM method call. Specify [in,out] to marshal the parameter both to and from the COM method call. |
iid=<GUID> | Identifies a COM interface parameter. Used instead of the type specification. |
thread=NO | AUTO | Specifies the marshaling behavior to be applied to the COM object parameter. If AUTO is specified, the Microsoft VM automatically marshals any calls from Java on this object to the thread that the object was created in. This is the safest option, and the default.
If NO is specified, the Microsoft VM assumes that the object is callable on any Java thread. Valid only for type=OBJECT and type=DISPATCH types. |
elementType=VARIANT | U1 | I2 | I4 | R4 | R8 | STRING | OBJECT | DISPATCH | Specifies the element type of arrays if type=ARRAY or type=PTR. A default value is generated based on the Java type of the element (for example, int[] is equal to I4).
Dependency: Used only if type=ARRAY or PTR. |
size=<int> | The number of elements in the array.
Dependency: Used only if type=array. |
customMarshal="<class>"
Example: customMarshal="com.ms. |
Specifies the name of a class. This class will marshal the parameter. Dependency: Used only if type =CUSTOM. |
customMarshalFlags=<int> | Specifies bits available to the custom marshaler when invoked. Must be in the 0 to 3 range. These bits move into the high two bits of the MCMT_Attr.typedesc.Flags field in the COM_MapsTo attribute generated for the field.
Dependency: Used only with customMarshal. |
vt=<int> | Specifies the VARTYPE of elements within a SAFEARRAY parameter. Use the VT_* values. For example, for an array of integers, use VT_I4 (3).
Dependency: Required and used only for parameters of type SAFEARRAY. |
byref | Specifies that this argument is passed by reference (VT_BYREF) in a VARIANT.
Dependency: Valid only for type=DISPATCH methods. |
array | Specifies that this parameter is passed as an array (VT_ARRAY) in a VARIANT.
Dependency: Valid only for type=DISPATCH methods. |
name="<name>"
Example: name="Hello" |
Specifies a name to be associated with this parameter.
Dependency: Valid only for type=DISPATCH methods. Note This parameter is not currently used by the Microsoft VM. |
The following table shows the attributes generated in class files by the @com.parameters directive.
Attribute | Scope |
COM_GuidPool | Class scope. |
COM_MethodPool | Class scope. |
COM interface methods typically return HRESULTs. A returned HRESULT other than S_OK causes the Microsoft VM to throw a com.ms.com.ComException. Additional returned data from a COM interface method, if any, is typically returned in the last parameter of the method. In an effort to make COM interfaces easier to use from Java, developers should endeavor to map the real result of the COM function to the return value of the Java method in the COM wrapper. For example, the COM definition of the IClassFactory interface's CreateInstance( ) method is as follows:
HRESULT CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
The equivalent Java representation might be as follows:
/** * @com.method(vtoffset=0, addFlagsVtable=4) * @com.parameters([in,type=OBJECT] pUnkOuter, [in,type=PTR] riid, [type=OBJECT] return) */ public IUnknown CreateInstance(IUnknown pUnkOuter, _Guid riid);
The interface pointer returned in **ppvObject from the IClassFactory::CreateInstance( ) COM method call will be mapped to the Java method's IUnknown return value.
Visual Basic provides a simple way to create Windows applications. However, Visual Basic does not support C++ features such as pointers and vtable methods. Automation makes COM objects accessible to Visual Basic and scripting languages such as VBScript, Microsoft® JScript®, and dynamic HTML (DHTML). Automation is enabled by a COM interface called IDispatch, which supports late binding of interface methods. This is similar to the reflection API in Java. Automation methods only use automation-compatible data types.
Besides the string, integer, and boolean data types, automation defines two other special data types: Variant and SafeArray. A Variant is a generic data type that can contain any other automation data type (including other Variants). A SafeArray is a structure that contains an array of automation data types, the number and type of the array elements, and the total size of the array. This table lists the automation data types and their Java equivalents:
Automation Type | Java Type |
VT_BOOL, boolean | boolean |
VT_I4, char | char |
VT_R8, DATE, double | double |
VT_I4, int | int |
VT_I4, long, int64, CURRENCY, CY | long |
VT_I4, short | short |
VT_I4, byte, unsigned char | byte |
VT_BSTR, LPCOLESTR | java.lang.String |
SCODE, HRESULT | int (see the com.ms.com.ComException class) |
VARIANT | com.ms.com.Variant |
IUnknown | com.ms.com.IUnknown |
IDispatch | java.lang.Object |
SAFEARRAY | com.ms.com.SafeArray |
VT_VOID, void | void |
VT_R8, float | float |
All Java objects not explicitly mentioned in this table map to VT_DISPATCH, except for the following special ActiveX control types that are used for the Microsoft Windows Foundation Classes (WFC) and JavaBeans ActiveX control bridges.
ActiveX type | Java type |
OLECOLOR | com.ms.wfc.ui.Color |
IFont | java.awt.Font, com.ms.Fx.FxFont |
IFonstDisp | com.ms.wfc.ui.Font |
IPictureDisp | com.ms.wfc.ui.Image |
When dealing with arrays, a generic <type> * parameter in IDL maps to an <type> [] parameter in Java. But when jactivex sees an <type> [] it is mapped to a Variant[]. For this case, the Microsoft VM doesn't have enough metadata to know the underlying element type of the single-dimensional array. As a result, it treats it as an array of Variants.
Note that the Variant and SafeArray data types are equivalent to specific Java classes, com.ms.com.Variant and com.ms.com.SafeArray, respectively. These classes are Java-Callable Data Wrappers (JCDWs). JCDWs are Java classes with extra attributes indicating to the Microsoft VM that the object has a native representation and how the object should be represented in native code.
Java-Callable Data Wrappers (JCDWs) represent native structures as Java objects. They are particularly useful for implementing structures, Variants, and SafeArrays. Each JCDW includes a generic Java object that exposes the native structure to Java developers, and an associated block of native memory that contains the native representation. It is the native memory that will be passed to native function calls or COM method calls. Figure 4 is an overview of how the JCDW is represented inside the Microsoft VM.
package tutorial.jcdw; /** @com.struct() */ public class JCDW { public int nField1; public String strField2; }
Figure 4: Abstract representation of the Java-Callable Data Wrapper at runtime in the Microsoft VM
The JCDW stores primitive data for the structure directly in the native memory, but must keep a separate garbage-collectible object for each embedded reference such as arrays, strings, or other JCDWs. The Microsoft VM typically propagates the referenced objects when it attempts to marshal the JCDW to native memory. Essentially, propagation involves synchronizing the native memory values with the garbage-collection objects on the way into native memory, and synchronizing the garbage-collection objects with the native memory values on the way out. The Microsoft VM supports implementation of custom marshaling through calls to toExternal( ) (for Java-to-native marshaling) and toJava( ) (for native-to-Java marshaling) methods in custom marshaling Java classes. The Microsoft SDK for Java contains detailed documentation and sample code for custom marshaling. The Microsoft VM is able to create the appropriate data structure in native memory by examining the underlying class file for the JCDW. A JCDW class will contain extra class file attributes that were generated by the @com.struct or @dll.struct directives, or the @com.structmap or @dll.structmap directives.
@com.struct and @dll.struct
The @com.struct directive is specified at the class level and marks the class declaration as equivalent to a C structure definition. Instances of the class can then be passed through J/Direct and COM method calls that require a C structure with the same composition.
Note The syntax for @dll.struct and @com.struct is identical. The only difference between them is the way that some fields are marshaled between Java and native code.
Within the class declaration, you need to supply fields whose Java types map to the types of the fields in the native structure in the order they occur in the native structure.
@dll.struct(ansi | unicode | auto, pack=1 | 2 | 4 | 8, noAutoOffset, safe, safeAddFlags=<int>)
Be aware that white space within @dll directives can cause syntax errors. You can put white space between the directive and the comment tags, but not within the directive itself. For example, the following directive would cause a syntax error:
/**@dll.struct("MyDll", auto)*/
But this one would not cause an error:
/** @dll.struct("MyDll",auto) */
Parameter | Description |
noAutoOffset | Specifies that the offset of each field in the structure will be explicitly defined. This is accomplished by specifying an @dll.structmap directive for each field, and using the offset parameter to specify the offset. If this parameter is specified, each field in the class must have an @dll.structmap directive that gives the field's offset.
If this parameter is not specified, the Microsoft VM automatically calculates offsets for each field in the structure. In this case, the default packing size is 8 bytes, but can be modified by the pack parameter. Dependency: @dll.structmap. |
unicode | Specifies that strings and char fields used in this structure should be converted to Unicode native values.
Dependency: This cannot be specified with ansi or auto. Not valid in @com.struct. |
ansi | Specifies that strings and char fields used in this structure should be converted to ANSI native values. This is the default.
Dependency: This cannot be specified with unicode or auto. Not valid in @com.struct. |
auto | Determines how the strings and char fields in the defined structure will be interpreted in the native mapping. Generally, Microsoft® Windows® 95 or Microsoft® Windows® 98 requires ANSI strings, while Microsoft® Windows NT® version 4.0 accepts Unicode strings. The auto parameter chooses the correct string type at runtime, depending on the system that the class is used on.
The default is ansi. Dependency: This cannot be specified with unicode or ansi. Not valid in @com.struct. |
safe | Specifies that the structure may be used safely by untrusted classes. A COM_Safety attribute is attached to the class. |
safeAddFlags=<int> | Specifies a word of information to be put in the COM_Safety attribute.
Note The Microsoft VM currently ignores this data when reading the COM_Safety attribute. |
pack=1 | 2 | 4 | 8 | Specifies the packing alignment of the structure, which determines how fields will be aligned in memory. Typically, the packing alignment for a structure is specified in C by using the #pragma pack(n) syntax. The value given for the Java pack parameter should be the same as that used for the corresponding C structure. The default value is 8, which is the default Microsoft® Visual C++® packing alignment.
Dependency: If the noAutoOffset parameter is specified, the Microsoft VM ignores this parameter. |
The following table shows class file attributes generated by the @com.struct and @dll.struct directives.
Attribute | Scope |
COM_ClassType | Class scope. |
COM_MapsTo | Field scope. |
COM_Safety | Class scope. |
@com.structmap and @dll.structmap
The @dll.structmap directive is applied to a field in an @dll.struct class to specify how a field is exposed to native memory. This directive must be used if the structure was declared using @dll.struct(noAutoOffset), which requires the offset keyword in each @dll.structmap directive. This directive can also override the default field mappings in other struct classes.
Static fields are not considered part of the structure by the Microsoft VM, and can hold information that is not part of the structure definition.
Note There are no differences other than name between @dll.structmap and @com.structmap.
@dll.structmap(offset=<int | long>, iid=<GUID>, thread=AUTO | NO, size=<int | long>, customMarshalFlags=<int>, customMarshal=<class name>, addFlags=<byte>, type=BOOLEAN | CURRENCY | CUSTOM | CUSTOMBYREF | CUSTOMBYVAL | DATE | DISPATCH | FIXEDARRAY | I1 | I2 | I4 | I8 | OBJECT | PTR | R4 | R8 | STRING | TCHAR<int> | U1 | U2 | U4 | U8 | VARIANT, <field name>)
Parameter | Description |
offset=<int | long>
Example: offset=4 |
Specifies the byte offset from the start of the structure.
Dependency: Used only in structures specified with no auto offsets, as in @dll.struct(noAutoOffset). |
thread=<AUTO | NO> | Specifies the Microsoft VM marshaling behavior to be applied to this field. If AUTO is specified, the Microsoft VM automatically marshals any calls from Java on this object to the thread that the object was created in. This is the safest option, and the default.
If NO is specified, the Microsoft VM assumes that the object is callable on any Java thread. Dependency: Valid only for type=OBJECT and type=DISPATCH types. |
iid=<GUID>
Example: iid=0000000F-0000-0000-C000-000000000046 |
Indicates the IID of the COM interface reference being mapped to this field. If this parameter is not used and the type is specified as OBJECT, the default is IID_IUnknown.
Dependency: Valid only for type=OBJECT and type=DISPATCH types. |
size=<int | long>
Example: size=3 |
Specifies the number of elements in an array.
Dependency: Valid only for type=FIXEDARRAY. |
customMarshalFlags=<int> | Specifies bits available to the custom marshaler when invoked. Must be in the 0 to 3 range. These bits move into the high two bits of the MCMT_Attr.typedesc.Flags field in the COM_MapsTo attribute generated for the field.
Dependency: Used only with customMarshal. |
customMarshal=<class name>
Example: customMarshal="com.ms.com.UniStringMarshaler" |
Specifies the name of a class that will marshal the field.
Dependency: Valid only for type=CUSTOM and type=CUSTOMBYVAL |
addFlags=<int> | Specifies a byte value in the 0 to 255 range that is moved into the MCMT_Attr.typedesc.Flags field in the COM_MapsTo attribute generated for the field. |
type=I1 | I2 | I4 | I8 | U1 | U2 | U4 | U8 | R4 | R8 | PTR | OBJECT | STRING | CUSTOM | CUSTOMBYVAL | DATE | CURRENCY | DISPATCH | BOOLEAN | TCHAR | TCHAR<int> | FIXEDARRAY | VARIANT | PTR is valid only for fields of structure (JCDW) type. Indicates a pointer to structure in a native structure.
CUSTOM specifies that the field should be custom-marshaled by the class specified in the customMarshaler parameter. The indicated class's toExternal( ) method will be called to marshal the field from Java to native, and its toJava( ) method will be called to marshal the field from native to Java. CUSTOMBYVAL specifies that the field should be custom-marshaled by the class specified in the customMarshaler parameter as a by-value entity. The indicated class's copyToExternal( ) method will be called to marshal the field from Java to native, and its copyToJava( ) method will be called to marshal the value from native to Java. OBJECT is a pointer to a COM object, IUnknown *. STRING indicates a pointer to a Unicode string. Valid only for fields of type java.lang.String. DATE is the same as R8. Use R8 instead. CURRENCY is the same as I8. Use I8 instead. DISPATCH is the same as OBJECT. Use OBJECT instead. BOOLEAN maps to U2 (VARIANT_BOOL). TCHAR specifies that a character type in a native structure (U1 or U2) is based on the default character type (auto, ansi, or unicode). TCHAR<int> is an embedded array of characters with length <int>. The character type in a native structure is based on the default character type. Valid only for fields of type java.lang.String. FIXEDARRAY is an embedded array. The size parameter defaults to 1, but can be overridden by specifying a value for it that gives the number of elements. Valid only for fields specified as an array of primitive types. For arrays of characters, native type is determined by default character type (auto, ansi, or unicode). Arrays of booleans are mapped to BOOL[] in native code. VARIANT should be used only for a field of type com.ms.com.Variant. |
If there is no structmap for a field, the defaults shown in the following table are used.
Java type | Native type for @dll.struct | Native type for @com.struct |
boolean | U4 | U2 |
byte | I1 | I1 |
short | I2 | I2 |
char | TCHAR | U2 |
int | I4 | I4 |
long | I8 | I8 |
float | R4 | R4 |
double | R8 | R8 |
java.lang.String | type=CUSTOM
customMarshal="com.ms.com. |
String |
com.ms.com.Variant | VARIANT | VARIANT |
struct class | OBJECT | OBJECT |
The following table shows the affected attributes.
Attribute | Scope |
COM_MapsTo | Field scope. |
Working with JCDWs
Sometimes it is necessary to access the address of the native memory used by the JCDW, or to create a JCDW from data in an arbitrary native memory block. The classes in the com.ms.dll package help accomplish these tasks. For example, to obtain the underlying native memory address of a JCDW, do something like this:
public class JCDWExample { public static MyJCDWClass myJCDW = new MyJCDWClass(); public static void main(String args[]){ int handle = com.ms.dll.Root.alloc(myJCDW); int addr = com.ms.dll.DllLib.addrOf(handle); com.ms.dll.Root.free(handle); } }
The call to com.ms.dll.Root.alloc( ) creates a root handle that represents the JCDW in the garbage-collection heap, and protects it from being garbage-collected. com.ms.dll.DllLib.addrOf( ) takes the root handle and returns the address of the underlying native memory to the caller. com.ms.dll.Root.free( ) frees the root handle, allowing the object to be garbage-collected.
To create a JCDW object from a given memory location, use the com.ms.dll.DllLib.ptrToStruct( ) method. It takes the native memory location of the data and the class of the JCDW representing the structure stored in that location. The result is an instance of specified JCDW initialized with the values from the specified memory location. Note that the Microsoft VM attempts to propagate all native values into the new JCDW instance. As a result, if the memory location is simply a newly allocated block, it will contain uninitialized garbage-collection values. This may cause the Microsoft VM to fault, especially if the JCDW contains some pointer values that may be mapped into protected memory. To guard against this, ensure that the memory location being used in the mapping has been initialized.
To access the native memory used by the JCDW from Raw Native Interface (RNI) code, use the functions described in Nativcom.h in the Microsoft SDK for Java. Specifically, use jcdwGetData(HObject *), which returns LPVOID that can be cast to your defined structure.
Variants
Variants are used as generic data types in automation for passing data through COM dispinterfaces. The @com.parameters section of this topic describes the default mappings between automation data types and Java data types. Whenever it is necessary to communicate with a COM object through its IDispatch interface, the parameters will need to be passed as variants. The Microsoft VM provides the com.ms.com.Variant class for this purpose.
Jactivex maps overloaded methods from dispinterfaces as methods that contain the maximum number of parameters, with each parameter as a variant. To invoke an overloaded method instance with fewer parameters, pass instances of the com.ms.com.Variant object, which have their noParam( ) method called. This indicates that the variant is being used to fill a missing or optional parameter in the underlying COM interface function call. All Java objects that don’t explicitly have a Variant mapping (as outlined in the Automation Data Types section of this topic) will be passed as COM objects exposed through their IDispatch interfaces. Methods can be invoked on the Java object parameter by invoking methods on this IDispatch interface by means of methods of the com.ms.com.Dispatch class.
SafeArrays
Languages that interact with COM objects through automation typically can't directly handle pointers and native memory. This becomes a problem when working with arrays of data types. A SafeArray is used in automation for passing an array of data, along with details about what type of elements are contained in the array, the size of the array, and the index of the first element. SafeArrays can be used to pass arrays of Variant data types. The Microsoft VM exposes SafeArrays to the Java developer through the com.ms.com.SafeArray class.
There are some strict rules that must be followed when dealing with SafeArrays to ensure that the data contained is not corrupt or lost. Only the owner of a SafeArray should dispose of the actual SafeArray and its contents. Any method that receives a SafeArray as a parameter should make a copy of the array's contents if it needs to hold the values after the method has returned. The following code demonstrates how a SafeArray containing an array of strings is created and passed to an arbitrary COM object. It also shows how a SafeArray received as a return value can be examined to retrieve its Java data types.
private void processStringArray() { SafeArray safeIn = new SafeArray(Variant.VariantVariant,4); safeIn.fromStringArray(new String[]{"start", "of", "the", "String", "Array"}); Variant retSafe = comObject.processStringArray(safeIn); for (int i=retSafe.toSafeArray().getLBound(); i<retSafe.toSafeArray().getUBound(); i++) System.out.println("index ["+i+"] value ["+retSafe.toSafeArray().getVariant(i)+"]"); }
Structures
JCDWs are also used to represent structures used in C, C++, and COM. Essentially, any Java class that contains an @com.struct or @dll.struct directive is marked as a JCDW, and any non-static fields within the class are exposed as native fields. The @com.structmap or @dll.structmap directive can be used in association with any field to specify exactly how that field is natively represented. The following is an example of a simple structure defined in Java using these directives:
package tutorial; /** @com.struct() */ public class MyStruct { public int foo; public String bar; /** @com.structmap([customMarshaler="tutorial.MyCustomMarshaler", type=CUSTOM]) public tutorial.MyObject; }
In COM, coclasses are never directly used. You must create an instance of a coclass, request one of its implemented interfaces, and use that interface to do the work you want. Method implementations exist in the JCW coclass only to conform to The Java Language Specification by Addison Wesley. To use the coclass in an application, create an instance of it and cast it to an interface as follows:
IWebBrowserApp ie = (IWebBrowserApp) new InternetExplorer();
The following is a simple application that creates an instance of Microsoft® Internet Explorer and writes a message to the browser's status area:
package tutorial.app; import tutorial.ie.*; public class TutorialApp1 { public static void main(String args[]) { IWebBrowserApp ie = new InternetExplorer(); ie.setStatusText("Our simple Java/COM sample."); ie.setVisible(true); } }
Some threading issues need to be considered when using COM objects with AWT components. AWT itself is thread-safe, but as with any other hierarchy of locks, be careful when taking non-AWT locks inside of an AWT lock, such as the global AWT component lock.
COM adds another element you must consider. While inside an AWT lock, a call to a method of a non-Java/COM object may involve a thread switch. If the thread that owns the COM object is blocked on a Java object monitor, the thread won't be servicing incoming COM calls, creating another type of deadlock.
For example, the Microsoft VM's internal support for running applets inside Microsoft® Internet Explorer is careful not to take AWT locks from the browser’s GUI thread. In general, the Microsoft VM posts messages to a worker thread to make the AWT calls on behalf of the client thread. AWT applications that create COM objects should ensure that new instances of the objects are created on a separate thread. This can be accomplished by creating a new java.lang.Thread, which has been declared as a message pump thread using com.ms.com.ComLib.declareMessagePumpThread( ) at the start of the run( ) method, which runs a message pump.
Any COM objects created remotely use Distributed COM (DCOM) to communicate between the local proxy and the remote COM object instance. Adding registry keys to the client computer that specifies the name of the server where the object creation occurs or using the COM API function CoCreateInstanceEx( ) will instantiate a remote instance of a COM object using DCOM. The remote computer is not required to have any registry settings only if the COM object's IDispatch interface is used.
Remote Registry Settings
COM will look under the COM object’s associated application identifier (APPID) registry key for a RemoteServerName setting. If this setting is there, its value is the server computer where the COM object should be created. COM will use this computer and the application's current security settings to create an instance of the COM object on the indicated server, and will return a proxy to the server object.
For a COM object to be usable out-of-process or remotely, the client computer must have registry entries for the COM interfaces to be used specifying the proxy/stub DLL for each interface. This DLL will contain the COM object to use as the proxy object, and performs the work of communicating references, methods calls, and lifetimes with the COM object on the server. If the COM object uses only dispinterfaces with automation types, only the type library is needed by the COM infrastructure to perform all the marshaling of data and method calls between the proxy and the client.
CoCreateInstanceEx( )
DCOM objects can be created by invoking the CoCreateInstanceEx( ) function using J/Direct. This has the advantage of removing the dependency on registry settings from the object creation and provides flexibility in specifying the security credentials used during the creation process.
For information about CoCreateInstanceEx( ), see the COM Functions Reference .
The following is an example of how to use CoCreateInstanceEx( ) from Java:
private IUnknown createInstance() { int HRESULT = 0; com.ms.com.COSERVERINFO ser = new com.ms.com.COSERVERINFO(); ser.pwszName = "MyRemoteMachineName"; ser.pAuthInfo = new com.ms.com.COAUTHINFO(); ser.pAuthInfo.dwAuthnLevel = com.ms.win32.win.RPC_C_AUTHN_LEVEL_CONNECT; ser.pAuthInfo.dwImpersonationLevel = com.ms.win32.win.RPC_C_IMP_LEVEL_IDENTIFY; ser.pAuthInfo.pAuthIdentityData = new com.ms.com.COAUTHIDENTITY(); ser.pAuthInfo.pAuthIdentityData.user = "UserName"; ser.pAuthInfo.pAuthIdentityData.domain = "UsersDomainName"; ser.pAuthInfo.pAuthIdentityData.password= "UsersPassword"; //Create the Multi_Qi structure indicating which interfaces we want. com.ms.com.MULTI_QI mul = new com.ms.com.MULTI_QI(); mul.pIID = com.ms.com.ComLib.getGuidOf(com.ms.com.IUnknown.class); //Perform the actual CreateInstance call. HRESULT = com.ms.win32.Ole32.CoCreateInstanceEx(new com.ms.com._Guid(clsid), null, com.ms.win32.win.CLSCTX_LOCAL_SERVER | com.ms.win32.win.CLSCTX_REMOTE_SERVER, ser, 1, new com.ms.com.MULTI_QI[]{mul}); return(mul[0].pItf); }
The com.ms.com.IUnknown COM object returned from the method can be cast to any JCW representing an interface implemented by the underlying COM object. For example, if the created COM object has an implementation of the IMoniker interface, it can be used like this:
int nHash = ((com.ms.com.IMoniker)COMobject).Hash();
When using CoCreateInstanceEx( ) to create instances of DCOM objects, it is important to note that the security credentials specified in the COAUTHINFO/COAUTHIDENTITY structure in the call apply only to the creation of the object. Security must be set on each interface of the COM object that will be used using the COM function CoSetProxyBlanket( ).
For information about CoSetProxyBlanket( ), see the COM Functions Reference .
This can be done as follows:
/** *@dll.import("OLE32") */ public static native int CoSetProxyBlanket( int pProxy, //Indicates the proxy to set. int dwAuthnSvc, //Authentication service to use. int dwAuthzSvc, //Authorization service to use. String pServerPrincName, //Server principal name to use with // the authentication service. int dwAuthnLevel, //Authentication level to use. int dwImpLevel, //Impersonation level to use. Object pAuthInfo, //Identity of the client. int dwCapabilities //Capability flags. ); /** * The IID for the IDispatch interface. */ public static final com.ms.com._Guid IID_IDispatch = new com.ms.com._Guid((int)0x20400, (short)0x0, (short)0x0, (byte)0xc0,(byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x46); /** * Method for setting security. */ private void setSecurity(COSERVERINFO ser, Object COMobject) { int intPtr = com.ms.com.ComLib.unknownToPtr(COMobject,com.ms.win32.win.IID_IUnknown); int HRESULT = CoSetProxyBlanket(intPtr, ser.pAuthInfo.dwAuthnSvc, ser.pAuthInfo.dwAuthzSvc, ser.pAuthInfo.pwszServerPrincName, ser.pAuthInfo.dwAuthnLevel, ser.pAuthInfo.dwImpersonationLevel, ser.pAuthInfo.pAuthIdentity, ser.pAuthInfo.dwCapabilities); com.ms.com.ComLib.ptrRelease(intPtr); try { // This second call may throw a ClassCastException if the security settings for IUnknown // are insufficient. The user needs to specify a higher level of security credentials // (connect) rather than none for authentication, or impersonate rather than identify. intPtr = com.ms.com.ComLib.unknownToPtr(COMobject, com.ms.win32.win.IID_IDispatch); HRESULT = CoSetProxyBlanket(intPtr, ser.pAuthInfo.dwAuthnSvc, ser.pAuthInfo.dwAuthzSvc, ser.pAuthInfo.pwszServerPrincName, ser.pAuthInfo.dwAuthnLevel, ser.pAuthInfo.dwImpersonationLevel, ser.pAuthInfo.pAuthIdentity, ser.pAuthInfo.dwCapabilities); com.ms.com.ComLib.ptrRelease(intPtr); } catch(ClassCastException c) { com.ms.com.ComLib.release(COMobject); object=null; return; } }
When the previous code is used with the CoCreateInstanceEx( ) snippet, the security credentials used for creating the COM object can also be used for setting credentials on the individual interfaces. You may want to experiment with CoCreateInstanceEx( ) and CoSetProxyBlanket( ) with various security settings to develop a solid understanding of how COM handles establishing security for COM objects and proxies.
The security set on the proxy must be at least as strict as the default security for the created COM object as specified on the server. The DCOMCnfg tool can be used to view the security settings for the local computer, and to adjust the access and launch access control lists (ACLs) for individual COM classes. The MSDN Library and the Platform SDK provide detailed information related to DCOM security settings.
Security
JCW and JCDW classes must be loaded from a fully trusted source to be usable from Java applets or applications. This means that they must be placed on the class path, trusted class library, or in a signed .cab file. The applications or applets that use them don’t need to have any special permission if they are found on the system’s class path. The security check for loading these classes happens during the resolving phase in the class loader. Once the class is loaded, it can be used by any Java program that was loaded from that class loader.
Java/COM developers who are creating COM wrappers should consider the security implications of where they place their class files. To ensure that the wrappers are accessible from only fully trusted Java programs, the classes should be placed in the trusted class library. This means that only local applications and fully trusted applications will be able to use them.
@security
Another method of restricting the accessibility of class files is through use of the @security directive, which controls when the security checks are performed for linking to the class. When class-level @security directive is used with the checkClassLinking=on parameter, the Microsoft VM allows only fully trusted classes to use the COM wrapper. A fully trusted class is any class that has been loaded from the local class path, trusted class library, or from a signed .cab (with low permissions). The @security directive can be placed on any Java class to restrict linking to fully trusted callers, regardless of whether the class uses any J/Direct or COM directives.
@security(checkClassLinking on | off | auto, checkDllCalls on | off)
Parameter | Description |
checkClassLinking= on | off | auto |
Specifies whether the Microsoft VM should allow only trusted classes to use this class (on), or whether any Java class can use this class (off).
Default is auto, which specifies normal Java access checks. |
checkDllCalls= on | off |
If on is specified, the Microsoft VM performs a security stack crawl when a J/Direct method is invoked. If the classes on the stack are untrusted and permissions have not been asserted, the J/Direct call throws a SecurityException.
Off disables the requirement that all callers on the stack be fully trusted. Default is on. |
The following table shows the affected attributes.
Attribute | Scope |
LinkSafe | Class scope. |
LinkUnsafe | Class scope. |
NAT_L_DCTS | Class scope. |