Measurement Studio for VC++

cancel
Showing results for 
Search instead for 
Did you mean: 

A strange problem of DataSocket Dll

I have asked 2 questions about my datasocket dll. And thanks all who answered my questions. And here is another problem I encountered.

My project is creating a Dll with datasocket for Java Native Interface (JNI) so that Java can use DataSocket. (I know there is a DataSocket javabean, but my supervisor don't want it 😞 )

The dll and JNI is finished and works fine in any single thread Java program. But when I call the JNI functions to access DataSocket in a multi-thread java program, problem shows up. It crashed. I added ::CoInitialize(NULL) function in each function to initialize COM. The program didn't crash but the JNI function never return, it stopped at the point of ::CoInitialize(), and never continued executing. One solutions is showing a model dialog and then close it to make the program continued. Here is an example:

JNIEXPORT jboolean JNICALL Java_DataSocket_IsDataUpdated(JNIEnv *env, jobject obj)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
::CoInitialize(NULL);
// must add this two lines, or never continue executing
form f = new form();
f.DoModal();
return theApp.m_DataSocket.GetDataUpdated();
}


And I also found that the data updated event handler in the Dll also stopped after connecting to DataSocket. But if you keep showing and destroying a model dialog, the handler will updated. Here is a example:

// this function will be called in a sub-thread of a Java program, therefore, the model dialog kept showing and closing
JNIEXPORT jlong JNICALL Java_DataSocket_GetUpdateIndex(JNIEnv *env, jobject obj)
{
// this two lines of code is essensial, or the update handler will not be executed
form f = new form();
f.DoModal();
// in the form class, i install a timer, in OnTimer function there is a OnOk() function to close tis Model window.
return theApp.index; // value of index will will be updated in data updated event handler
}

void CDataSocketdllApp::DataUpdatedEventHandler (CNiDataSocketData &data)
{
index++;
}

And I also need to show and close a model window to connect datasocket in the connnect function:

JNIEXPORT jint JNICALL Java_DataSocket_Connect(JNIEnv *env, jobject obj, jstring urlstr, jint mode)
{
theApp.m_DataSocket.Connect(url,CNiDataSocket::ReadAutoUpdate);
form f = new form();
f.DoModal();
}

Although this method made my program worked, but it seems stupid to keep showing a model dialog.

Can anybody give me advice to solve this problem in a normal way? Thank you very much in advanced!!!!
0 Kudos
Message 1 of 8
(6,060 Views)
I have a couple of ideas about the behavior you are seeing. I am not familiar with Java, so you'll need to try to translate the COM concepts to the JNI concepts accordingly.

1. DataSocket operations, including connect, read, and write, are asynchronous. This means that your program must enter a message processing loop before the operation can complete. You can poll the status of the datasocket inside your message loop to determine when the connection completes.

2. You cannot just pass COM interfaces between threads. You must marshal COM pointers across threads. In COM programming, this means that you use the Global Interface Table or CoMarshalInterthreadInterfaceInStream to pass your COM interfaces between threads. Again, I don't know how you do this in
Java.

David Rohacek
National Instruments
0 Kudos
Message 2 of 8
(6,060 Views)
It sounds like David's #1 is your primary problem. Whenever you create a modal dialog, you are causing the program to enter a message loop while waiting for you to dismiss the modal dialog. DataSocket requires that a message loop be active in order for events to be fired and asynchronous operations (like Connect) to complete.

This topic gives more detail on DataSocket and how to implement a message loop in C/C++. I don't know much of anything about JNI, but hopefully this will be helpful.

Tony
0 Kudos
Message 3 of 8
(6,060 Views)
Thanks both of you for answering my questions. I have found that this problem is related to the messages processing loop. And I also found that DataSocket doesn't support cosole program. I created a DLL with datasocket which has a data updated event handler:

void CDataSocketdllApp::DataUpdatedEventHandler (CNiDataSocketData &data)
{
index++;
}

and a function to return the index:

extern "C" DllExport int Returnvalue()
{

return theApp.index;
}

