www.pudn.com > mfc资源大全1.rar > atl_Conexe.shtml


 
 
 
 
    
    
   ATL - Converting Connect Sample to a Local Server 
 
 
 
 
 
 

Converting the CONNECT sample to a local server.


This article was contributed by Paul Shaffer.

Download the CONEXE example project.


The CONNECT sample included with Microsoft Visual C++ 5.0 is an example of how to use connection points with ATL. The in-process server is implemented in connect.dll and the client is a simple dialog based application called MDrive. It's intended to be an example of using connection points within a single process boundary. However, the first thing you may want to do with connection points is use them between different processes. I couldn't find an example of how to do this so I had to improvise. What follows is an example of how to convert the CONNECT sample to a local server.

First I converted the in-process server DLL to a server EXE. The fastest way to do this is to create a new application using the ATL COM AppWizard. I called the new application "Conexe" to differentiate it from the original project. The boilerplate code in conexe.cpp for the new app is ready to use without modifcation. Retain the use of CoInitialize in _tWinMain rather than CoInitializeEx.

Then I used the ClassView right click menu to create a new interface called IRandexe. I then copied the IDL interface related lines over from the IRandom interface in CONNECT. Finally, I just copied all the functions in the original Random.cpp and definitions in Random.h to complete the new interface. The result is a new interface that works just like IRandom, but with a new name and IID.

Also, I added this line to Stdafx.h:

 
#define _ATL_FREE_THREADED 

Now for the really interesting parts. I tried quite a few threading designs in creating this project and this is the only one that seems to work properly. In the local server version I had to add a call to CoInitialize in the RandomSession thread. So each thread that's created via a client request will get it's own private single threaded apartment. (It's not really clear to me why the in-process version does not require this.)

 
DWORD WINAPI RandomSessionThreadEntry(void* pv) 
{ 
    // Need to call CoInitialize on this thread to create a single 
    // threaded apartment. If you don't do this you will get the  
    // "CoInitialize has not been called." error. 
     
    CoInitialize(NULL);	// new line 
    CRandexe::RandomSessionData* pS = (CRandexe::RandomSessionData*)pv; 
    CRandexe* p = pS->pRandom; 
    while (WaitForSingleObject(pS->m_hEvent, 0) != WAIT_OBJECT_0) 
        p->Fire(pS->m_nID); 
    CoUninitialize();	// new line 
    return 0; 
} 

The next problem is related to interface marshalling between threads. The local server version won't work when events are fired since the interface was marshalled by COM on a different thread. Now we get to use those really long API calls, CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream.

 
// broadcast to all the objects 
HRESULT CRandexe::Fire(long nID) 
{ 
    IConnectionPointImpl* p = this; 
    Lock(); 
    HRESULT hr = S_OK; 
    IUnknown** pp = p->m_vec.begin(); 
	 
    int n = 0; 
 
    while (pp < p->m_vec.end() && hr == S_OK) 
    { 
        if (*pp != NULL) 
        { 
            IRandexeEvent* pIRandomEvent = (IRandexeEvent*)*pp; 
            _ASSERTE( pIRandomEvent != NULL ); 
 
            // If you don't call CoMarshalInterThreadInterfaceInStream 
            // followed by CoGetInterfaceAndReleaseStream you will get the 
            // "The application called an interface that was marshalled for different thread." 
            // error message. 
 
            LPSTREAM pStm; 
            hr = CoMarshalInterThreadInterfaceInStream( IID_IRandexeEvent, (IRandexeEvent*)pIRandomEvent, &pStm ); 
 
            IRandexeEvent *pI; 
            hr = CoGetInterfaceAndReleaseStream( pStm, IID_IRandexeEvent, (void**)&pI ); 
 
            hr = pI->Fire(nID); 
        } 
        pp++; 
    } 
    Unlock(); 
    return hr; 
} 

The client MDrive project was simply copied over to a new subdirectory and only modified slightly to use the new server. Multiple instances of MDrive can be launched and they all have access to the Conexe.exe local server. One thing to note is that the local server version is a lot slower as seen by the pixel drawing rate in MDrive. I'm fairly new to COM and ATL programming, and if I've made any errors please let me know. I'd really like to know if I've created needless inefficiencies with the constant inter-thread interface marshalling; if there's a better way, tell me. I also created a version of this project that uses IUnknown instead of IDispatch and it also seems to work correctly.

Last updated: 11 June 1998


Goto HomePage
© 1998 Zafir Anjum 
Contact me: zafir@home.com