Microsoft KB Archive/260280

From BetaArchive Wiki

Article ID: 260280

Article Last Modified on 6/14/2006



APPLIES TO

  • Microsoft Java Virtual Machine



This article was previously published under Q260280

SYMPTOMS

A java.lang.ClassCastException is generated when you use a COM object obtained from a com.ms.com.Variant.toObject() or Variant.toDispatch(). Specifically, the Variant, generated through Variant.clone() or Variant.cloneIndirect(), contains a reference to a COM proxy (COM interface pointer marshaled to another thread).

CAUSE

The Microsoft virtual machine's implementation of Variant.clone() and Variant.cloneIndirect() invokes the Win32 VariantCopy and VariantCopyInd functions, respectively, on the caller's thread instead of the Variant's home thread. If the underlying object stored in the Variant is a COM proxy, an attempt to perform a QueryInterface to the COM interface causes a COM error, which is represented as a java.lang.ClassCastException in Java.

An example that demonstrates this problem is included in the "More Information" section.

RESOLUTION

A workaround for this problem is to use ComLib.executeOnContext() to call Variant.clone() or Variant.cloneIndirect() on the Variant's home thread by passing the target Variant as the first parameter to ComLib.executeOnContext().

STATUS

Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.

This problem was corrected in Windows 2000 Service Pack 1.


MORE INFORMATION

Steps to Reproduce Behavior

This code causes the java.lang.ClassCastException that is described in the "Symptoms" section:

import com.ms.com.*;

public class Class1 implements NoAutoMarshaling { 
  static Object m_obj;
  static Variant m_variant;
  static Variant m_cloned_variant;
  
  public static void main (String[] args) { 
    // COM hostable thread
    ComLib.declareMessagePumpThread();
    
    // force creation of Java/COM wrapper for instance
    // of Class1
    m_obj = ComLib.makeProxyRef(new Class1());
    
    // invoke Thread1.run() on new thread
    Thread t1 = new Thread(new Thread1());
    t1.start();
    
    // pump messages for COM
    PumpMessages();
  }
  
  public static class Thread1 implements Runnable { 
    // marshals m_obj to this thread, and stores instance
    // in m_variant
    public void run() { 
      // COM hostable thread
      ComLib.declareMessagePumpThread();
      
      // Force virtual machine to marshal m_obj
      // to this thread
      int nPtr = ComLib.unknownToPtr(m_obj, 
                                     ComLib.IID_IUnknown);
      Object objProxy = 
                       ComLib.ptrToUnknown(IUnknown.class, 
                                           nPtr,
                                           false);
      
      // Store proxy to m_obj in a variant.
      // Note that COM proxy (objProxy) is only valid 
      // on this thread:
      // --performing any COM operation on this proxy 
      // will cause a COM error
      m_variant = new Variant(Variant.VariantObject, 
                              objProxy);
      
      // Invoke Thread2.run() on a new thread
      Thread t2 = new Thread(new Thread2());
      t2.start();
      
      PumpMessages();
    }
  }

  public static class Thread2 implements Runnable { 
    // Calls Variant.clone() on m_variant and performs COM
    // QueryInterface on internal COM object
    public void run() { 
      // COM hostable thread
      ComLib.declareMessagePumpThread();
      try {
        // Clone m_variant
        m_cloned_variant = 
                          (Variant)m_variant.clone();
        
        System.out.println("after clone");
        
        // Get COM proxy to m_obj from cloned 
        // variant
        Object obj = m_cloned_variant.getObject();
        
        System.out.println("after getObject");
        
        // Try to perform COM QueryInterface on 
        // COM proxy. This fails with a 
        // ClassCastException because the COM
        // proxy is being invoked on the wrong 
        // thread
        boolean b = ComLib.supportsInterface(obj, 
                                             ComLib.IID_IDispatch);
        System.out.println("supportsInterface "+b);
      }
      catch (Exception e) {
        e.printStackTrace();
      }
      
      try {
        System.out.print("Please press enter "+
                         "to exit..");
        System.in.read();
      }
      catch (Exception e) {
      }
      System.exit(0);
    }
  }
  
  // standard Win32 message pump
  public static void PumpMessages() { 
    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 );
    }
  }
} 
                

REFERENCES

For additional information about the latest service pack for Windows 2000, click the article number below to view the article in the Microsoft Knowledge Base:

260910 How to Obtain the Latest Windows 2000 Service Pack


For support information about Visual J++ and the SDK for Java, visit the following Microsoft Web site:

Keywords: kbbug kbfix kbjavavm33xxfix kbwin2000sp1fix kbjava KB260280