Visual Basic

Using COM and ActiveX vs. Using a DLL

A DLL is an operating system facility whereas COM is a specification (and some technology) that's been designed from the ground up for building and publishing components and for exporting interfaces. Because COM happens to be implemented largely through the DLL linkage mechanism, you should prefer it to a DLL. However, because language vendors must support it, the DLL linkage mechanism is by far the most common method for connecting objects at run time. Table 11-1 and Table 11-2 help summarize the pros and cons of using COM/ActiveX and a DLL.

Table 11-1 Pros and Cons of Using COM/ActiveX

Pros (subjective) Cons (subjective)
Well implemented, understood technology. Registry and installation dependent. (The integrity and "correctness" of the system Registry determines the ultimate integrityof the application that depends on it.)
Designed from the ground up to be portable across architectures; thus inherently cross-platform. Isolates architectural boundaries.
Part of the operating system. Based on the Windows operating system.
A Microsoft standard. A "wholly-owned" Microsoft standard.
Somewhat versioned by the operating system in the form of the GUID and the Registry. Requires a certain amount of extra savvy on the part of the developer to use with any authority.
Tools such as Visual B++ create COM components by default. Technology is cutting edge and thus often prone to being misapplied or badly applied by inexperienced developers.
Trendy and fashionable. The current fad?

Table 11-2 The Pros and Cons of Using a DLL

Pros (subjective) Cons (subjective)
These days, most languages support building DLLs, so the language choice is vast. Arbitrary interface.
Subject to running afoul of version problems and bad linkage, especially if the DLL doesn't contain a VERSIONINFO resource or if that resource isn't tested by the consuming module. True DLLs cannot be built by Visual Basic (only ActiveX DLLs can be built that export objects).
Skills required to understand the technology are fundamental.

COM COM is a standard (or model) for the interaction of binary objects. An important feature of COM is that objects are precompiled, which means that the implementation language is irrelevant. If you include tokenized code (for example, the p-code in Visual B++ or the bytecode in Java), objects will not necessarily be tied to a specific hardware platform or operating system. COM is also an integration technology. Components can be developed independently and COM provides the standard model for integrating these components. One can think of COM as an enabling technology rather than as a solution in itself.

The major goals of COM are language and vendor independence, location transparency, and reduced version problems.

Language independence When developing components, you should not need to choose a specific language. In COM, any language can be used as long as it allows functions to be called through function pointers. Even interpreted languages are not excluded if the interpretive environment can provide these pointer services on behalf of the application. You can therefore develop COM component software by using languages such as C++, Java, Visual B++, and Visual Basic, Scripting Edition (VBScript).

Location transparency In addition, you should not have to know in which module and location the file system provides a service. This becomes increasingly important when specific services cannot be provided locally, if services are late bound, or if the process that provides these services changes location. Just as hard-coded paths are problematic in applications, hard-coded dependence on the location of services can also cause errors. COM separates clients from servers, which allows servers to be moved without impacting clients.

Vendor independence Consider an example of what happens frequently in one of the most current models for software development. A new vendor provides an ODBC driver for your database that is better than the driver provided by your current vendor. It would be a lot of effort if you had to port all existing code to use the new driver. The effort involved might tempt you to keep the existing, less effective driver. But because COM objects export only interfaces, any new object that exposes the same interfaces as an existing object can transparently replace the existing object. Vendor independence extends not just to external vendors, but also internally to objects that can be easily upgraded without recompiling.

Reduced version problems COM requires immutable interfaces. Although this specification requirement does not entirely eliminate version problems, it greatly reduces the extent of the problem.

DLLs in Java

As a language, Java is pretty cool because it gives you the tools you need to do some pretty serious object-oriented development without some of the complications and overhead of the object-oriented assembly language approach in C++. Visual J++ 6 also has a recognizable IDE and, as far as Visual B++ developers are concerned, follows a familiar development metaphor. For Visual B++ developers, Visual J++ 6 is probably the natural choice for creating DLLs as opposed to, say, Visual C++.

If you wanted to get some Java code to work with Visual B++, one easy way to join the two is by utilizing a DLL as a kind of go-between.

Calling out of Java From time to time, Java, like any professional language, needs the native capabilities provided by the operating system. Indeed, the designers of the Java language realized this need long ago, so since JDK 1.0.2 we've been able to call out of Java and into the operating system. We do this through "native methods," also called the Raw Native Interface (RNI). With Java, this need to go outside is especially acute since built-in functionality is incomplete (due primarily to its portability, a common problem with any cross-platform product or technology). Try implementing F1-Help in a Java program for an example of what I mean. As I've stated, Java's creators anticipated this need and had the foresight to define native methods.

In Java, a method that is modified (flagged) as native is implemented in platform-dependent code, which is typically written in another programming language such as C, C++, or assembly language. The declaration of a native method is followed by a semicolon only, whereas an internal Java method declaration would be followed by a block of implementation code.

In Visual J++, Microsoft has extended the native metaphor and named the result J/Direct. For example, they've made it extremely simple to call DLLs, which are declared via Javadoc comment blocks. In fact, J/Direct is so good at allowing you to call out of Java that Microsoft itself uses the technology in the Windows Foundation Classes (WFC) to give Visual J++ great performance when it comes to creating, and even tearing down, your application's user interface.

In summary, J/Direct allows you to call most any DLL directly (a Windows DLL or your own). The Virtual Machine (VM) also takes care of thorny issues such as mapping of data types for you.

