Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 6 of 7
Creating our own native thread raises a problem, though. We will need to call Java methods from within our event-handling routine, and these will happen in the context of our own thread. The JVM, however, has its own multithreading scheme, and we cannot just intrude whenever we please. We need to have our own thread work politely with the JVM. Fortunately, JNI provides the facility for attaching our native threads to the VM and enabling them to work with the VM's synchronization scheme. When attaching the thread, we receive our own environment interface, through which we can make calls to the VM.
This is a delicate point, so we will illustrate it by including the full thread procedure:
DWORD WINAPI DesktopIndicatorThread::ThreadProc( LPVOID lpParameter )
{
DesktopIndicatorThread *l_this = (DesktopIndicatorThread *) lpParameter;
// Attach the thread to the VM
l_this->m_vm->AttachCurrentThread( (void**) &l_this->m_env, NULL );
MSG msg;
while( GetMessage( &msg, NULL, 0, 0 ) )
{
if( msg.message == WM_DESKTOPINDICATOR )
{
// Extract handler
DesktopIndicatorHandler *l_handler = (DesktopIndicatorHandler*) msg.lParam;
switch( msg.wParam )
{
case DesktopIndicatorHandler::enableCode:
l_this->m_handlerCount++;
l_handler->doEnable();
break;
case DesktopIndicatorHandler::updateCode:
l_handler->doUpdate();
break;
case DesktopIndicatorHandler::disableCode:
// Destroy it!
delete l_handler;
// No more handlers?
if( !--l_this->m_handlerCount )
{
l_this->m_thread = 0;
// Detach thread from VM
l_this->m_vm->DetachCurrentThread();
// Time to die
ExitThread( 0 );
}
break;
}
}
else
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
// Detach thread from VM
l_this->m_vm->DetachCurrentThread();
return 0;
}
The thread begins by attaching itself to the JVM; the thread's environment pointer is stored in its instance. Note that this
is a static function, so we are passing the this pointer by ourselves via the thread's private general-purpose parameter, which we sent in our CreateThread call.
Next, we will loop on our message queue. The GetMessage function blocks until a message arrives on the queue. We are using our own user-defined message code, with our custom identifier
codes and a pointer to the relevant handler instance. The handler class does the real work, so we will simply delegate the
operation to its methods in our Java-safe context.
The TranslateMessage and DispatchMessage calls will delegate messages to our invisible windows. This is important to note, because we are using our own user-defined
messages, and must make sure that they have different codes; if we fail to take this precaution, our thread procedure will
not be able to tell the difference.
To make the code cleaner, we will make the PostThreadMessage calls implicit. For example:
void DesktopIndicatorHandler::enable( JNIEnv *env )
{
g_DesktopIndicatorThread.MakeSureThreadIsUp( env );
while( !PostThreadMessage( g_DesktopIndicatorThread, WM_DESKTOPINDICATOR, enableCode, (LPARAM) this ) )
Sleep( 0 );
}
As seen in our thread procedure above, the message will cause doEnable to be called in the safe context. The weird loop and sleep setup is there because it may take a short while for the thread's
message queue to initialize. Once the queue is up, PostThreadMessage should always return true. (Note that, although doEnable is private, DesktopIndicatorThread can call it because it is declared as a friend class. Yes, it's obvious, but it also may be a bit confusing with all this
switching between Java and C++.)