JNI调用DLL的COM初始化问题

时间:2022-08-05 17:28:40

我用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(); 

#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);
}

#6


提醒你:所有UI操作都是线程相关的,即使是MessageBox;Java UI操作和Windows UI操作是不兼容。
具体原因你要自己分析了。
你把MessageBox改成printf或System.out.println看看能否正确输出。

#7


我试了OutputDebugString问题还是一样。这个问题实在奇怪,我再看看。

最近学习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(); 

#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);
}

#6


提醒你:所有UI操作都是线程相关的,即使是MessageBox;Java UI操作和Windows UI操作是不兼容。
具体原因你要自己分析了。
你把MessageBox改成printf或System.out.println看看能否正确输出。

#7


我试了OutputDebugString问题还是一样。这个问题实在奇怪,我再看看。

最近学习Java和COM的交互,流程基本明白了,主要目的是达到了。

这个问题算是枝节小问题了,但是心里老是觉得有什么事没有完成一样。