Comparing J/Direct to the RNI J/Direct and native calls are complementary technologies. Using the RNI requires that DLL functions adhere to a strict naming convention, and it requires that the DLL functions work harmoniously with the Java garbage collector. That is, RNI functions must be sure to call GCEnable and GCDisable around code that is time-consuming, code that could yield, code that could block on another thread, and code that blocks while waiting for user input. RNI functions must be specially designed to operate in the Java environment. In return, RNI functions benefit from fast access to Java object internals and the Java class loader.

J/Direct links Java with existing code such as the Win32 API functions, which were not designed to deal with the Java garbage collector and the other subtleties of the Java run-time environment. However, J/Direct automatically calls GCEnable on your behalf so that you can call functions that block or perform user interfaces without having a detrimental effect on the garbage collector. In addition, J/Direct automatically translates common data types such as strings and structures to the forms that C functions normally expect, so you don't need to write as much glue code and wrapper DLLs. The trade-off is that DLL functions cannot access fields and methods of arbitrary Java objects. They can only access fields and methods of objects that are declared using the @dll.struct directive. Another limitation of J/Direct is that RNI functions cannot be invoked from DLL functions that have been called using J/Direct. The reason for this restriction is that garbage collection can run concurrently with your DLL functions. Therefore, any object handles returned by RNI functions or manipulated by DLL functions are inherently unstable. Fortunately, you can use either RNI or J/Direct (or both). The compiler and the Microsoft Win32 VM for Java allow you to mix and match J/Direct and RNI within the same class as your needs dictate.

Why might you want to call DLL code instead of coding purely in Java? Here are a few reasons:

  • A lot of code already exists in C and C++. These languages have been around for a long time, remember, so there's probably a lot of heritage code in existence that could be made available to your Java code via a DLL. If you have such a code base, J/Direct provides an easy way to utilize it that avoids rewriting large bodies of code.
  • Java code compiles into what's called bytecode. One strength and, as it happens, one weakness of p-code-oops, I mean bytecode-is that the file format is very well documented (for the VM writers mainly). Hence it is extremely easy to reverse engineer; indeed, a whole raft of third-party utilities ready to download exist that can disassemble bytecode back into easy-to-read source code. Using J/Direct with a native DLL therefore can provide a more secure approach to deploying sensitive applications.
  • The Sun Abstract Windowing Toolkit (AWT) is the library from which graphical user interface (GUI) applications are constructed-and it sucks. By using J/Direct you can bypass the AWT to use a more traditional, Windows SDK_type, approach to access the operating system's API.

Here's an example of a Java application (this code is the entire thing) showing how easy it is to call a Windows' routine, MessageBox, in this case.

class ShowMsgBox
  {
      public static void main(String args[])
      {
        MessageBox(0, "It worked!",
                  "This is a message box from Java", 0);
            }
     /** @dll.import("USER32") */
     private static native int MessageBox(
                                           int hwnd
                                         , String text
                                         , String title
                                         , int style
                                         );
  }

Simple, eh?

Calling into or out of Java Microsoft's Java VM has added the ability to treat COM objects as special Java classes. COM integration is best for calling APIs that are wrapped in COM objects, or for ActiveX controls. About the only disadvantage to this wrapping method is that there's a translation layer between Java and COM, so the performance won't be as fast as if you were using C++ (and therefore had one less translation layer).

Creating this type of COM object is really simple if you're a Visual B++ developer, which I assume you already are. Here are the steps. Follow along if you have Visual J++ 6 installed.

  1. Select New Project from the File menu. From the New Project dialog box open the Components folder then select COM DLL. Change the Name and Location if you want and click Open.
  2. In the Project Explorer double-click the class that's been inserted for you by default (Class1). This will open Class1.java in the code editor.

    You'll see something like this:

    // Class1.java
      /**
       * This class is designed to be packaged with a COM DLL output format.
       * The class has no standard entry points, other than the constructor.
       * Public methods will be exposed as methods on the default COM interface.
       * @com.register ( clsid=B6E79300-384E-11D2-B30B-008C7360DC1, typelib=B6E79301-384E
      -11D2-B30B-0080C7360DC1 )
       */
      public class Class1
      {
          // TODO: Add additional methods and code here
          /**
           * NOTE: To add auto-registration code, refer to the documentation
           * on the following method
           *   public static void onCOMRegister(boolean unRegister) {}
           */
      }
    
    NOTE
    Notice that when you generate a COM DLL the opening comments specify a class ID, signifying that this class is a COM class. Visual J++ has an option that lets you specify whether this class should be a COM class. Select Project Properties from the Project menu and then click the COM Classes tab. By default the check box for Class1 is checked, meaning make Class1 a COM class.
  3. Add the following boldface code to make your class look like this:
      // Class1.java
      import wfc.ui.*;
      /**
       * This class is designed to be packaged with a COM DLL output format.
       * The class has no standard entry points, other than the constructor.
       * Public methods will be exposed as methods on the default COM interface.
       * @com.register ( clsid=B6E79300-384E-11D2-B30B-008C7360DC1, *typelib=B6E79301-384E
      -11D2-B30B-0080C7360DC1 )
       */
      public class Class1
      {
             // TODO: Add additional methods and code here
             public int Doit()
             {
                 String sText = new String ();
                 sText = "Hello from Java";
                 return MessageBox.show (
                                   sText
                                  ,"Java"
                                  ,MessageBox.DEFBUTTON1      |
                                   MessageBox.ICONEXCLAMATION |
                                   MessageBox.YESNOCANCEL
                                  );
              }
      }
    
  4. Build your project by selecting Build from the Build menu. Select Deploy Solution from the Project menu. You're now finished with Java (for the time being).

You can change wfc.ui.* to awt.ui.* in order to use the real, non_J/Direct Abstract Windowing Toolkit (although you'd want to do this only to build cross-platform).