From 2406511e02f8996d820672e22ac63e6e078e1534 Mon Sep 17 00:00:00 2001 From: clay_shooter <> Date: Sun, 6 Jul 2008 12:39:08 +0000 Subject: [PATCH] Sourceforge 2011706 windows memory error when exiting program with call back proxies still hooked up. --- jacob/docs/ReleaseNotes.html | 41 ++++ jacob/docs/UsingJacob.html | 4 +- jacob/jni/EventProxy.cpp | 25 +-- .../com/jacob/test/events/IETest.java | 185 ++++++++++-------- 4 files changed, 162 insertions(+), 93 deletions(-) diff --git a/jacob/docs/ReleaseNotes.html b/jacob/docs/ReleaseNotes.html index 993a66c..ac39265 100644 --- a/jacob/docs/ReleaseNotes.html +++ b/jacob/docs/ReleaseNotes.html @@ -1,5 +1,46 @@ + +

JACOB 1.14.2

+

Tracked Changes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bugs
2011706Fixed windows memory corruption unhooking + call back proxies
  
Patches
  
Feature Requests
  
Known Issues
+ +

JACOB 1.14.1

Tracked Changes

diff --git a/jacob/docs/UsingJacob.html b/jacob/docs/UsingJacob.html index 9958bc8..8dd3491 100644 --- a/jacob/docs/UsingJacob.html +++ b/jacob/docs/UsingJacob.html @@ -73,8 +73,8 @@

Classloader issues

Microsoft Visual C++ library dependencies.

Jacob 1.13 is built with VC++ 2005 that creates a dependency on msvcr80.dll. Windows XP and later seem to already include the necessary components. - NT/2000 and Server/2003 require that you download the Visual C redistributable package, - vcredist_x86.exe from the microsoft web site. + NT/2000 and Server/2003 require that you download the Visual C 2005 redistributable + package, vcredist_x86.exe from the microsoft web site. Microsoft has a download available that supplies the necessary components. It is distributed as a redistributable package.

diff --git a/jacob/jni/EventProxy.cpp b/jacob/jni/EventProxy.cpp index 8d00871..496e8fc 100644 --- a/jacob/jni/EventProxy.cpp +++ b/jacob/jni/EventProxy.cpp @@ -60,33 +60,36 @@ EventProxy::~EventProxy() JNIEnv *env; Disconnect(); jint vmConnectionStatus = JNI_EVERSION ; - + jint attachReturnStatus = -1; // AttachCurrentThread return status.. negative numbers are failure return codes. + // attach to the current running thread -- JDK 1.4 jni.h has two param cover for 3 param call vmConnectionStatus = jvm->GetEnv((void **)&env, JNI_VERSION_1_2); - if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} + if ((env != NULL)&& env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} if (vmConnectionStatus == JNI_EDETACHED){ //printf("Unhook: Attaching to current thread using JNI Version 1.2 (%d)\n",vmConnectionStatus); JavaVMAttachArgs attachmentArgs; attachmentArgs.version = JNI_VERSION_1_2; attachmentArgs.name = NULL; attachmentArgs.group = NULL; - jvm->AttachCurrentThread((void **)&env, &attachmentArgs); - if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} + attachReturnStatus = jvm->AttachCurrentThread((void **)&env, &attachmentArgs); + if ((env != NULL) && env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} } else { - // should really look for JNI_OK versus an error - // started method hooked so no need to attach again + // should really look for JNI_OK versus an error because it could have been JNI_EVERSION + // started method with thread hooked to VM so no need to attach again //printf("Unhook: No need to attach because already attached %d\n",vmConnectionStatus); } - - env->DeleteGlobalRef(javaSinkObj); - if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} + // we should always have an env by this point but lets be paranoid and check + if (env != NULL){ + env->DeleteGlobalRef(javaSinkObj); + if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear();} + } if (MethNum) { delete [] MethName; delete [] MethID; } - // detach from thread - if (vmConnectionStatus == JNI_EDETACHED){ + // detach from thread only if we attached to it in this function + if (attachReturnStatus == 0){ jvm->DetachCurrentThread(); //printf("Unhook: Detached\n"); } else { diff --git a/jacob/unittest/com/jacob/test/events/IETest.java b/jacob/unittest/com/jacob/test/events/IETest.java index c3d4230..e4560a9 100644 --- a/jacob/unittest/com/jacob/test/events/IETest.java +++ b/jacob/unittest/com/jacob/test/events/IETest.java @@ -11,7 +11,7 @@ * 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 + * open up IE and print out some of the events it listens to as it navigates to * web sites. contributed by Niels Olof Bouvin mailto:n.o.bouvin@daimi.au.dk and * Henning Jae jehoej@daimi.au.dk *

