JNI: Раковина не стреляет, релизы замораживаются - PullRequest
0 голосов
/ 08 ноября 2011

Я пытаюсь создать оболочку JNI для этого приложения Windows под названием «Личная связь» через предоставленный им COM-объект. Справка PCOM

Благодаря большому количеству поисков, я справился со многими самостоятельно. Я могу достаточно подключиться к COM-объекту и запустить методы получения / установки для получения различной информации о приложении.

Моя проблема теперь связана с объектами Sink (пытаюсь заставить приложение отправлять мне события). Насколько я могу судить, все возвращает хороший код возврата, но Invoke для класса Sink никогда не вызывается. Что еще хуже, если событие должно было быть вызвано, при освобождении COM-объекта, который сказал ему начать отправку событий, приложение зависает. Если я заканчиваю приложение, сообщая о приемнике, но не заставляя его запускать какие-либо события, приложение не зависает.

Я пробовал различные методы и компиляторы. Все они дают одинаковый результат. Что странно, хотя, если я использую тот же код в реальном приложении (exe) за пределами Java, все работает нормально, и события запускаются через объект Sink. Так что я действительно растерялся из-за того, что не так с Java на картинке.

Вот мой код, который у меня есть:

PcommControl.h (My 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 Sink, которая предполагает получение всех событий из 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 Ответ

1 голос
/ 17 ноября 2011

Я разобрался в своей проблеме.

Поток, создавший объект приемника , должен также прокачать очередь сообщений потока, используя функцию окна 'GetMessage' .

Именно поэтому исполняемый файл windows работал правильно, в нем уже есть насос сообщений для созданного окна. Я понял это, когда поставил точку останова в объекте приемника при вызове invoke и посмотрел на трассировку стека. «GetMessage» был первым, что пришло из моего кода.

В этом случае я верю, почему ответ «GetMessage» является ответом, потому что я считаю, что код COM вызывает «PostThreadMessage», чтобы уведомить меня о том, что произошло событие приемника, и «GetMessage» просматривает эти сообщения и каким-то образом знает, что они COM связаны и обрабатывают их самостоятельно.

PS : обратите внимание, я сказал нить , которая создала приемник. Если вы создаете приемник в одном потоке, а насос сообщений - в другом, вы получите сообщение от COM о приемнике, но он не будет знать, как его обработать автоматически, так как в этом потоке ничего не загружено в COM. пространство. Таким образом, вызов объекта-получателя никогда не вызывается.

Таким образом, Java могла обращаться к одному и тому же COM везде, я заставил 2-й поток контролировать все, что касается COM, и любые вызываемые собственные методы перенаправляли запрос в этот поток. Поэтому, независимо от того, в каком потоке работает Java, он всегда будет иметь доступ к одному и тому же COM без перезагрузки всего.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...