JNI:水槽对象没有被射击,释放冻结

时间:2011-11-08 16:56:43

标签: object com java-native-interface

我正在尝试通过它提供的COM对象为这个名为“个人通信”的Windows应用程序创建一个JNI包装器。 PCOM Help

通过大量的谷歌搜索,我自己管理了很多。我可以充分地连接到COM对象并运行getter / setter方法来检索应用程序的各种信息。

我现在的问题是Sink Objects(尝试让应用程序向我发送事件)。 据我所知,一切都返回了一个好的返回代码,但是从不调用Sink类上的Invoke。更糟糕的是,如果要调用一个Event,释放告诉它开始发送事件的COM对象,挂起应用程序。如果我结束应用程序通知它的接收器,但没有使它触发任何事件,则应用程序不会挂起。

我尝试了各种方法和编译器。他们都给出了相同的结果。但是很奇怪,如果我在Java之外的实际应用程序(exe)中使用相同的代码,一切正常,并且通过Sink对象触发事件。所以我真的很想知道Java中的Java错误。

这是我到目前为止的代码:

PcommControl.h(我的Wrapper用于控制COM)

initConnectionManagerEvents:创建一个新的接收器对象并告知它的COM。 RegisterStartEvent告诉COM开始向接收器对象发送消息。

removeConnectionManagerEvents:取消删除并删除所有接收器对象。 UnregisterStartEvent告诉COM停止向接收器对象发送消息。

 jobject PcommControl::initConnectionManagerEvents(jobject sinkType) {
    if (!initConnectionManager())
        return NULL;

    if (autConnectionManagerPoint == NULL) {
        autConnectionManagerPoint = getConnectionPoint(autConnectionManager,
                DIID_IStartEvent);
        if (autConnectionManagerPoint == NULL)
            return NULL;
    }

    ConnectionSink *conSinkC = new ConnectionSink(jvm, sinkType);

    if (!conSinkC->isInitialized()) {
        delete conSinkC;
        return NULL;
    }

    if (!conSinkC->Advise(autConnectionManagerPoint)) {
#ifdef DEBUG
        printf("PcommControl: failed to advise ConnectionManagerEvent\n");
#endif
        delete conSinkC;
        return NULL;
    }

#ifdef DEBUG
    printf("PcommControl: ConnectionManagerEvent> %ld\n", conSinkC->getCookie());
#endif

    ConnectionSink *temp = connectionManagerEvents.put(
            (long) conSinkC->getCookie(), conSinkC);
    if (temp) {
        temp->Release();
    }

    if (connectionManagerEvents.getSize() == 1) {
#ifdef DEBUG
        printf("PcommControl: Registering ConnectionManagerEvent\n");
#endif
        HRESULT hresult = autConnectionManager->RegisterStartEvent();
        if (!SUCCEEDED(hresult)) {
#ifdef DEBUG
            printf("Failed to get RegisterStartEvent\n");
#endif
            // TODO
        }
    }

    return conSinkC->getJavaObjectConnection();
}

void PcommControl::removeConnectionManagerEvents() {
    ConnectionSink *connectionSink;

    if ((autConnectionManager) && (!safeUnload)) {
#ifdef DEBUG
        printf("PcommControl: Unregistering ConnectionManagerEvent\n");
#endif
        // TODO: seems to cause hanging issues for Java
        HRESULT hresult = autConnectionManager->UnregisterStartEvent();
        if (!SUCCEEDED(hresult)) {
#ifdef DEBUG
            printf("Failed to UnregisterStartEvent\n");
#endif
        }
    }

    while ((connectionSink = connectionManagerEvents.removeHead()) != NULL) {
        if (!safeUnload) {
#ifdef DEBUG
            printf("PcommControl: releasing a connection manager event\n");
#endif
            connectionSink->Unadvise();
#ifdef DEBUG
            printf("PcommControl: start release\n");
#endif
            connectionSink->Release();
        }
    }

#ifdef DEBUG
    printf("PcommControl: done releasing ConnectionManager events\n");
#endif
}

JNIEventSink.h(所有JNI接收器的接收器接口)

#ifndef JNIEVENTSINK_H_
#define JNIEVENTSINK_H_

#include <jni.h>
#include <OCIdl.h>

#define FARFAR  FAR* FAR*

class JNIEventSink: public IDispatch {
protected:

private:
    bool initialized;
    bool deconstructor;
    DWORD referenceCount;

    JavaVM *jvm;
    jobject javaObjectConnection;
    DWORD cookie;