@@ -22,28 +22,49 @@ public class IETest extends BaseTestCase { + /** + * well known address we can navigate to + */ + private String testUrls[] = { + "http://sourceforge.net/projects/jacob-project", + "http://www.google.com" }; + + /** + * runs the IE test and feeds it commands + */ + public void testRunIECleanly() { + runTheTest(true, testUrls); + } + /** * runs the IE test and feeds it commands */ - public void testRunIE() { + public void testRunIETerminateWithoutWait() { + runTheTest(false, testUrls); + } + + /** + * The actual work of running the test. + * + * @param waitForQuit + * @param urls + */ + private void runTheTest(boolean waitForQuit, String[] urls) { // this line starts the pump but it runs fine without it ComThread.startMainSTA(); - // remove this line and it dies - // /ComThread.InitMTA(true); - IETestThread aThread = new IETestThread(); + // Run the test in a thread. Lets us test running out of "main" thread + IETestThread aThread = new IETestThread(waitForQuit, urls); aThread.start(); while (aThread.isAlive()) { try { - Thread.sleep(1000); + Thread.sleep(250); } catch (InterruptedException e) { - // doen with the sleep - // e.printStackTrace(); + // done with the sleep } } - 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 + System.out.println("Main: Thread quit, about to quitMainSTA in thread " + + Thread.currentThread().getName()); + // this line only does something if startMainSTA() was called ComThread.quitMainSTA(); System.out.println("Main: did quit main sta in thread " + Thread.currentThread().getName()); @@ -59,6 +80,17 @@ class IETestThread extends Thread { /** flag that says we got a quit message from IE */ public static boolean quitHandled = false; + /** + * determines if we wait until last quit call back received before + * terminating + */ + private static boolean waitUntilReceivedQuitCallback = true; + + /** + * the places we should navigate to + */ + private static String[] targets = null; + /** * holds any caught exception so the main/test case can see them */ @@ -66,15 +98,25 @@ class IETestThread extends Thread { /** * constructor for the test thread + * + * @param beNeat + * should we wait until quit received + * @param urls + * the web pages we will navigate to */ - public IETestThread() { + public IETestThread(boolean beNeat, String urls[]) { super(); + waitUntilReceivedQuitCallback = beNeat; + targets = urls; } + /** + * Run through the addresses passed in via the constructor + */ public void run() { - // this used to be 5 seconds but sourceforge is slow - int delay = 5000; // msec - // paired with statement below that blows up + // pick a time that lets sourceforge respond (in msec) + int delay = 3000; + // pre-1.14 paired with statement below that blows up ComThread.InitMTA(); ActiveXComponent ie = new ActiveXComponent( "InternetExplorer.Application"); @@ -88,27 +130,16 @@ public void run() { IEEvents ieE = new IEEvents(); 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(); - optional.putNoParam(); - System.out - .println("IETestThread: About to call navigate to sourceforge"); - Dispatch.call(ie, "Navigate", new Variant( - "http://sourceforge.net/projects/jacob-project")); - System.out - .println("IETestThread: Did call navigate to sourceforge"); - try { - Thread.sleep(delay); - } catch (Exception e) { - } - System.out.println("IETestThread: About to call navigate to yahoo"); - Dispatch.call(ie, "Navigate", new Variant( - "http://groups.yahoo.com/group/jacob-project")); - System.out.println("IETestThread: Did call navigate to yahoo"); - try { - Thread.sleep(delay); - } catch (Exception e) { + for (String url : targets) { + System.out.println("IETestThread: About to call navigate to " + + url); + Dispatch.call(ie, "Navigate", new Variant(url)); + System.out.println("IETestThread: Did call navigate to " + url); + try { + Thread.sleep(delay); + } catch (Exception e) { + } } } catch (Exception e) { threadFailedWithException = e; @@ -121,35 +152,29 @@ public void run() { ie.invoke("Quit", new Variant[] {}); System.out.println("IETestThread: 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("IETestThread: Waiting until we've received the quit callback"); - while (!quitHandled) { - try { - Thread.sleep(delay / 5); - } catch (InterruptedException e) { + // a value is set to false if we try to crash VM by leaving before + // callbacks all received + if (waitUntilReceivedQuitCallback) { + System.out + .println("IETestThread: Waiting until we've received quit callback"); + // wait a little while for it to end + while (!quitHandled) { + try { + Thread.sleep(delay / 10); + } catch (InterruptedException e) { + } } + System.out.println("IETestThread: Received the OnQuit callback"); + } else { + System.out.println("IETestThread: Not waiting for OnQuit callback"); } - System.out.println("IETestThread: Received the quit callback"); - // wait a little while for it to end - // try {Thread.sleep(delay); } catch (InterruptedException e) {} - System.out - .println("IETestThread: about to call ComThread.Release in thread " - + Thread.currentThread().getName()); - + System.out.println("IETestThread: Calling ComThread.Release in thread " + + Thread.currentThread().getName()); ComThread.Release(); } /** - * The events class must be publicly accessable for reflection to work. The + * The events class must be publicly accessible for reflection to work. The * list of available events is located at * http://msdn2.microsoft.com/en-us/library/aa768280.aspx */ @@ -163,7 +188,7 @@ public class IEEvents { public void BeforeNavigate2(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): BeforeNavigate2 " - + args.length); + + args.length + " parameters"); } /** @@ -175,7 +200,7 @@ public void BeforeNavigate2(Variant[] args) { public void CommandStateChange(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() - + "): CommandStateChange " + args.length); + + "): CommandStateChange " + args.length + " parameters"); } /** @@ -187,7 +212,7 @@ public void CommandStateChange(Variant[] args) { public void DocumentComplete(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): DocumentComplete " - + args.length); + + args.length + " parameters"); } /** @@ -199,7 +224,7 @@ public void DocumentComplete(Variant[] args) { public void DownloadBegin(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): DownloadBegin " - + args.length); + + args.length + " parameters"); } /** @@ -211,7 +236,7 @@ public void DownloadBegin(Variant[] args) { public void DownloadComplete(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): DownloadComplete " - + args.length); + + args.length + " parameters"); } /** @@ -223,7 +248,7 @@ public void DownloadComplete(Variant[] args) { public void NavigateError(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): NavigateError " - + args.length); + + args.length + " parameters"); } /** @@ -235,7 +260,7 @@ public void NavigateError(Variant[] args) { public void NavigateComplete2(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): NavigateComplete " - + args.length); + + args.length + " parameters"); } /** @@ -247,7 +272,7 @@ public void NavigateComplete2(Variant[] args) { public void NewWindow2(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): NewWindow2 " - + args.length); + + args.length + " parameters"); } /** @@ -259,7 +284,7 @@ public void NewWindow2(Variant[] args) { public void OnFullScreen(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): OnFullScreen " - + args.length); + + args.length + " parameters"); } /** @@ -271,7 +296,7 @@ public void OnFullScreen(Variant[] args) { public void OnMenuBar(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): OnMenuBar " - + args.length); + + args.length + " parameters"); } /** @@ -283,7 +308,7 @@ public void OnMenuBar(Variant[] args) { public void OnQuit(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): OnQuit " - + args.length); + + args.length + " parameters"); IETestThread.quitHandled = true; } @@ -296,7 +321,7 @@ public void OnQuit(Variant[] args) { public void OnStatusBar(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): OnStatusBar " - + args.length); + + args.length + " parameters"); } /** @@ -308,7 +333,7 @@ public void OnStatusBar(Variant[] args) { public void OnTheaterMode(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): OnTheaterMode " - + args.length); + + args.length + " parameters"); } /** @@ -320,7 +345,7 @@ public void OnTheaterMode(Variant[] args) { public void OnToolBar(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): OnToolBar " - + args.length); + + args.length + " parameters"); } /** @@ -332,7 +357,7 @@ public void OnToolBar(Variant[] args) { public void OnVisible(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): OnVisible " - + args.length); + + args.length + " parameters"); } /** @@ -344,7 +369,7 @@ public void OnVisible(Variant[] args) { public void ProgressChange(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): ProgressChange " - + args.length); + + args.length + " parameters"); } /** @@ -356,7 +381,7 @@ public void ProgressChange(Variant[] args) { public void PropertyChange(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): PropertyChange " - + args.length); + + args.length + " parameters"); } /** @@ -368,7 +393,7 @@ public void PropertyChange(Variant[] args) { public void SetSecureLockIcon(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() - + "): setSecureLockIcon " + args.length); + + "): setSecureLockIcon " + args.length + " parameters"); } /** @@ -380,7 +405,7 @@ public void SetSecureLockIcon(Variant[] args) { public void StatusTextChange(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): StatusTextChange " - + args.length); + + args.length + " parameters"); } /** @@ -392,7 +417,7 @@ public void StatusTextChange(Variant[] args) { public void TitleChange(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): TitleChange " - + args.length); + + args.length + " parameters"); } /** @@ -404,7 +429,7 @@ public void TitleChange(Variant[] args) { public void WindowClosing(Variant[] args) { System.out.println("IEEvents Received (" + Thread.currentThread().getName() + "): WindowClosing " - + args.length); + + args.length + " parameters"); } }