diff --git a/jacob/docs/EventCallbacks.htm b/jacob/docs/EventCallbacks.htm deleted file mode 100644 index 1d4a23a..0000000 --- a/jacob/docs/EventCallbacks.htm +++ /dev/null @@ -1,408 +0,0 @@ - - -
- - - - - -Jacob can register Java classes for MS application events -or callbacks. The normal flow for this -is:
- -1) -Application thread creates an instance of the -event handler and registers it with Jacob
- -2) -The application continues on doing other work.
- -3) -Some time later, the MS application takes some -action and initiates the event callback.
- -4) -The Java VM receives the event and spins up a -new thread to handle it.
- -5) -The Jacob jni EventProxy in the dll is called by -the VM.
- -6) -The Jacob jni EventProxy creates Variant objects -to handle the parameters of the passed in event.
- -7) -The Jacob jni EventProxy sends the name of the -callback and the array of Variant objects to the Java InvocationProxy that was registered to catch -events.
- -8) -The Java InvocationProxy uses reflection to map -the event name to a method name with the exact same name.
- -9) -The Java InvocationProxy sends the message to -the registered event handler and returns if the event handler is of type void -(standard behavior).
- -10) The -Java InvocationProxy sends the message to the registered event handler and -returns the Variant that resulted from the call back to the Jacob jni -EventProxy that then returns it to the windows calling program.
- -Swing developers should note that this message comes in -on a thread other than the event thread. -All objects receiving events that require user intervention or drawing -in the UI should use invokeLater() to post requests for actions onto the event -queue. Failure to do so will insure random -failures in the GUI.
- -Java Web Start (JWS) and other launchers can have -additional issues related to the class loader. -The Jacob C++ library uses FindClass() to find the Variant class when -building the parameter list. FindClass() -uses the system class loader which includes only the classes specified at run -time or in the CLASSPATH. Most of the -application classes in this situation live in an alternate set of class loaders -that were created when the launcher located and ran the application -classes. This means that the search for -Variant will fail usually with the silent and immediate termination of the Java -application. The thread classloader -probably can’t be used to try and find the class because this new thread does -not have a classloader associated with it other than the system class -loader. The end result is that the -Variant class needs to be located via other means and that the thread -classloader should be set to be the context class loader of the event handler -class.
- -<b>1.8 and 1.9 behavior</b>
- -The Jacob EventProxy class has been modified (off of the -1.8 tree) so that it takes a two step approach towards fixing these problems.
- -- -
1) -The EventProxy constructor now accepts an extra -object, an instance of the Variant class. -This gives the EventProxy a way to get to the Variant class and thus to -its classloader. All of the callers of the constructor have been modified to -pass a Variant object to the EventProxy without programmer intervention.
- -2) -EventProxy first attempts to locate the Variant -class using FindClass()
- -3) -Failing that, it looks to see if a variant -object had been passed in. If so, it calls class() and goes from there.
- -4) -If all that fails, it logs a message and then -fails in the spectacular fashion of the previous versions.
- -<b>1.10 behavior</b>
- -The Jacob EventProxy class has been modified so that it -takes a different approach towards fixing this problem.
- -1. All -objects that request event notification are now wrapped in a Java -InvocationProxy so that a standard interface is always presented to the JNI -EventProxy object.
- -2. The -EventProxy constructor accepts an Java InvocationProxy as the object that will -receive any callbacks for this set of events.
- -3. The -Java InvocationProxy has a method on it that will return the Variant class that -the EventProxy.
- -Developers can receive call back events in JWS other Java -launching programs without implementing any additional code. They should be aware that their callback -methods may need to set the class loader. If they expect to create any objects.:
- -Public xxx -someHandler(Variant[] foo){
- -Thread.currentThread().setContextClassLoader(
- -this.getClass().getClassLoader());
- -// do -something
- -}
- -There may still be a dual event queue issue in JWS -applications that needs to be looked at.
- -+
+ Public xxx someHandler(Variant[] foo){ + Thread.currentThread().setContextClassLoader( + this.getClass().getClassLoader()); + // do something + } ++There may still be a dual event queue issue in JWS applications that needs to be looked at. + +
+
+Issues with this approach +
+ * Use this exactly like the DispatchEvents class. This class plugs in + * an ActiveXInvocationProxy instead of an InvocationProxy. It is the + * ActiveXInvocationProxy that implements the reflection calls and invoke + * the found java event callbacks. See ActiveXInvocationProxy for details. + * + * + */ +public class ActiveXDispatchEvents extends DispatchEvents { + + /** + * This is the most commonly used constructor. + *
+ * Creates the event callback linkage between the the + * MS program represented by the Dispatch object and the + * Java object that will receive the callback. + * @param sourceOfEvent Dispatch object who's MS app will generate callbacks + * @param eventSink Java object that wants to receive the events + */ + public ActiveXDispatchEvents(Dispatch sourceOfEvent, Object eventSink) { + super(sourceOfEvent, eventSink, null ); + } + + /** + * None of the samples use this constructor. + *
+ * Creates the event callback linkage between the the + * MS program represented by the Dispatch object and the + * Java object that will receive the callback. + * @param sourceOfEvent Dispatch object who's MS app will generate callbacks + * @param eventSink Java object that wants to receive the events + * @param progId ??? + */ + public ActiveXDispatchEvents(Dispatch sourceOfEvent, Object eventSink, String progId) { + super(sourceOfEvent, eventSink, progId, null ); + } + + /** + * Creates the event callback linkage between the the + * MS program represented by the Dispatch object and the + * Java object that will receive the callback. + *
+ * >ActiveXDispatchEvents de = + * new ActiveXDispatchEvents(someDispatch,someEventHAndler, + * "Excel.Application", + * "C:\\Program Files\\Microsoft Office\\OFFICE11\\EXCEL.EXE"); + * + * @param sourceOfEvent Dispatch object who's MS app will generate callbacks + * @param eventSink Java object that wants to receive the events + * @param progId , mandatory if the typelib is specified + * @param typeLib The location of the typelib to use + */ + public ActiveXDispatchEvents(Dispatch sourceOfEvent, Object eventSink, String progId, String typeLib) + { + super(sourceOfEvent, eventSink, progId, typeLib); + } + + /* + * (non-Javadoc) + * @see com.jacob.com.DispatchEvents#getInvocationProxy(java.lang.Object) + */ + protected InvocationProxy getInvocationProxy(Object pTargetObject){ + InvocationProxy newProxy = new ActiveXInvocationProxy(); + newProxy.setTarget(pTargetObject); + return newProxy; + } + +} diff --git a/jacob/src/com/jacob/activeX/ActiveXInvocationProxy.java b/jacob/src/com/jacob/activeX/ActiveXInvocationProxy.java new file mode 100644 index 0000000..59f8e9f --- /dev/null +++ b/jacob/src/com/jacob/activeX/ActiveXInvocationProxy.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 1999-2004 Sourceforge JACOB Project. + * All rights reserved. Originator: Dan Adler (http://danadler.com). + * Get more information about JACOB at http://sourceforge.net/projects/jacob-project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package com.jacob.activeX; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import com.jacob.com.InvocationProxy; +import com.jacob.com.NotImplementedException; +import com.jacob.com.Variant; + +/** + * RELEASE 1.12 EXPERIMENTAL. + *+ * This class that lets event handlers receive events with all java + * objects as parameters. The standard Jacob event methods all accept an array of + * Variant objects. When using this class, you can set up your event methods + * as regular java methods with the correct number of parameters of the correct + * java type. This does NOT work for any event that wishes to accept a call + * back and modify the calling parameters to tell windows what to do. An example + * is when an event lets the receiver cancel the action by setting a boolean flag + * to false. The java objects cannot be modified and their values will not be passed + * back into the originating Variants even if they could be modified. + *
+ * This class acts as a proxy between the windows event callback mechanism and + * the Java classes that are looking for events. It assumes that + * all of the Java classes that are looking for events implement methods with the + * same names as the windows events and that the implemented methods native + * java objects of the type and order that match the windows documentation. + * The methods can return void or a Variant that + * will be returned to the calling layer. All Event methods that will be + * recognized by InvocationProxyAllEvents have the signature + * + *
void eventMethodName(Object,Object...)
+ * or + *Object eventMethodName(Object,Object...)
+ */ +public class ActiveXInvocationProxy extends InvocationProxy { + + /* + * (non-Javadoc) + * @see com.jacob.com.InvocationProxy#invoke(java.lang.String, com.jacob.com.Variant[]) + */ + public Variant invoke(String methodName, Variant targetParameters[]) { + Variant mVariantToBeReturned = null; + if (mTargetObject == null){ + // structured programming guidlines say this return should not be up here + return null; + } + Class targetClass = mTargetObject.getClass(); + if (methodName == null){ + throw new IllegalArgumentException("InvocationProxy: missing method name"); + } + if (targetParameters == null){ + throw new IllegalArgumentException("InvocationProxy: missing Variant parameters"); + } + try { + Method targetMethod; + Object parametersAsJavaObjects[] = getParametersAsJavaObjects(targetParameters); + Class parametersAsJavaClasses[] = getParametersAsJavaClasses(parametersAsJavaObjects); + targetMethod = targetClass.getMethod(methodName, parametersAsJavaClasses); + if (targetMethod != null){ + // protected classes can't be invoked against even if they + // let you grab the method. you could do targetMethod.setAccessible(true); + // but that should be stopped by the security manager + Object mReturnedByInvocation = null; + mReturnedByInvocation = + targetMethod.invoke(mTargetObject,parametersAsJavaObjects); + if (mReturnedByInvocation == null){ + mVariantToBeReturned = null; + } else if (!(mReturnedByInvocation instanceof Variant)){ + mVariantToBeReturned = new Variant(mReturnedByInvocation); + } else { + mVariantToBeReturned = (Variant) mReturnedByInvocation; + } + } + } catch (SecurityException e) { + // what causes this exception? + e.printStackTrace(); + } catch (NoSuchMethodException e) { + // this happens whenever the listener doesn't implement all the methods + } catch (IllegalArgumentException e) { + // we can throw these inside the catch block so need to re-throw it + Exception oneWeShouldToss = new IllegalArgumentException("Unable to map parameters for method "+methodName + + ": "+e.toString()); + oneWeShouldToss.printStackTrace(); + } catch (IllegalAccessException e) { + // can't access the method on the target instance for some reason + e.printStackTrace(); + } catch (InvocationTargetException e) { + // invocation of target method failed + e.printStackTrace(); + } + return mVariantToBeReturned; + + } + + /** + * creates a method signature compatible array of classes from an array of parameters + * @param parametersAsJavaObjects + * @return + */ + private Class[] getParametersAsJavaClasses(Object[] parametersAsJavaObjects) { + if (parametersAsJavaObjects == null){ + throw new IllegalArgumentException("This only works with an array of parameters"); + } + int numParameters = parametersAsJavaObjects.length; + Class parametersAsJavaClasses[] = new Class[numParameters]; + for ( int parameterIndex = 0 ; parameterIndex < numParameters; parameterIndex++){ + Object oneParameterObject = parametersAsJavaObjects[parameterIndex]; + if (oneParameterObject == null){ + parametersAsJavaClasses[parameterIndex] = null; + } else { + Class oneParameterClass = oneParameterObject.getClass(); + parametersAsJavaClasses[parameterIndex] = oneParameterClass; + } + } + return parametersAsJavaClasses; + } + + /** + * converts an array of Variants to their associated Java types + * @param targetParameters + * @return + */ + private Object[] getParametersAsJavaObjects(Variant[] targetParameters) { + if (targetParameters == null){ + throw new IllegalArgumentException("This only works with an array of parameters"); + } + int numParameters = targetParameters.length; + Object parametersAsJavaObjects[] = new Object[numParameters]; + for ( int parameterIndex = 0 ; parameterIndex < numParameters; parameterIndex++){ + Variant oneParameterObject = targetParameters[parameterIndex]; + if (oneParameterObject == null){ + parametersAsJavaObjects[parameterIndex] = null; + } else { + try { + parametersAsJavaObjects[parameterIndex] = oneParameterObject.toJavaObject(); + } catch (NotImplementedException nie){ + throw new IllegalArgumentException("Can't convert parameter "+parameterIndex+ + " type "+oneParameterObject.getvt() + +" to java object: "+nie.getMessage()); + } + } + } + return parametersAsJavaObjects; + } + + +} diff --git a/jacob/src/com/jacob/com/DispatchEvents.java b/jacob/src/com/jacob/com/DispatchEvents.java index 9c86a7f..ac7a671 100644 --- a/jacob/src/com/jacob/com/DispatchEvents.java +++ b/jacob/src/com/jacob/com/DispatchEvents.java @@ -108,7 +108,7 @@ public DispatchEvents(Dispatch sourceOfEvent, Object eventSink, String progId, S if (eventSink instanceof InvocationProxy) { mInvocationProxy = (InvocationProxy) eventSink; } else { - mInvocationProxy = new InvocationProxy(eventSink); + mInvocationProxy = getInvocationProxy(eventSink); } if (mInvocationProxy != null) { init3(sourceOfEvent, mInvocationProxy, progId, typeLib); @@ -121,6 +121,17 @@ public DispatchEvents(Dispatch sourceOfEvent, Object eventSink, String progId, S } /** + * returns an instance of the proxy configured with pTargetObject as its target + * @param pTargetObject + * @return InvocationProxy an instance of the proxy this DispatchEvents will send to the COM layer + */ + protected InvocationProxy getInvocationProxy(Object pTargetObject){ + InvocationProxy newProxy = new InvocationProxyAllVariants(); + newProxy.setTarget(pTargetObject); + return newProxy; + } + + /** * hooks up a connection point proxy by progId * event methods on the sink object will be called * by name with a signature of(Variant[] args) @@ -155,7 +166,7 @@ protected void finalize() { */ public void safeRelease(){ if (mInvocationProxy!=null){ - mInvocationProxy.clearTarget(); + mInvocationProxy.setTarget(null); } mInvocationProxy = null; super.safeRelease(); diff --git a/jacob/src/com/jacob/com/InvocationProxy.java b/jacob/src/com/jacob/com/InvocationProxy.java index 5387186..d2bccec 100644 --- a/jacob/src/com/jacob/com/InvocationProxy.java +++ b/jacob/src/com/jacob/com/InvocationProxy.java @@ -19,9 +19,6 @@ */ package com.jacob.com; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - /** * @version $Id$ * @author joe @@ -31,136 +28,44 @@ * This means that EventProxy.cpp just calls invoke(String,Variant[]) * against the instance of this class. Then this class does * reflection against the event listener to call the actual event methods. - * All Event methods have the signature - * - * void eventMethodName(Variant[])
- * or - *Variant eventMethodName(Variant[])
+ * The event methods can return void or return a Variant. Any + * value returned will be passed back to the calling windows module + * by the Jacob JNI layer. + *+ * * The void returning signature is the standard legacy signature. * The Variant returning signature was added in 1.10 to support event handlers * returning values. * */ -public class InvocationProxy { +public abstract class InvocationProxy { /** * the object we will try and forward to. */ - Object mTargetObject = null; + protected Object mTargetObject = null; /** * dummy constructor for subclasses that don't actually wrap * anything and just want to override the invoke() method */ protected InvocationProxy(){ - - } - - /** - * Constructs an invocation proxy that fronts for an event listener. - * The InvocationProxy will wrap the target object and forward - * any received messages to the wrapped object - * @param pTargetObject - */ - protected InvocationProxy(Object pTargetObject){ super(); - if (JacobObject.isDebugEnabled()){ - JacobObject.debug( - "InvocationProxy: created for object "+pTargetObject); - } - mTargetObject = pTargetObject; - if (mTargetObject == null){ - throw new IllegalArgumentException("InvocationProxy requires a target"); - } - // JNI code apparently bypasses this check and could operate against - // protected classes. This seems like a security issue... - // maybe it was because JNI code isn't in a package? - if (!java.lang.reflect.Modifier.isPublic( - pTargetObject.getClass().getModifiers())){ - throw new IllegalArgumentException( - "InvocationProxy only public classes can receive event notifications"); - } } /** * The method actually invoked by EventProxy.cpp. * The method name is calculated by the underlying JNI code from the MS windows - * Callback function name. The method is assumed to take an array of Variant + * Callback function name. The method is assumed to take an array of Variant * objects. The method may return a Variant or be a void. Those are the only * two options that will not blow up. + *
+ * Subclasses that override this should make sure mTargetObject is not null before processing. * * @param methodName name of method in mTargetObject we will invoke - * @param targetParameter Variant[] that is the single parameter to the method + * @param targetParameters Variant[] that is the single parameter to the method */ - public Variant invoke(String methodName, Variant targetParameter[]){ - Variant mVariantToBeReturned = null; - if (mTargetObject == null){ - if (JacobObject.isDebugEnabled()){ - JacobObject.debug( - "InvocationProxy: received notification ("+methodName+") with no target set"); - } - // structured programming guidlines say this return should not be up here - return null; - } - Class targetClass = mTargetObject.getClass(); - if (methodName == null){ - throw new IllegalArgumentException("InvocationProxy: missing method name"); - } - if (targetParameter == null){ - throw new IllegalArgumentException("InvocationProxy: missing Variant parameters"); - } - try { - if (JacobObject.isDebugEnabled()){ - JacobObject.debug("InvocationProxy: trying to invoke "+methodName - +" on "+mTargetObject); - } - Method targetMethod; - targetMethod = targetClass.getMethod(methodName, - new Class[] {Variant[].class}); - if (targetMethod != null){ - // protected classes can't be invoked against even if they - // let you grab the method. you could do targetMethod.setAccessible(true); - // but that should be stopped by the security manager - Object mReturnedByInvocation = null; - mReturnedByInvocation = - targetMethod.invoke(mTargetObject,new Object[] {targetParameter}); - if (mReturnedByInvocation == null){ - // so we do something in this block - mVariantToBeReturned = null; - } else if (!(mReturnedByInvocation instanceof Variant)){ - throw new IllegalArgumentException( - "InvocationProxy: invokation of target method returned " - +"non-null non-variant object: "+mReturnedByInvocation); - } else { - mVariantToBeReturned = (Variant) mReturnedByInvocation; - } - } - } catch (SecurityException e) { - // what causes this exception? - e.printStackTrace(); - } catch (NoSuchMethodException e) { - // this happens whenever the listener doesn't implement all the methods - if (JacobObject.isDebugEnabled()){ - JacobObject.debug("InvocationProxy: listener ("+mTargetObject+") doesn't implement " - + methodName); - } - } catch (IllegalArgumentException e) { - e.printStackTrace(); - // we can throw these inside the catch block so need to re-throw it - throw e; - } catch (IllegalAccessException e) { - // can't access the method on the target instance for some reason - if (JacobObject.isDebugEnabled()){ - JacobObject.debug("InvocationProxy: probably tried to access public method on non public class" - + methodName); - } - e.printStackTrace(); - } catch (InvocationTargetException e) { - // invocation of target method failed - e.printStackTrace(); - } - return mVariantToBeReturned; - } + public abstract Variant invoke(String methodName, Variant targetParameters[]); /** * used by EventProxy.cpp to create variant objects in the right thread @@ -171,10 +76,26 @@ public Variant getVariant(){ } /** - * helper method used by DispatchEvents to help clean up and reduce references - * - */ - protected void clearTarget(){ - mTargetObject = null; - } + * Sets the target for this InvocationProxy. + * @param pTargetObject + * @throws IllegalArgumentException if target is not publicly accessable + */ + public void setTarget(Object pTargetObject){ + if (JacobObject.isDebugEnabled()){ + JacobObject.debug( + "InvocationProxy: setting target "+pTargetObject); + } + if (pTargetObject != null){ + // JNI code apparently bypasses this check and could operate against + // protected classes. This seems like a security issue... + // maybe it was because JNI code isn't in a package? + if (!java.lang.reflect.Modifier.isPublic( + pTargetObject.getClass().getModifiers())){ + throw new IllegalArgumentException( + "InvocationProxy only public classes can receive event notifications"); + } + } + mTargetObject = pTargetObject; + } + } diff --git a/jacob/src/com/jacob/com/InvocationProxyAllVariants.java b/jacob/src/com/jacob/com/InvocationProxyAllVariants.java new file mode 100644 index 0000000..e1b3aad --- /dev/null +++ b/jacob/src/com/jacob/com/InvocationProxyAllVariants.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1999-2004 Sourceforge JACOB Project. + * All rights reserved. Originator: Dan Adler (http://danadler.com). + * Get more information about JACOB at http://sourceforge.net/projects/jacob-project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +package com.jacob.com; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * This class acts as a proxy between the windows event callback mechanism and + * the Java classes that are looking for events. It assumes that + * all of the Java classes that are looking for events implement methods with the + * same names as the windows events and that the implemented methods accept an + * array of variant objects. The methods can return void or a Variant that + * will be returned to the calling layer. All Event methods that will be + * recognized by InvocationProxyAllEvents have the signature + * + *
void eventMethodName(Variant[])
+ * or + *Variant eventMethodName(Variant[])
+ */ +public class InvocationProxyAllVariants extends InvocationProxy { + + /* + * (non-Javadoc) + * @see com.jacob.com.InvocationProxy#invoke(java.lang.String, com.jacob.com.Variant[]) + */ + public Variant invoke(String methodName, Variant targetParameters[]) { + Variant mVariantToBeReturned = null; + if (mTargetObject == null){ + if (JacobObject.isDebugEnabled()){ + JacobObject.debug( + "InvocationProxy: received notification ("+methodName+") with no target set"); + } + // structured programming guidlines say this return should not be up here + return null; + } + Class targetClass = mTargetObject.getClass(); + if (methodName == null){ + throw new IllegalArgumentException("InvocationProxy: missing method name"); + } + if (targetParameters == null){ + throw new IllegalArgumentException("InvocationProxy: missing Variant parameters"); + } + try { + if (JacobObject.isDebugEnabled()){ + JacobObject.debug("InvocationProxy: trying to invoke "+methodName + +" on "+mTargetObject); + } + Method targetMethod; + targetMethod = targetClass.getMethod(methodName, + new Class[] {Variant[].class}); + if (targetMethod != null){ + // protected classes can't be invoked against even if they + // let you grab the method. you could do targetMethod.setAccessible(true); + // but that should be stopped by the security manager + Object mReturnedByInvocation = null; + mReturnedByInvocation = + targetMethod.invoke(mTargetObject,new Object[] {targetParameters}); + if (mReturnedByInvocation == null){ + mVariantToBeReturned = null; + } else if (!(mReturnedByInvocation instanceof Variant)){ + // could try and convert to Variant here. + throw new IllegalArgumentException( + "InvocationProxy: invokation of target method returned " + +"non-null non-variant object: "+mReturnedByInvocation); + } else { + mVariantToBeReturned = (Variant) mReturnedByInvocation; + } + } + } catch (SecurityException e) { + // what causes this exception? + e.printStackTrace(); + } catch (NoSuchMethodException e) { + // this happens whenever the listener doesn't implement all the methods + if (JacobObject.isDebugEnabled()){ + JacobObject.debug("InvocationProxy: listener ("+mTargetObject+") doesn't implement " + + methodName); + } + } catch (IllegalArgumentException e) { + e.printStackTrace(); + // we can throw these inside the catch block so need to re-throw it + throw e; + } catch (IllegalAccessException e) { + // can't access the method on the target instance for some reason + if (JacobObject.isDebugEnabled()){ + JacobObject.debug("InvocationProxy: probably tried to access public method on non public class" + + methodName); + } + e.printStackTrace(); + } catch (InvocationTargetException e) { + // invocation of target method failed + e.printStackTrace(); + } + return mVariantToBeReturned; + + } +} diff --git a/jacob/src/com/jacob/com/Variant.java b/jacob/src/com/jacob/com/Variant.java index fbe2aa0..856b952 100644 --- a/jacob/src/com/jacob/com/Variant.java +++ b/jacob/src/com/jacob/com/Variant.java @@ -361,8 +361,7 @@ public void putStringRef(String in){ * * @throws IllegalArgumentException * if inVariant = null - * @param in - * a variant that is to be referenced by this variant + * @param inVariant A variant that is to be referenced by this variant */ public void putVariant(Variant inVariant) { if (inVariant == null) { @@ -387,7 +386,7 @@ public void putVariant(Variant inVariant) { * Used to get the value from a windows type of VT_VARIANT * or a jacob Variant type of VariantVariant. * Added 1.12 pre 6 - VT_VARIANT support is at an alpha level - * @returns Object a java Object that represents the content of the enclosed Variant + * @return Object a java Object that represents the content of the enclosed Variant */ public Object getVariant() { if ((this.getvt() & VariantVariant) == VariantVariant diff --git a/jacob/unittest/com/jacob/test/events/ExcelEventTest.java b/jacob/unittest/com/jacob/test/events/ExcelEventTest.java index f9c2c14..d1ce34b 100644 --- a/jacob/unittest/com/jacob/test/events/ExcelEventTest.java +++ b/jacob/unittest/com/jacob/test/events/ExcelEventTest.java @@ -74,13 +74,14 @@ public static void main(String args[]) { } /** - * dummy consturctor to create an InvocationProxy that wraps nothing + * Constructor so we can create an instance that implements invoke() */ public ExcelEventTest() { } /** - * override the invoke method to log all the events + * Override the invoke method to log all the events so that we don't have to + * implement all of the specific events. */ public Variant invoke(String methodName, Variant targetParameter[]) { System.out.println("Received event from Windows program" + methodName); diff --git a/jacob/unittest/com/jacob/test/events/IETest.java b/jacob/unittest/com/jacob/test/events/IETest.java index b166a1f..15fdffd 100644 --- a/jacob/unittest/com/jacob/test/events/IETest.java +++ b/jacob/unittest/com/jacob/test/events/IETest.java @@ -3,8 +3,7 @@ import com.jacob.com.*; import com.jacob.activeX.*; /** - * This test runs fine against jdk.1.5.0_05 and kills the VM - * under any variant of 1.4. + * This test runs fine against jdk 1.4 and 1.5 * * This demonstrates the new event handling code in jacob 1.7 * This example will open up IE and print out some of the events @@ -66,7 +65,7 @@ public void run() System.out.println("IETestThread: About to hookup event listener"); IEEvents ieE = new IEEvents(); - new DispatchEvents((Dispatch) ie, ieE,"InternetExplorer.Application.1"); + new DispatchEvents(ie, ieE,"InternetExplorer.Application.1"); System.out.println("IETestThread: Did hookup event listener"); /// why is this here? Was there some other code here in the past? Variant optional = new Variant(); @@ -110,7 +109,8 @@ public void run() } /** - * the events class must be publicly accessable for reflection to work + * The events class must be publicly accessable for reflection to work. + * The list of available events is located at http://msdn2.microsoft.com/en-us/library/aa768280.aspx */ public class IEEvents { @@ -134,6 +134,10 @@ public void DownloadComplete(Variant[] args) { System.out.println("IEEvents Received ("+Thread.currentThread().getName()+"): DownloadComplete "+args.length); } + public void NavigateError(Variant[] args) { + System.out.println("IEEvents Received ("+Thread.currentThread().getName()+"): NavigateError "+args.length); + } + public void NavigateComplete2(Variant[] args) { System.out.println("IEEvents Received ("+Thread.currentThread().getName()+"): NavigateComplete "+args.length); } @@ -179,6 +183,10 @@ public void PropertyChange(Variant[] args) { System.out.println("IEEvents Received ("+Thread.currentThread().getName()+"): PropertyChange "+args.length); } + public void SetSecureLockIcon(Variant[] args) { + System.out.println("IEEvents Received ("+Thread.currentThread().getName()+"): setSecureLockIcon "+args.length); + } + public void StatusTextChange(Variant[] args) { System.out.println("IEEvents Received ("+Thread.currentThread().getName()+"): StatusTextChange "+args.length); } @@ -186,6 +194,10 @@ public void StatusTextChange(Variant[] args) { public void TitleChange(Variant[] args) { System.out.println("IEEvents Received ("+Thread.currentThread().getName()+"): TitleChange "+args.length); } + + public void WindowClosing(Variant[] args) { + System.out.println("IEEvents Received ("+Thread.currentThread().getName()+"): WindowClosing "+args.length); + } } } diff --git a/jacob/unittest/com/jacob/test/events/IETestActiveXProxy.java b/jacob/unittest/com/jacob/test/events/IETestActiveXProxy.java new file mode 100644 index 0000000..6cd90a2 --- /dev/null +++ b/jacob/unittest/com/jacob/test/events/IETestActiveXProxy.java @@ -0,0 +1,207 @@ +package com.jacob.test.events; + +import com.jacob.activeX.ActiveXComponent; +import com.jacob.activeX.ActiveXDispatchEvents; +import com.jacob.com.ComThread; +import com.jacob.com.Dispatch; +import com.jacob.com.Variant; +/** + * This test runs fine against jdk 1.4 and 1.5 + * + * This demonstrates the new event handling code in jacob 1.7 + * This example will open up IE and print out some of the events + * it listens to as it havigates to web sites. + * contributed by Niels Olof Bouvin mailto:n.o.bouvin@daimi.au.dk + * and Henning Jae jehoej@daimi.au.dk + *+ * May need to run with some command line options (including from inside Eclipse). + * Look in the docs area at the Jacob usage document for command line options. + */ + +class IETestActiveXProxy +{ + public static void main(String[] args) + { + // this line starts the pump but it runs fine without it + ComThread.startMainSTA(); + // remove this line and it dies + ///ComThread.InitMTA(true); + IETestActiveProxyThread aThread = new IETestActiveProxyThread(); + aThread.start(); + while (aThread.isAlive()){ + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // doen with the sleep + //e.printStackTrace(); + } + } + System.out.println("Main: Thread quit, about to quit main sta in thread " + +Thread.currentThread().getName()); + // this line only does someting if startMainSTA() was called + ComThread.quitMainSTA(); + System.out.println("Main: did quit main sta in thread " + +Thread.currentThread().getName()); + } +} + +class IETestActiveProxyThread extends Thread +{ + public static boolean quitHandled = false; + + public IETestActiveProxyThread(){ + super(); + } + + public void run() + { + // this used to be 5 seconds but sourceforge is slow + int delay = 5000; // msec + // paired with statement below that blows up + ComThread.InitMTA(); + ActiveXComponent ie = new ActiveXComponent("InternetExplorer.Application"); + try { + Dispatch.put(ie, "Visible", new Variant(true)); + Dispatch.put(ie, "AddressBar", new Variant(true)); + System.out.println("IETestActiveProxyThread: " + Dispatch.get(ie, "Path")); + Dispatch.put(ie, "StatusText", new Variant("My Status Text")); + + System.out.println("IETestActiveProxyThread: About to hookup event listener"); + IEEventsActiveProxy ieE = new IEEventsActiveProxy(); + new ActiveXDispatchEvents(ie, ieE,"InternetExplorer.Application.1"); + System.out.println("IETestActiveProxyThread: Did hookup event listener"); + /// why is this here? Was there some other code here in the past? + Variant optional = new Variant(); + optional.putNoParam(); + + System.out.println("IETestActiveProxyThread: About to call navigate to sourceforge"); + Dispatch.call(ie, "Navigate", new Variant("http://sourceforge.net/projects/jacob-project")); + System.out.println("IETestActiveProxyThread: Did call navigate to sourceforge"); + try { Thread.sleep(delay); } catch (Exception e) {} + System.out.println("IETestActiveProxyThread: About to call navigate to yahoo"); + Dispatch.call(ie, "Navigate", new Variant("http://groups.yahoo.com/group/jacob-project")); + System.out.println("IETestActiveProxyThread: Did call navigate to yahoo"); + try { Thread.sleep(delay); } catch (Exception e) {} + } catch (Exception e) { + e.printStackTrace(); + } catch (Throwable re){ + re.printStackTrace(); + } finally { + System.out.println("IETestActiveProxyThread: About to send Quit"); + ie.invoke("Quit", new Variant[] {}); + System.out.println("IETestActiveProxyThread: Did send Quit"); + } + // this blows up when it tries to release a DispatchEvents object + // I think this is because there is still one event we should get back + // "OnQuit" that will came after we have released the thread pool + // this is probably messed up because DispatchEvent object will have been + // freed before the callback + // commenting out ie.invoke(quit...) causes this to work without error + // this code tries to wait until the quit has been handled but that doesn't work + System.out.println("IETestActiveProxyThread: Waiting until we've received the quit callback"); + while (!quitHandled){ + try { Thread.sleep(delay/5);} catch (InterruptedException e) {} + } + System.out.println("IETestActiveProxyThread: Received the quit callback"); + // wait a little while for it to end + //try {Thread.sleep(delay); } catch (InterruptedException e) {} + System.out.println("IETestActiveProxyThread: about to call ComThread.Release in thread " + + Thread.currentThread().getName()); + + ComThread.Release(); + } + +/** + * The events class must be publicly accessable for reflection to work. + * The list of available events is located at http://msdn2.microsoft.com/en-us/library/aa768280.aspx + */ +public class IEEventsActiveProxy +{ + public void BeforeNavigate2(Dispatch pDisp, String url, Integer flags, + String targetFrameName, Variant postData, String headers, Boolean cancel) { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): BeforeNavigate2 "+url); + } + + public void CommandStateChange(Integer command, Boolean enable) { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): CommandStateChange "+command); + } + + public void DocumentComplete(Dispatch pDisp, String url) { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): DocumentComplete "+url); + } + + public void DownloadBegin() { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): DownloadBegin "); + } + + public void DownloadComplete() { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): DownloadComplete "); + } + + public void NavigateComplete2(Dispatch pDisp, String url) { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): NavigateComplete "+url); + } + + public void NavigateError(Dispatch pDispatch, String url, String targetFrameName, Integer statusCode, Boolean Cancel) { + System.out.println("IEEvents Received ("+Thread.currentThread().getName()+"): NavigateError "+statusCode); + } + + public void NewWindow2(Dispatch pDisp, Boolean cancel) { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): NewWindow2 "+pDisp); + } + + public void OnFullScreen(Boolean fullScreen) { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): OnFullScreen "+fullScreen); + } + + public void OnMenuBar(Boolean menuBar) { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): OnMenuBar "+menuBar); + } + + public void OnQuit() { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): OnQuit "); + IETestActiveProxyThread.quitHandled = true; + } + + public void OnStatusBar(Boolean statusBar) { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): OnStatusBar "+statusBar); + } + + public void OnTheaterMode(Boolean theaterMode) { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): OnTheaterMode "+theaterMode); + } + + public void OnToolBar(Boolean onToolBar) { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): OnToolBar "+onToolBar); + } + + public void OnVisible(Boolean onVisible) { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): onVisible "+ onVisible); + } + + public void ProgressChange() { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): ProgressChange "); + } + + public void PropertyChange() { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): PropertyChange "); + } + + public void SetSecureLockIcon(Integer secureLockIcon) { + System.out.println("IEEvents Received ("+Thread.currentThread().getName()+"): setSecureLockIcon "+secureLockIcon); + } + + public void StatusTextChange() { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): StatusTextChange "); + } + + public void TitleChange() { + System.out.println("IEEventsActiveProxy Received ("+Thread.currentThread().getName()+"): TitleChange "); + } + + public void WindowClosing(Boolean isChildWindow) { + System.out.println("IEEvents Received ("+Thread.currentThread().getName()+"): WindowClosing "+isChildWindow); + } +} + +} diff --git a/jacob/unittest/com/jacob/test/events/WordEventTest.java b/jacob/unittest/com/jacob/test/events/WordEventTest.java index addbedc..c6bb634 100644 --- a/jacob/unittest/com/jacob/test/events/WordEventTest.java +++ b/jacob/unittest/com/jacob/test/events/WordEventTest.java @@ -69,13 +69,13 @@ public static void main(String args[]) { } /** - * dummy consturctor to create an InvocationProxy that wraps nothing + * Constructor so we can create an instance that implements invoke() */ public WordEventTest() { } /** - * override the invoke method to loga ll the events + * override the invoke() method to log all the events without writing a bunch of code */ public Variant invoke(String methodName, Variant targetParameter[]) { System.out.println("Received event from Windows program" + methodName);