我正在尝试通过它提供的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
感谢您的帮助。
答案 0 :(得分:1)
我发现了我的问题。
创建接收器对象的线程还需要使用窗口函数'GetMessage'来抽取线程的消息队列。
这就是Windows可执行文件正常工作的原因,它已经为创建的窗口提供了消息泵。当我在调用调用并查看堆栈跟踪时在sink对象中放置断点时,我想到了这一点。 “GetMessage”是我的代码中跟踪的第一件事。
在这个例子中,我相信为什么'GetMessage'就是答案,因为我相信COM的代码正在调用'PostThreadMessage'来通知我发生了一个接收器事件,'GetMessage'查看这些消息并以某种方式知道它们是COM相关并自行处理它们。
PS :注意我说创建接收器的线程。如果您在一个线程中创建接收器并在另一个线程中使用消息泵,您将收到来自COM的有关接收器的消息,但它不知道如何自动处理它,因为此线程没有任何关于COM的加载空间。所以从不调用接收器对象的调用。
所以Java可以在任何地方访问相同的COM,我让第二个线程控制关于COM的所有内容,并且所有被调用的本机方法都将请求重定向到该线程。因此,无论Java运行什么线程,它都将始终可以访问相同的COM,而无需重新加载所有内容。