diff --git a/docs/ReleaseNotes.html b/docs/ReleaseNotes.html index 4856bc5..40902ef 100644 --- a/docs/ReleaseNotes.html +++ b/docs/ReleaseNotes.html @@ -30,6 +30,11 @@ has been added to the DispatchEvents constructors that lets a user provide the location of the OLB or EXE that contains the information required to retrieve the events. +
  • Event handlers can now return a value to calling MS Windows program. + The event handlers must return an objec of type Variant if they wish to return + a value. All + previous event handlers still work with a void return. (This change should be + backwards compatible)
  • @@ -53,7 +58,11 @@ Patches - 1394001 + + + 1386454 + Return values from event callbacks (pre4) + 1394001 Missing variable initialization (pre3) diff --git a/jni/EventProxy.cpp b/jni/EventProxy.cpp index cf91f61..d31a500 100644 --- a/jni/EventProxy.cpp +++ b/jni/EventProxy.cpp @@ -42,9 +42,15 @@ EventProxy::EventProxy(JNIEnv *env, env->GetJavaVM(&jvm); if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} AddRef(); + Connect(env); +} + +void EventProxy::Connect(JNIEnv *env) { HRESULT hr = pCP->Advise(this, &dwEventCookie); if (SUCCEEDED(hr)) { + connected = 1; } else { + connected = 0; ThrowComFail(env, "Advise failed", hr); } } @@ -52,8 +58,8 @@ EventProxy::EventProxy(JNIEnv *env, // unhook myself up as a listener and get rid of delegate EventProxy::~EventProxy() { - pCP->Unadvise(dwEventCookie); JNIEnv *env; + Disconnect(); // attach to the current running thread #ifdef JNI_VERSION_1_2 printf("using version 1.2 API\n"); @@ -74,6 +80,12 @@ EventProxy::~EventProxy() jvm->DetachCurrentThread(); } +void EventProxy::Disconnect() { + if (connected) { + pCP->Unadvise(dwEventCookie); + } +} + // I only support the eventIID interface which was passed in // by the DispatchEvent wrapper who looked it up as the // source object's default source interface @@ -106,6 +118,7 @@ STDMETHODIMP EventProxy::Invoke(DISPID dispID, REFIID riid, const char *eventMethodName = NULL; //Sourceforge report 1394001 JNIEnv *env = NULL; + jobject retObj; // map dispID to jmethodID for(int i=0;iGetObjectClass(javaSinkObj); if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} - jmethodID invokeMethod = env->GetMethodID(javaSinkClass, "invoke", - "(Ljava/lang/String;[Lcom/jacob/com/Variant;)V"); + jmethodID invokeMethod; + invokeMethod = env->GetMethodID(javaSinkClass, "invoke", "(Ljava/lang/String;[Lcom/jacob/com/Variant;)Lcom/jacob/com/Variant;"); if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} jstring eventMethodNameAsString = env->NewStringUTF(eventMethodName); // now do what we need for the variant - jmethodID getVariantMethod = - env->GetMethodID(javaSinkClass, "getVariant", "()Lcom/jacob/com/Variant;"); + jmethodID getVariantMethod = env->GetMethodID(javaSinkClass, "getVariant", "()Lcom/jacob/com/Variant;"); if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} jobject aVariantObj = env->CallObjectMethod(javaSinkObj, getVariantMethod); if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} @@ -169,8 +181,14 @@ STDMETHODIMP EventProxy::Invoke(DISPID dispID, REFIID riid, env->DeleteLocalRef(arg); if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} } - env->CallVoidMethod(javaSinkObj, invokeMethod, - eventMethodNameAsString, varr); + // Set up the return value + jobject ret; + + ret = env->CallObjectMethod(javaSinkObj, invokeMethod, + eventMethodNameAsString, varr); + if (!env->ExceptionOccurred() && ret != NULL) { + VariantCopy(pVarResult, extractVariant(env,ret)); + } if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} // don't need the first variant we created to get the class env->DeleteLocalRef(aVariantObj); @@ -182,6 +200,19 @@ STDMETHODIMP EventProxy::Invoke(DISPID dispID, REFIID riid, jobject arg = env->GetObjectArrayElement(varr, j); VARIANT *java = extractVariant(env, arg); VARIANT *com = &pDispParams->rgvarg[i]; + convertJavaVariant(java, com); + zeroVariant(env, arg); + env->DeleteLocalRef(arg); + } + // End code from Jiffie team that copies parameters back from java to COM + // detach from thread + jvm->DetachCurrentThread(); + return S_OK; + } + return E_NOINTERFACE; +} + +void EventProxy::convertJavaVariant(VARIANT *java, VARIANT *com) { switch (com->vt) { @@ -795,15 +826,6 @@ STDMETHODIMP EventProxy::Invoke(DISPID dispID, REFIID riid, } - } - zeroVariant(env, arg); - env->DeleteLocalRef(arg); } - // End code from Jiffie team that copies parameters back from java to COM - - // detach from thread - jvm->DetachCurrentThread(); - return S_OK; - } - return E_NOINTERFACE; } + diff --git a/jni/EventProxy.h b/jni/EventProxy.h index e92fb87..e0c79ac 100644 --- a/jni/EventProxy.h +++ b/jni/EventProxy.h @@ -37,6 +37,7 @@ class EventProxy : public IDispatch { private: + int connected; LONG m_cRef; // a reference counter CComPtr pCP; // the connection point DWORD dwEventCookie; // connection point cookie @@ -47,6 +48,9 @@ private: CComBSTR *MethName; // Array of method names DISPID *MethID; // Array of method ids, used to match invokations to method names JavaVM *jvm; // The java vm we are running + void convertJavaVariant(VARIANT *java, VARIANT *com); + void Connect(JNIEnv *env); + void Disconnect(); public: // constuct with a global JNI ref to a sink object // to which we will delegate event callbacks @@ -62,7 +66,8 @@ public: // IUnknown methods STDMETHODIMP_(ULONG) AddRef(void) { - return InterlockedIncrement(&m_cRef); + LONG res = InterlockedIncrement(&m_cRef); + return res; } STDMETHODIMP_(ULONG) Release(void) diff --git a/samples/com/jacob/samples/visio/VisioApp.java b/samples/com/jacob/samples/visio/VisioApp.java new file mode 100644 index 0000000..4f28247 --- /dev/null +++ b/samples/com/jacob/samples/visio/VisioApp.java @@ -0,0 +1,85 @@ +package com.jacob.samples.visio; + +import com.jacob.com.*; +import com.jacob.activeX.*; +import java.io.File; + +/** + * Created as part of sourceforge 1386454 to demonstrate returning values in event handlers + * @author miles@rowansoftware.net + * + * This class represents the visio app itself + */ +public class VisioApp extends ActiveXComponent { + + + public VisioApp() throws VisioException { + super("Visio.Application"); + setVisible(false); + } + + /** + * creates a DispatchEvents boject to register o as a listener + * @param o + */ + public void addEventListener(VisioEventListener o) { + DispatchEvents events = new DispatchEvents(this, o); + if (events == null){ + System.out.println("You should never get null back when creating a DispatchEvents object"); + } + } + + + public void open(File f) throws VisioException { + try { + ActiveXComponent documents = new ActiveXComponent(getProperty("Documents").toDispatch()); + Variant[] args = new Variant[1]; + args[0] = new Variant(f.getPath()); + documents.invoke("Open",args); + } catch (Exception e) { + e.printStackTrace(); + throw new VisioException(e); + } + } + + public void save() throws VisioException { + try { + ActiveXComponent document = new ActiveXComponent(getProperty("ActiveDocument").toDispatch()); + document.invoke("Save"); + } catch (Exception e) { + e.printStackTrace(); + throw new VisioException(e); + } + } + + /** + * terminates visio + */ + public void quit() { + System.out.println("Received quit()"); + // there can't be any open documents for this to work + // you'll get a visio error if you don't close them + ActiveXComponent document = new ActiveXComponent(getProperty("ActiveDocument").toDispatch()); + document.invoke("Close"); + invoke("Quit"); + } + + public void export(File f) throws VisioException { + try { + ActiveXComponent document = new ActiveXComponent(getProperty("ActivePage").toDispatch()); + Variant[] args = new Variant[1]; + args[0] = new Variant(f.getPath()); + document.invoke("Export",args); + } catch (Exception e) { + throw new VisioException(e); + } + } + + public void setVisible(boolean b) throws VisioException { + try { + setProperty("Visible",new Variant(b)); + } catch (Exception e) { + throw new VisioException(e); + } + } +} diff --git a/samples/com/jacob/samples/visio/VisioAppFacade.java b/samples/com/jacob/samples/visio/VisioAppFacade.java new file mode 100644 index 0000000..fbd5bac --- /dev/null +++ b/samples/com/jacob/samples/visio/VisioAppFacade.java @@ -0,0 +1,120 @@ +package com.jacob.samples.visio; + +import java.io.*; + +/** + * Created as part of sourceforge 1386454 to demonstrate returning values in event handlers + * @author miles@rowansoftware.net + * + * This singleton isolates the demo app from the Visio instance object so that + * you can't try and send messages to a dead Visio instance after quit() has been + * called. Direct consumption of VisioApp would mean you could quit but would + * still have a handle to the no longer connected application proxy + * + */ +public class VisioAppFacade { + + + private VisioApp app; + private static VisioAppFacade instance; + + public static final String IMAGE_EXT = ".jpg"; + public static final String VISIO_EXT = ".vsd"; + public static final int BUFFER_SIZE = 2048; + + private VisioAppFacade() throws VisioException { + this.app = new VisioApp(); + app.addEventListener(new VisioEventAdapter(app)); + } + + public static VisioAppFacade getInstance() throws VisioException { + if (instance == null) { + instance = new VisioAppFacade(); + } + return instance; + } + + public byte[] createPreview(byte[] visioData) throws VisioException { + byte[] preview; + File tmpFile; + try { + tmpFile = getTempVisioFile(); + OutputStream out = new FileOutputStream(tmpFile); + out.write(visioData); + out.close(); + } catch (IOException ioe) { + throw new VisioException(ioe); + } + preview = createPreview(tmpFile); + tmpFile.delete(); + return preview; + } + + public byte[] createPreview(File visioFile) throws VisioException { + try { + File imageFile; + imageFile = getTempImageFile(); + app.open(visioFile); + app.export(imageFile); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + FileInputStream fin = new FileInputStream(imageFile); + copy(fin, bout); + fin.close(); + imageFile.delete(); + bout.close(); + return bout.toByteArray(); + } catch (IOException ioe) { + throw new VisioException(ioe); + } + } + + private void copy(InputStream in, OutputStream out) throws IOException { + byte[] buff = new byte[BUFFER_SIZE]; + int read; + do { + read = in.read(buff); + if (read > 0) { + out.write(buff,0,read); + } + } while (read > 0); + } + + public byte[] createPreview(InputStream in) throws VisioException { + byte[] preview; + //byte[] buff = new byte[2048]; + //int read = 0; + OutputStream out; + File tmpFile; + + try { + tmpFile = getTempVisioFile(); + out = new FileOutputStream(tmpFile); + copy(in, out); + out.close(); + } catch (IOException ioe) { + throw new VisioException(ioe); + } + + preview = createPreview(tmpFile); + tmpFile.delete(); + return preview; + } + + public void editDiagram(File f) throws VisioException { + app.open(f); + app.setVisible(true); + } + + private File getTempVisioFile() throws IOException { + return File.createTempFile("java",VISIO_EXT); + } + + private File getTempImageFile() throws IOException { + return File.createTempFile("java",IMAGE_EXT); + } + + public void quit() { + app.quit(); + instance = null; + } +} diff --git a/samples/com/jacob/samples/visio/VisioDemo.java b/samples/com/jacob/samples/visio/VisioDemo.java new file mode 100644 index 0000000..151e405 --- /dev/null +++ b/samples/com/jacob/samples/visio/VisioDemo.java @@ -0,0 +1,181 @@ +package com.jacob.samples.visio; +import javax.swing.*; +import javax.swing.filechooser.FileFilter; + +import com.jacob.com.ComThread; + +import java.io.*; +import java.awt.*; +import java.awt.event.*; + +/** + * Created as part of sourceforge 1386454 to demonstrate returning values in event handlers + * @author miles@rowansoftware.net + * + * This file contains the main() that runs the demo + * + * This can be run in Eclipse with options + *
    + * -Djava.library.path=d:/jacob/release -Dcom.jacob.autogc=false
    + * -Dcom.jacob.debug=false
    + * 
    + */ +public class VisioDemo extends JFrame implements ActionListener, WindowListener { + + /** + * Totally dummy value to make Eclipse quit complaining + */ + private static final long serialVersionUID = 1L; + + JButton chooseButton; + JButton openButton; + JPanel buttons; + + ImageIcon theImage; + JLabel theLabel; // the icon on the page is actually this button's icon + + File selectedFile; + /** everyone should get this through getVisio() */ + private VisioAppFacade visioProxy = null; + + // put this up here so it remembers where we were on the last choose + JFileChooser chooser = null; + + + public class VisioFileFilter extends FileFilter { + public boolean accept(File f) { + if (f.isDirectory()){ + return true; + } else { + return (f.getName().toUpperCase().endsWith(".VSD")); + } + } + + public String getDescription() { + return "Visio Drawings"; + } + } + + public VisioDemo() { + super("Visio in Swing POC"); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + buttons = new JPanel(); + getContentPane().setLayout(new BorderLayout()); + chooseButton = new JButton("Choose file to display"); + openButton = new JButton("Open file chosen file in Visio"); + chooseButton.addActionListener(this); + openButton.addActionListener(this); + buttons.add(chooseButton); + buttons.add(openButton); + getContentPane().add(buttons, BorderLayout.SOUTH); + theLabel = new JLabel(""); + getContentPane().add(theLabel, BorderLayout.CENTER); + addWindowListener(this); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + setSize(640,480); + this.setVisible(true); + } + + public static void main(String args[]) throws Exception { + SwingUtilities.invokeLater(new Runnable(){ + public void run(){ + ComThread.InitSTA(); + VisioDemo poc = new VisioDemo(); + ComThread.Release(); + if (poc == null){ + System.out.println("poc== null? That should never happen!"); + } + } + }); + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == chooseButton) { + pickFile(); + } else if (e.getSource() == openButton) { + try { + openFile(); + } catch (Exception ex) { + ex.printStackTrace(); + throw new RuntimeException(ex); + } + } else { + System.out.println("Awesome!"); + } + } + + private void pickFile() { + try { + chooser = new JFileChooser(); + // comment this out if you want it to always go to myDocuments + chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); + chooser.setFileFilter(new VisioFileFilter()); + int returnVal = chooser.showOpenDialog(this); + if(returnVal == JFileChooser.APPROVE_OPTION) { + selectedFile = chooser.getSelectedFile(); + showSelectedFilePreview(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * use this private method instead of initializing on boot up so that instance + * and all listeners are created in this thread (event thread) rather than root thread + * @return + */ + private VisioAppFacade getVisio(){ + if (visioProxy == null){ + try { + visioProxy = VisioAppFacade.getInstance(); + } catch (VisioException ve){ + System.out.println("ailed to openFile()"); + ve.printStackTrace(); + } + } + return visioProxy; + } + private void showSelectedFilePreview() throws VisioException { + if (selectedFile != null) { + byte[] image = getVisio().createPreview(selectedFile); + theImage = new ImageIcon(image); + theLabel.setIcon(theImage); + } + } + + private void openFile() throws VisioException { + try { + getVisio().editDiagram(selectedFile); + showSelectedFilePreview(); + } catch (VisioException ve){ + System.out.println("ailed to openFile()"); + ve.printStackTrace(); + } + + } + + public void windowActivated(WindowEvent e) { + } + + public void windowClosed(WindowEvent e) { + System.out.println("WINDOW CLOSED"); + if (visioProxy != null){ + visioProxy.quit(); + } + } + public void windowClosing(WindowEvent e){ + } + + public void windowDeactivated(WindowEvent e){ + } + + public void windowDeiconified(WindowEvent e){ + } + + public void windowIconified(WindowEvent e){ + System.out.println("Fooboo"); + } + public void windowOpened(WindowEvent e){ + } +} diff --git a/samples/com/jacob/samples/visio/VisioEventAdapter.java b/samples/com/jacob/samples/visio/VisioEventAdapter.java new file mode 100644 index 0000000..77b91a6 --- /dev/null +++ b/samples/com/jacob/samples/visio/VisioEventAdapter.java @@ -0,0 +1,59 @@ +package com.jacob.samples.visio; +import com.jacob.com.*; + +/** + * Created as part of sourceforge 1386454 to demonstrate returning values in event handlers + * @author miles@rowansoftware.net + * + * You can subclass this class and only implement the methods you're interested in + */ +public class VisioEventAdapter implements VisioEventListener { + + VisioApp app = null; + + public VisioEventAdapter(VisioApp pApp){ + app = pApp; + System.out.println("Event listener constructed"); + } + + public void BeforeQuit(Variant[] args){ } + + public void DocumentChanged(Variant[] args){ + System.out.println("documentChanged()"); + } + + public void DocumentCloseCanceled(Variant[] args){ } + + public void DocumentCreated(Variant[] args){ } + + public void DocumentOpened(Variant[] args){ + System.out.println("DocumentOpened()"); + } + + public void DocumentSaved(Variant[] args){ } + + public void DocumentSavedAs(Variant[] args){ } + + public Variant QueryCancelDocumentClose(Variant[] args){ + System.out.println("QueryCancelDocumentClose()"); + return new Variant(false); + } + + /** + * we don't actually let it quit. We block it so + * that we don't have to relaunch when we look at a new document + */ + public Variant QueryCancelQuit(Variant[] args) { + // these may throw VisioException + System.out.println("Saving document, hiding and telling visio not to quit"); + try { + app.save(); + app.setVisible(false); + } catch (VisioException ve){ + System.out.println("ailed to openFile()"); + ve.printStackTrace(); + } + return new Variant(true); + } +} + diff --git a/samples/com/jacob/samples/visio/VisioEventListener.java b/samples/com/jacob/samples/visio/VisioEventListener.java new file mode 100644 index 0000000..3303045 --- /dev/null +++ b/samples/com/jacob/samples/visio/VisioEventListener.java @@ -0,0 +1,33 @@ +package com.jacob.samples.visio; +import com.jacob.com.*; + + + +/** + * Created as part of sourceforge 1386454 to demonstrate returning values in event handlers + * @author miles@rowansoftware.net + * + * There are many more Visio events available. See the Microsoft + * Office SDK documentation. To receive an event, add a method to this interface + * whose name matches the event name and has only one parameter, Variant[]. + * The JACOB library will use reflection to call that method when an event is received. + */ +public interface VisioEventListener { + + public void BeforeQuit(Variant[] args); + + public void DocumentChanged(Variant[] args); + + public void DocumentCloseCanceled(Variant[] args); + + public void DocumentCreated(Variant[] args); + + public void DocumentOpened(Variant[] args); + + public void DocumentSaved(Variant[] args); + + public void DocumentSavedAs(Variant[] args); + + public Variant QueryCancelQuit(Variant[] args); +} + diff --git a/samples/com/jacob/samples/visio/VisioException.java b/samples/com/jacob/samples/visio/VisioException.java new file mode 100644 index 0000000..1a055cb --- /dev/null +++ b/samples/com/jacob/samples/visio/VisioException.java @@ -0,0 +1,22 @@ +package com.jacob.samples.visio; + +/** + * Created as part of sourceforge 1386454 to demonstrate returning values in event handlers + * @author miles@rowansoftware.net + * + * This extends runtime exception so that we can be sloppy and not put catch blocks everywhere + */ +public class VisioException extends Exception { + /** + * Totally dummy value to make Eclipse quit complaining + */ + private static final long serialVersionUID = 1L; + + public VisioException(String msg) { + super(msg); + } + + public VisioException(Throwable cause) { + super(cause); + } +} diff --git a/src/com/jacob/com/InvocationProxy.java b/src/com/jacob/com/InvocationProxy.java index 0ffc4a8..d797a73 100644 --- a/src/com/jacob/com/InvocationProxy.java +++ b/src/com/jacob/com/InvocationProxy.java @@ -28,12 +28,17 @@ import java.lang.reflect.Method; * * DispatchProxy wraps this class around any event handlers * before making the JNI call that sets up the link with EventProxy. - * This means that EventProxy just calls invoke(String,Variant[]) + * 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 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 { @@ -69,7 +74,7 @@ public class InvocationProxy { } // JNI code apparently bypasses this check and could operate against // protected classes. This seems like a security issue... - // mayb eit was because JNI code isn't in a package? + // maybe it was because JNI code isn't in a package? if (!java.lang.reflect.Modifier.isPublic( pTargetObject.getClass().getModifiers())){ throw new IllegalArgumentException( @@ -78,17 +83,24 @@ public class InvocationProxy { } /** - * the method actually invoked by EventProxy.cpp + * 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 + * objects. The method may return a Variant or be a void. Those are the only + * two options that will not blow up. + * * @param methodName name of method in mTargetObject we will invoke * @param targetParameter Variant[] that is the single parameter to the method */ - public void invoke(String methodName, Variant targetParameter[]){ + public Variant invoke(String methodName, Variant targetParameter[]){ + Variant mVariantToBeReturned = null; if (mTargetObject == null){ if (JacobObject.isDebugEnabled()){ JacobObject.debug( - "InvocationProxy: received notification with no target set"); + "InvocationProxy: received notification ("+methodName+") with no target set"); } - return; + // structured programming guidlines say this return should not be up here + return null; } Class targetClass = mTargetObject.getClass(); if (methodName == null){ @@ -103,24 +115,39 @@ public class InvocationProxy { JacobObject.debug("InvocationProxy: trying to invoke "+methodName +" on "+mTargetObject); } - Method targetMethod = targetClass.getMethod(methodName, + 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 - targetMethod.invoke(mTargetObject,new Object[] {targetParameter}); + 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) { e.printStackTrace(); } catch (NoSuchMethodException e) { // this happens whenever the listener doesn't implement all the methods if (JacobObject.isDebugEnabled()){ - JacobObject.debug("InvocationProxy: listener doesn't implement " + 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) { if (JacobObject.isDebugEnabled()){ JacobObject.debug("InvocationProxy: probably tried to access public method on non public class" @@ -130,6 +157,7 @@ public class InvocationProxy { } catch (InvocationTargetException e) { e.printStackTrace(); } + return mVariantToBeReturned; } /** diff --git a/unittest/com/jacob/com/ExcelEventTest.java b/unittest/com/jacob/com/ExcelEventTest.java index 6bfac2d..85b0907 100644 --- a/unittest/com/jacob/com/ExcelEventTest.java +++ b/unittest/com/jacob/com/ExcelEventTest.java @@ -74,10 +74,11 @@ public class ExcelEventTest extends InvocationProxy { } /** - * override the invoke method to loga ll the events + * override the invoke method to log all the events */ - public void invoke(String methodName, Variant targetParameter[]) { + public Variant invoke(String methodName, Variant targetParameter[]) { System.out.println("Received event from Windows program" + methodName); + return null; } } diff --git a/unittest/com/jacob/com/WordEventTest.java b/unittest/com/jacob/com/WordEventTest.java index 9f49a2c..4d07ed7 100644 --- a/unittest/com/jacob/com/WordEventTest.java +++ b/unittest/com/jacob/com/WordEventTest.java @@ -68,8 +68,9 @@ public class WordEventTest extends InvocationProxy { /** * override the invoke method to loga ll the events */ - public void invoke(String methodName, Variant targetParameter[]) { + public Variant invoke(String methodName, Variant targetParameter[]) { System.out.println("Received event from Windows program" + methodName); + return null; } }