diff --git a/docs/EventCallbacks.htm b/docs/EventCallbacks.htm deleted file mode 100644 index 1d4a23a..0000000 --- a/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/src/com/jacob/activeX/ActiveXInvocationProxy.java b/src/com/jacob/activeX/ActiveXInvocationProxy.java
new file mode 100644
index 0000000..59f8e9f
--- /dev/null
+++ b/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/src/com/jacob/com/DispatchEvents.java b/src/com/jacob/com/DispatchEvents.java
index 9c86a7f..ac7a671 100644
--- a/src/com/jacob/com/DispatchEvents.java
+++ b/src/com/jacob/com/DispatchEvents.java
@@ -108,7 +108,7 @@ public class DispatchEvents extends JacobObject {
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 class DispatchEvents extends JacobObject {
}
/**
+ * 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 @@ public class DispatchEvents extends JacobObject {
*/
public void safeRelease(){
if (mInvocationProxy!=null){
- mInvocationProxy.clearTarget();
+ mInvocationProxy.setTarget(null);
}
mInvocationProxy = null;
super.safeRelease();
diff --git a/src/com/jacob/com/InvocationProxy.java b/src/com/jacob/com/InvocationProxy.java
index 5387186..d2bccec 100644
--- a/src/com/jacob/com/InvocationProxy.java
+++ b/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 @@ import java.lang.reflect.Method;
* 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 class InvocationProxy {
}
/**
- * 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/src/com/jacob/com/InvocationProxyAllVariants.java b/src/com/jacob/com/InvocationProxyAllVariants.java
new file mode 100644
index 0000000..e1b3aad
--- /dev/null
+++ b/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/src/com/jacob/com/Variant.java b/src/com/jacob/com/Variant.java
index fbe2aa0..856b952 100644
--- a/src/com/jacob/com/Variant.java
+++ b/src/com/jacob/com/Variant.java
@@ -361,8 +361,7 @@ public class Variant extends JacobObject {
*
* @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 class Variant extends JacobObject {
* 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/unittest/com/jacob/test/events/ExcelEventTest.java b/unittest/com/jacob/test/events/ExcelEventTest.java
index f9c2c14..d1ce34b 100644
--- a/unittest/com/jacob/test/events/ExcelEventTest.java
+++ b/unittest/com/jacob/test/events/ExcelEventTest.java
@@ -74,13 +74,14 @@ public class ExcelEventTest extends InvocationProxy {
}
/**
- * 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/unittest/com/jacob/test/events/IETest.java b/unittest/com/jacob/test/events/IETest.java
index b166a1f..15fdffd 100644
--- a/unittest/com/jacob/test/events/IETest.java
+++ b/unittest/com/jacob/test/events/IETest.java
@@ -3,8 +3,7 @@ package com.jacob.test.events;
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 @@ class IETestThread extends Thread
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 @@ class IETestThread extends Thread
}
/**
- * 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 class IEEvents
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 class IEEvents
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 class IEEvents
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/unittest/com/jacob/test/events/IETestActiveXProxy.java b/unittest/com/jacob/test/events/IETestActiveXProxy.java
new file mode 100644
index 0000000..6cd90a2
--- /dev/null
+++ b/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/unittest/com/jacob/test/events/WordEventTest.java b/unittest/com/jacob/test/events/WordEventTest.java
index addbedc..c6bb634 100644
--- a/unittest/com/jacob/test/events/WordEventTest.java
+++ b/unittest/com/jacob/test/events/WordEventTest.java
@@ -69,13 +69,13 @@ public class WordEventTest extends InvocationProxy {
}
/**
- * 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);