    IConnectionPoint *point;

    static jclass javaLangClass;
    static jmethodID javaLangClassNewInstance;

    void init(JavaVM *javaVM, jobject sinkType) {
        initialized = false;
        deconstructor = false;
        referenceCount = 0;
        jvm = javaVM;
        javaObjectConnection = NULL;
        cookie = 0;

        AddRef();

        // create Java sink class from sinkType
//      if (javaVM) {
//          JNIEnv *env;
//          javaVM->AttachCurrentThread((void **) &env, NULL);
//          if (env == NULL) {
//#ifdef DEBUG
//              printf("JNIEventSink: java environment not found!\n");
//#endif
//              return;
//          }
//
//          if (javaLangClass == NULL) {
//              javaLangClass = NULL;
//              javaLangClassNewInstance = NULL;
//
//              javaLangClass = env->FindClass("java/lang/Class");
//              if (javaLangClass == NULL) {
//#ifdef DEBUG
//                  printf("JNIEventSink: javaLangClass not found!\n");
//#endif
//                  return;
//              }
//              javaLangClassNewInstance = env->GetMethodID(javaLangClass,
//                      "newInstance", "()Ljava/lang/Object;");
//              if (javaLangClassNewInstance == NULL) {
//#ifdef DEBUG
//                  printf(
//                          "JNIEventSink: javaLangClass NewInstance not found!\n");
//#endif
//                  return;
//              }
//          }
//
//          javaObjectConnection = env->CallObjectMethod(sinkType,
//                  javaLangClassNewInstance);
//          if (javaObjectConnection == NULL) {
//#ifdef DEBUG
//              printf(
//                      "JNIEventSink: Failed to create new Connection Object!\n");
//#endif
//              return;
//          }
//      }
        initialized = true;
    }
public:
    bool test;

    JNIEventSink(JavaVM *javaVM, jobject sinkType) {
#ifdef DEBUG
        printf("JNIEventSink: constructor\n");
#endif
        init(javaVM, sinkType);
        test = false;
    }

    virtual ~JNIEventSink() {
#ifdef DEBUG
        printf("JNIEventSink: deconstructor\n");

        if (test)
            printf("YESYESYESYESYESYESYESYES\n");
#endif
        deconstructor = true;
//
//      if (point != NULL)
//          Unadvise();
//
//      if (referenceCount > 0)
//          Release();
    }

    bool isInitialized() {
        return initialized;
    }

    bool Advise(IConnectionPoint *point) {
#ifdef DEBUG
        printf("JNIEventSink: Start Advise\n");
#endif
        this->point = point;
        this->point->AddRef();
        HRESULT hresult = point->Advise(this, &cookie);

        // TODO set cookie to java class

#ifdef DEBUG
        printf("JNIEventSink: Advise End\n");

        if (!SUCCEEDED(hresult))
            printf("JNIEventSink: failed\n");
#endif

        return SUCCEEDED(hresult);
    }

    bool Unadvise() {
#ifdef DEBUG
        printf("JNIEventSink: Start Unadvise\n");
#endif
        if (point == NULL)
            return true;

        IConnectionPoint *point = this->point;
        this->point = NULL;

        HRESULT hresult = point->Unadvise(cookie);
        point->Release();

#ifdef DEBUG
        printf("JNIEventSink: Unadvise End\n");

        if (!SUCCEEDED(hresult))
            printf("JNIEventSink: failed\n");
#endif

        return SUCCEEDED(hresult);
    }

    DWORD getCookie() {
        return cookie;
    }

    jobject getJavaObjectConnection() {
        return javaObjectConnection;
    }

    ULONG
    STDMETHODCALLTYPE AddRef() {
#ifdef DEBUG
        printf("JNIEventSink: Add Ref %ld,%ld,%ld\n", (long) this, cookie,
                referenceCount);
#endif
        referenceCount++;
        return referenceCount;
    }

