我用JNI去调用一个DLL,这个DLL使用了COM。
我在DllMain里初始化COM:
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
::CoInitialize(NULL);
break;
case DLL_PROCESS_DETACH:
::CoUninitialize();
break;
}
return TRUE;
}
在一个DLL的导出函数connect里使用COM:
JNIEXPORT void JNICALL Java_opcjni_connect(JNIEnv *e, jobject obj)
{
//::CoInitialize(NULL); //问题来了:如果注释这句
HRESULT hr=::CoCreateInstance(cls,NULL,CLSCTX_ALL,IID_IServer,(void**)&pServer);
_com_error err(hr);
::MessageBox(NULL,err.ErrorMessage(),"CoInitialize",0); //则报错:COM没有初始化
}
而我用VC++写的程序去调同一个DLL,则没有问题。想了几天,一直没有解决这个问题,请教大家到底是哪有问题呢?
7 个解决方案
#1
CoInitialize是单线程的。
用CoInitializeEx(COINIT_MULTITHREADED)可以初始化多线程的。
case DLL_PROCESS_ATTACH:
::CoInitializeEx(COINIT_MULTITHREADED);
break;
case DLL_PROCESS_DETACH:
::CoUninitialize();
或者,你在DllMain里对每个线程都初始化
case DLL_THREAD_ATTACH:
::CoInitialize(NULL);
break;
case DLL_THREAD_DETACH:
::CoUninitialize();
用CoInitializeEx(COINIT_MULTITHREADED)可以初始化多线程的。
case DLL_PROCESS_ATTACH:
::CoInitializeEx(COINIT_MULTITHREADED);
break;
case DLL_PROCESS_DETACH:
::CoUninitialize();
或者,你在DllMain里对每个线程都初始化
case DLL_THREAD_ATTACH:
::CoInitialize(NULL);
break;
case DLL_THREAD_DETACH:
::CoUninitialize();
#2
sorry,刚看了一下MSDN.CoInitialize和CoInitializeEx都是初始化当前线程的,而且在DllMain里调用它们都是不可靠的。
#3
主要问题是很明显没有错误的DLL代码,JNI去调这个DLL有问题,而VC写的DLL去调就没有问题。好像JNI去调的时候,出了某个函数,所有的已进行过的行为(如初始化COM)都无效了一样。
#4
如果不考虑动态卸载相关的DLL资源,你就在JNI函数里调::CoInitialize应该就可以了。多次调用CoInitialize应该是没有什么问题的。
#5
我感觉会不会是JNI调DLL的时候的线程管理的方式和VC程序调DLL的时候不一样。
一个很简单的例子,我的目的是JNI通过调用DLL去异步读一个COM数据源的数据,读完以后再把数据返回给Java程序进行一些处理。
在DLL里,一个函数connect连接数据源,一个函数readAsync通过出接口异步读数据,读完会自动调用一个实现了出接口的类的OnDataReadComplete函数,在这个OnDataReadComplete函数里只要一个简单的功能,::MessageBox读到的数据。我用VC程序去连这个DLL完全没有问题,在VC程序里,点击一个按钮实现以上过程,可以成功立刻MessageBox读到的数据,
但是我用JNI去连这个DLL的时候,问题很奇怪,在Java程序里点击一个按钮实现以上过程,没有任何反应,但是关闭Java程序的时候,那个MessageBox框却出现了,而且数据也对,点了这个MessageBox框,整个程序才退出。实在搞不懂!
核心代码在下面:
DLL:
JNIEXPORT void JNICALL Java_opcjni_connect(JNIEnv *e, jobject obj)
{
HRESULT hr=::CoCreateInstance(cls,NULL,CLSCTX_ALL,IID_IServer,(void**)&pServer);
}
JNIEXPORT void JNICALL Java_opcjni_readAsync(JNIEnv *e, jobject obj)
{
IConnectionPoint *pCP=NULL;
IConnectionPointContainer *pCPC=NULL;
IDataRead *pIO=NULL;
IUnknown *pUnk;
pUnk->QueryInterface(IID_IConnectionPointContainer,(void**)&pCPC);
pCPC->FindConnectionPoint(IID_IDataReadCompleted,&pCP);
IDataReadCompleted *pDRC=new CDataReadCompleted(e,obj);
DWORD dwC;
pCP->Advise(pDRC,&dwC);
hr=pIO->Read();
//::MessageBox(NULL,"1","caption",0); //如果加这句,则java程序可以正确立刻调 //用::OnDataReadComplete 的MessageBox,如不加, //则要到java程序关闭才会调用
}
类CDataReadCompleted
STDMETHODIMP CDataReadCompleted ::OnDataReadComplete (float f)
{
char c[256];
sprintf(c,"this %f",f);
::MessageBox(NULL,c,"caption",0);
return S_OK);
}
一个很简单的例子,我的目的是JNI通过调用DLL去异步读一个COM数据源的数据,读完以后再把数据返回给Java程序进行一些处理。
在DLL里,一个函数connect连接数据源,一个函数readAsync通过出接口异步读数据,读完会自动调用一个实现了出接口的类的OnDataReadComplete函数,在这个OnDataReadComplete函数里只要一个简单的功能,::MessageBox读到的数据。我用VC程序去连这个DLL完全没有问题,在VC程序里,点击一个按钮实现以上过程,可以成功立刻MessageBox读到的数据,
但是我用JNI去连这个DLL的时候,问题很奇怪,在Java程序里点击一个按钮实现以上过程,没有任何反应,但是关闭Java程序的时候,那个MessageBox框却出现了,而且数据也对,点了这个MessageBox框,整个程序才退出。实在搞不懂!
核心代码在下面:
DLL:
JNIEXPORT void JNICALL Java_opcjni_connect(JNIEnv *e, jobject obj)
{
HRESULT hr=::CoCreateInstance(cls,NULL,CLSCTX_ALL,IID_IServer,(void**)&pServer);
}
JNIEXPORT void JNICALL Java_opcjni_readAsync(JNIEnv *e, jobject obj)
{
IConnectionPoint *pCP=NULL;
IConnectionPointContainer *pCPC=NULL;
IDataRead *pIO=NULL;
IUnknown *pUnk;
pUnk->QueryInterface(IID_IConnectionPointContainer,(void**)&pCPC);
pCPC->FindConnectionPoint(IID_IDataReadCompleted,&pCP);
IDataReadCompleted *pDRC=new CDataReadCompleted(e,obj);
DWORD dwC;
pCP->Advise(pDRC,&dwC);
hr=pIO->Read();
//::MessageBox(NULL,"1","caption",0); //如果加这句,则java程序可以正确立刻调 //用::OnDataReadComplete 的MessageBox,如不加, //则要到java程序关闭才会调用
}
类CDataReadCompleted
STDMETHODIMP CDataReadCompleted ::OnDataReadComplete (float f)
{
char c[256];
sprintf(c,"this %f",f);
::MessageBox(NULL,c,"caption",0);
return S_OK);
}
#6
提醒你:所有UI操作都是线程相关的,即使是MessageBox;Java UI操作和Windows UI操作是不兼容。
具体原因你要自己分析了。
你把MessageBox改成printf或System.out.println看看能否正确输出。
具体原因你要自己分析了。
你把MessageBox改成printf或System.out.println看看能否正确输出。
#7
我试了OutputDebugString问题还是一样。这个问题实在奇怪,我再看看。
最近学习Java和COM的交互,流程基本明白了,主要目的是达到了。
这个问题算是枝节小问题了,但是心里老是觉得有什么事没有完成一样。
最近学习Java和COM的交互,流程基本明白了,主要目的是达到了。
这个问题算是枝节小问题了,但是心里老是觉得有什么事没有完成一样。
#1
CoInitialize是单线程的。
用CoInitializeEx(COINIT_MULTITHREADED)可以初始化多线程的。
case DLL_PROCESS_ATTACH:
::CoInitializeEx(COINIT_MULTITHREADED);
break;
case DLL_PROCESS_DETACH:
::CoUninitialize();
或者,你在DllMain里对每个线程都初始化
case DLL_THREAD_ATTACH:
::CoInitialize(NULL);
break;
case DLL_THREAD_DETACH:
::CoUninitialize();
用CoInitializeEx(COINIT_MULTITHREADED)可以初始化多线程的。
case DLL_PROCESS_ATTACH:
::CoInitializeEx(COINIT_MULTITHREADED);
break;
case DLL_PROCESS_DETACH:
::CoUninitialize();
或者,你在DllMain里对每个线程都初始化
case DLL_THREAD_ATTACH:
::CoInitialize(NULL);
break;
case DLL_THREAD_DETACH:
::CoUninitialize();
#2
sorry,刚看了一下MSDN.CoInitialize和CoInitializeEx都是初始化当前线程的,而且在DllMain里调用它们都是不可靠的。
#3
主要问题是很明显没有错误的DLL代码,JNI去调这个DLL有问题,而VC写的DLL去调就没有问题。好像JNI去调的时候,出了某个函数,所有的已进行过的行为(如初始化COM)都无效了一样。
#4
如果不考虑动态卸载相关的DLL资源,你就在JNI函数里调::CoInitialize应该就可以了。多次调用CoInitialize应该是没有什么问题的。
#5
我感觉会不会是JNI调DLL的时候的线程管理的方式和VC程序调DLL的时候不一样。
一个很简单的例子,我的目的是JNI通过调用DLL去异步读一个COM数据源的数据,读完以后再把数据返回给Java程序进行一些处理。
在DLL里,一个函数connect连接数据源,一个函数readAsync通过出接口异步读数据,读完会自动调用一个实现了出接口的类的OnDataReadComplete函数,在这个OnDataReadComplete函数里只要一个简单的功能,::MessageBox读到的数据。我用VC程序去连这个DLL完全没有问题,在VC程序里,点击一个按钮实现以上过程,可以成功立刻MessageBox读到的数据,
但是我用JNI去连这个DLL的时候,问题很奇怪,在Java程序里点击一个按钮实现以上过程,没有任何反应,但是关闭Java程序的时候,那个MessageBox框却出现了,而且数据也对,点了这个MessageBox框,整个程序才退出。实在搞不懂!
核心代码在下面:
DLL:
JNIEXPORT void JNICALL Java_opcjni_connect(JNIEnv *e, jobject obj)
{
HRESULT hr=::CoCreateInstance(cls,NULL,CLSCTX_ALL,IID_IServer,(void**)&pServer);
}
JNIEXPORT void JNICALL Java_opcjni_readAsync(JNIEnv *e, jobject obj)
{
IConnectionPoint *pCP=NULL;
IConnectionPointContainer *pCPC=NULL;
IDataRead *pIO=NULL;
IUnknown *pUnk;
pUnk->QueryInterface(IID_IConnectionPointContainer,(void**)&pCPC);
pCPC->FindConnectionPoint(IID_IDataReadCompleted,&pCP);
IDataReadCompleted *pDRC=new CDataReadCompleted(e,obj);
DWORD dwC;
pCP->Advise(pDRC,&dwC);
hr=pIO->Read();
//::MessageBox(NULL,"1","caption",0); //如果加这句,则java程序可以正确立刻调 //用::OnDataReadComplete 的MessageBox,如不加, //则要到java程序关闭才会调用
}
类CDataReadCompleted
STDMETHODIMP CDataReadCompleted ::OnDataReadComplete (float f)
{
char c[256];
sprintf(c,"this %f",f);
::MessageBox(NULL,c,"caption",0);
return S_OK);
}
一个很简单的例子,我的目的是JNI通过调用DLL去异步读一个COM数据源的数据,读完以后再把数据返回给Java程序进行一些处理。
在DLL里,一个函数connect连接数据源,一个函数readAsync通过出接口异步读数据,读完会自动调用一个实现了出接口的类的OnDataReadComplete函数,在这个OnDataReadComplete函数里只要一个简单的功能,::MessageBox读到的数据。我用VC程序去连这个DLL完全没有问题,在VC程序里,点击一个按钮实现以上过程,可以成功立刻MessageBox读到的数据,
但是我用JNI去连这个DLL的时候,问题很奇怪,在Java程序里点击一个按钮实现以上过程,没有任何反应,但是关闭Java程序的时候,那个MessageBox框却出现了,而且数据也对,点了这个MessageBox框,整个程序才退出。实在搞不懂!
核心代码在下面:
DLL:
JNIEXPORT void JNICALL Java_opcjni_connect(JNIEnv *e, jobject obj)
{
HRESULT hr=::CoCreateInstance(cls,NULL,CLSCTX_ALL,IID_IServer,(void**)&pServer);
}
JNIEXPORT void JNICALL Java_opcjni_readAsync(JNIEnv *e, jobject obj)
{
IConnectionPoint *pCP=NULL;
IConnectionPointContainer *pCPC=NULL;
IDataRead *pIO=NULL;
IUnknown *pUnk;
pUnk->QueryInterface(IID_IConnectionPointContainer,(void**)&pCPC);
pCPC->FindConnectionPoint(IID_IDataReadCompleted,&pCP);
IDataReadCompleted *pDRC=new CDataReadCompleted(e,obj);
DWORD dwC;
pCP->Advise(pDRC,&dwC);
hr=pIO->Read();
//::MessageBox(NULL,"1","caption",0); //如果加这句,则java程序可以正确立刻调 //用::OnDataReadComplete 的MessageBox,如不加, //则要到java程序关闭才会调用
}
类CDataReadCompleted
STDMETHODIMP CDataReadCompleted ::OnDataReadComplete (float f)
{
char c[256];
sprintf(c,"this %f",f);
::MessageBox(NULL,c,"caption",0);
return S_OK);
}
#6
提醒你:所有UI操作都是线程相关的,即使是MessageBox;Java UI操作和Windows UI操作是不兼容。
具体原因你要自己分析了。
你把MessageBox改成printf或System.out.println看看能否正确输出。
具体原因你要自己分析了。
你把MessageBox改成printf或System.out.println看看能否正确输出。
#7
我试了OutputDebugString问题还是一样。这个问题实在奇怪,我再看看。
最近学习Java和COM的交互,流程基本明白了,主要目的是达到了。
这个问题算是枝节小问题了,但是心里老是觉得有什么事没有完成一样。
最近学习Java和COM的交互,流程基本明白了,主要目的是达到了。
这个问题算是枝节小问题了,但是心里老是觉得有什么事没有完成一样。