and when I tried to get the value of index from the dll in a MFC console program, I found the data was not updated. That is the data updated event handler didn't work. I used SPY++ to trace the messages, I found the messages between my console program and the DataSocket component (_NISock) were blocked.

Following the advices from TonyH (Thanks!!) I add a message pump in the Returnvalue() function:

extern "C" DllExport int Returnvalue()
{

while( PeekMessage(&msg,NULL,0,0,PM_REMOVE) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

//return true;
return theApp.index;
}

And it finally worked! And my JNI program also work if I call this function in a main function

But there is another problem, when I tried to access datasocket from a thread (non-main thread, i tried it in a multi-thread java program, but i didn't tried MFC console program, but i think it's the same), this method didn't work. From the MSDN, the PeekMessage(&msg,NULL,0,0,PM_REMOVE) function "retrieves messages for any window that belongs to the current thread making the call", therefore, we could not retrieve messages between the main thread and _NISock if we call this message pump from anohter thread. So the _NISock stops working and no data updated event comes.

Currently I solved this problem by calling a function in the loop of main function (main thread) to keep pumping message. But i don't think it's a good idea. Would you please give me advice how to solve this problem in a normal way? And why the messages between console program and datasocket are blocked?

Thank you very much in advanced!
0 Kudos
Message 4 of 8
(6,060 Views)
Here we go into the hairy world of COM:

COM is a Microsoft technology for sharing binary code components. DataSocket is a COM DLL. One common mechanism for providing thread-safety in COM is called "apartment threading". This means that any function calls that are made on a COM object, like DataSocket, are dispatched to the thread that created the object. In Windows, the only way a function call can be dispatched to another thread is if the thread that will be receiving the function call and executing the function is running a message loop.

OK, so the important results from this are:

(1) the thread from which you create the DataSocket object is the thread that will actually execute all function calls that are made to the DataSo
cket object.

(2) the thread from which you create the DataSocket must be running a message loop.

(3) if you use the DataSocket from a thread other than the one you created it in from, that thread does NOT need a message loop.

If you are using your DataSocket exclusively in a thread other than the main thread, it is best to create the datasocket in that thread if possible.

Hope that helps.

- Tony
0 Kudos
Message 5 of 8
(6,060 Views)
Oh yes. I almost forgot:

If you are going to use the DataSocket from multiple threads, you cannot just pass the pointer to the object from one thread to another. Each thread needs to have its own special COM-marshalled pointer. To accomplish this, you need to create an instance of the IGlobalInterfaceTable object in the thread that creates the DataSocket as well as each thread that uses it. See the MSDN Library documentation for IGlobalInterfaceTable for info on how to do this.

In the thread that created the DataSocket, you register your DataSocket object in the global interface table (GIT) with IGlobalInterfaceTable::RegisterInterfaceInGlobal. In other threads that you will be using the DataSocket object,
you retrieve a pointer to the DataSocket object using the cookie that was returned from RegisterInterfaceInGlobal and passing it to IGlobalInterfaceTable::GetInterfaceFromGlobal. Then when all is done, free the cookie with IGlobalInterfaceTable::RevokeInterfaceFromGlobal.

See the MSDN documentation (available on the web at http://msdn.microsoft.com if you don't have it) for more information on all of these functions.

- Tony
0 Kudos
Message 6 of 8
(6,060 Views)
Also, if you're using Visual C++ .NET to create your JNI interface, you can use ATL7's CComGITPtr smart pointer class to make this a lot easier. It would still be the same basic steps that Tony outlined above, but you could use CComGITPtr::Attach instead of IGlobalInterfaceTable::RegisterInterfaceInGlobal, CComGITPtr::CopyTo instead of IGlobalInterfaceTable::GetInterfaceFromGlobal, a
nd CComGITPtr::Revoke instead of IGlobalInterfaceTable::RevokeInterfaceFromGlobal.

- Elton
0 Kudos
Message 7 of 8
(6,061 Views)
Thanks for reply... i tried marshalling using CComGITPtr or RegisterInterfaceInGlobal.. but it required the base interface of CNiDataSocket class.. could you give method to reterive the base interface of the CNiDataSocket
0 Kudos
Message 8 of 8
(5,686 Views)