    ULONG
    STDMETHODCALLTYPE Release() {
#ifdef DEBUG
        printf("JNIEventSink: Start Release %ld,%ld,%ld\n", (long) this,
                cookie, referenceCount);
#endif
        if (referenceCount == 0) {
#ifdef DEBUG
            printf("0 ref\n");
#endif
            return 0;
        }

        referenceCount--;
        long temp = referenceCount;

        if ((temp == 0) && (!deconstructor)) {
#ifdef DEBUG
            printf("JNIEventSink: deleting\n");
#endif
            delete this;
        }

        return temp;
    }

    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
            void **ppvObject) {
#ifdef DEBUG
        printf("JNIEventSink: QueryInterface %ld,%ld\n", (long) this, cookie);
#endif
        if (iid == IID_IUnknown) {
            *ppvObject = (IUnknown *) this;
        } else if (iid == IID_IDispatch) {
            *ppvObject = (IDispatch *) this;
        } else {
            *ppvObject = (void *) this;
        }

        ((IUnknown *) (*ppvObject))->AddRef();
        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) {
#ifdef DEBUG
        printf("JNIEventSink: GetTypeInfoCount %ld,%ld\n", (long) this, cookie);
#endif
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid,
            LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {
#ifdef DEBUG
        printf("JNIEventSink: GetIDsOfNames %ld,%ld\n", (long) this, cookie);
#endif
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int iTInfo,
            LCID lcid, ITypeInfo FARFAR ppTInfo) {
#ifdef DEBUG
        printf("JNIEventSink: GetTypeInfo %ld,%ld\n", (long) this, cookie);
#endif
        return E_NOTIMPL;
    }

    //  virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid,
    //          LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams,
    //          VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo,
    //          unsigned int FAR* puArgErr) = 0;
};

jclass JNIEventSink::javaLangClass = NULL;
jmethodID JNIEventSink::javaLangClassNewInstance = NULL;

#endif /* JNIEVENTSINK_H_ */

ConnectionSink.h(我的JNI接收器的实现,假设从autConnectionManager获取所有事件)

#ifndef CONNECTIONSINK_H_
#define CONNECTIONSINK_H_

#include <jni.h>
#include "PcommInterfaces.h"
#include "..\COM\JNIEventSink.h"

class ConnectionSink: public JNIEventSink {
private:

public:
    ConnectionSink(JavaVM *javaVM, jobject sinkType) :
        JNIEventSink(javaVM, sinkType) {
    }

    virtual ~ConnectionSink() {
#ifdef DEBUG
        printf("ConnectionSink: deconstructor\n");
#endif
    }
    // IStartEvent
    // future events I want to call
    // 1 - void NotifyStartEvent(VARIANT ConnHandle, VARIANT_BOOL bStarted);
    // 2 - void NotifyStartError(VARIANT ConnHandle);
    // 3 - void NotifyStartStop(int* Reason);

    virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid,
            LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams,
            VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo,
            unsigned int FAR* puArgErr) {
        // TODO this seems like it is never called

#ifdef DEBUG
        printf("ConnectionSink: Invoke %ld,%ld,%ld\n", (long) this,
                this->getCookie(), dispIdMember);
#endif

        test = true;

        return S_OK;
        //return E_NOTIMPL;
    }
};

#endif /* CONNECTIONSINK_H_ */

挂起时的输出示例:

Java_gov_ssa_utils_pcomm_ConnectionManager_registerStartEvents
JNIEventSink: constructor
JNIEventSink: Add Ref 72880304,0,0
JNIEventSink: Start Advise
JNIEventSink: Add Ref 72880304,0,1
JNIEventSink: Advise End
PcommControl: ConnectionManagerEvent> 1
PcommControl: Registering ConnectionManagerEvent
Created and attached sink.
Waiting 10 seconds for user to fire event
Destroying.
Java_gov_ssa_utils_PComm_release
PcommControl: destroying
PcommControl: Unregistering ConnectionManagerEvent

感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

我发现了我的问题。

创建接收器对象的线程还需要使用窗口函数'GetMessage'来抽取线程的消息队列。

这就是Windows可执行文件正常工作的原因,它已经为创建的窗口提供了消息泵。当我在调用调用并查看堆栈跟踪时在sink对象中放置断点时,我想到了这一点。 “GetMessage”是我的代码中跟踪的第一件事。

在这个例子中,我相信为什么'GetMessage'就是答案,因为我相信COM的代码正在调用'PostThreadMessage'来通知我发生了一个接收器事件,'GetMessage'查看这些消息并以某种方式知道它们是COM相关并自行处理它们。

PS :注意我说创建接收器的线程。如果您在一个线程中创建接收器并在另一个线程中使用消息泵,您将收到来自COM的有关接收器的消息,但它不知道如何自动处理它,因为此线程没有任何关于COM的加载空间。所以从不调用接收器对象的调用。

所以Java可以在任何地方访问相同的COM,我让第二个线程控制关于COM的所有内容,并且所有被调用的本机方法都将请求重定向到该线程。因此,无论Java运行什么线程,它都将始终可以访问相同的COM,而无需重新加载所有内容。