Click here to Skip to main content
16,009,068 members
Home / Discussions / ATL / WTL / STL
   

ATL / WTL / STL

 
GeneralContainer & Dll Pin
Bernhard20-Oct-03 3:13
Bernhard20-Oct-03 3:13 
GeneralRe: Container & Dll Pin
geo_m21-Oct-03 1:50
geo_m21-Oct-03 1:50 
GeneralRe: Container & Dll Pin
Bernhard21-Oct-03 19:16
Bernhard21-Oct-03 19:16 
GeneralCAccessor question Pin
Paul Silvernail20-Oct-03 0:31
Paul Silvernail20-Oct-03 0:31 
GeneralAny questions Pin
El'Cachubrey19-Oct-03 23:14
El'Cachubrey19-Oct-03 23:14 
Generalinserting ActiveX control with WTL... (VS.NET2003) Pin
tlpr16-Oct-03 21:58
tlpr16-Oct-03 21:58 
GeneralRe: inserting ActiveX control with WTL... (VS.NET2003) Pin
rlodina30-Oct-03 3:06
rlodina30-Oct-03 3:06 
QuestionWhere have my connection point fired events gone? Pin
Colin F16-Oct-03 1:01
sussColin F16-Oct-03 1:01 

Before we start I should point out that this tale of woe is a perfect example
of the old saying "a little knowledge is a dangerous thing".



I'm looking for some COM / ATL help with the latest version of my server program
and its interaction with clients. The server is an old MFC application that has had ATL
support for COM interfaces added. I have managed to make use of the Visual C++
6.0 ALT wizards and various tutorials to add methods and properties without
needing to know the deeper ins and outs of ATL COM programming. This is where "a
little knowledge is a dangerous thing" kicks in as I don't have time to become a
pure ATL guru and I need a quick solution to the following problem.


The two functions below are part of a CAnalysis ATL class that implements an
interface called IAnalysis. They worked fine for two years until I had to change
to COINIT_APARTMENTTHREADED in the server's InitInstance function.


The scenario is that an external (Visual Basic) program contacts the server
and calls the NotifyAnalysisComplete() method.


The NotifyAnalysisComplete() method spawns a new thread then returns
immediately, allowing the VB program to do other work.


The new thread CAnalysis::DoAnalysis() does the work then uses the pointer
passed to it to call the Fire_AnalysisComplete function of the original object
which uses the connection point mechanism to notify the original VB program.


There's nothing too spectacular in this: its a typical case of allowing the
client to do other things while the server does some heavy duty processing,
probably on a different machine on the network equipped with dedicated hardware
support.


<font color="#0000FF">STDMETHODIMP CAnalysis::NotifyAnalysisComplete()
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState())

    // Create a thread that will wait for the processing to complete.
    m_hThread = ::CreateThread(
        NULL,
        0,
        (LPTHREAD_START_ROUTINE) &CAnalysis::DoAnalysis,
        this,
        0,
        &m_dwThreadId);

    // Return an error code if the thread could not be created.
    if ( m_hThread == NULL )
        return S_FALSE;

    // Return success.
    return S_OK;
}


DWORD WINAPI CAnalysis::DoAnalysis(LPVOID pParam)
{
    CAnalysis *pCaller = (CAnalysis *)pParam;

    ... do the hard work of the analysis here ...

    // Signal back to the instigator that the process is complete
    pCaller->Fire_AnalysisComplete();

    return 0;
}
</font>

Here are the relevant parts of the CAnalysis class definition


<font color="#0000FF">class ATL_NO_VTABLE CAnalysis : 
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CAnalysis, &CLSID_Analysis>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<CAnalysis>,
public IDispatchImpl<IAnalysis, &IID_IAnalysis, &LIBID_GNTScopeLib>,
public CProxy_IAnalysisEvents< CAnalysis >,
public IProvideClassInfo2Impl<&CLSID_Analysis, &DIID__IAnalysisEvents, &LIBID_AnalysisLib, LIBRARY_MAJOR, LIBRARY_MINOR>
{
    public:
    CAnalysis()
    {
    }

    DECLARE_REGISTRY_RESOURCEID(IDR_ANALYSIS)

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    BEGIN_COM_MAP(CAnalysis)
    COM_INTERFACE_ENTRY(IAnalysis)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
    COM_INTERFACE_ENTRY(IConnectionPointContainer)
    COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
    COM_INTERFACE_ENTRY(IProvideClassInfo2)
    COM_INTERFACE_ENTRY(IProvideClassInfo)
    END_COM_MAP()
    BEGIN_CONNECTION_POINT_MAP(CAnalysis)
    CONNECTION_POINT_ENTRY(DIID__IAnalysisEvents)
    END_CONNECTION_POINT_MAP()


    // ISupportsErrorInfo
    STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

    // IAnalysis
    public:
        STDMETHOD(NotifyAnalysisComplete)();
</font>

<font color="#0000FF">    private:
        static DWORD WINAPI WaitForAnalysisCompleteTask(LPVOID pParam);
};</font>

As I mentioned earlier this worked fine for years until I had to change
to COINIT_APARTMENTTHREADED in the server's InitInstance function. The change
was necessary because the server's user interface was being revamped using some
ActiveX controls which would not load if the original COINIT_MULTITHREADED
server state was used.


As far as the server is concerned the flow is as before and I can step
through the code without any problems, even into the ATL wizard generated Fire_AnalysisComplete()
function. Inside that function there is a loop to iterate through all the
connections (only one in my case) and invoke the corresponding function back in
the client(s), i.e. the VB program. This loop seems to work fine but the VB
never receives the notification.


As a check I have gone back to the earlier non-user interface enhanced
version of the server and confirmed that making the COINIT_MULTITHREADED to COINIT_APARTMENTTHREADED
change is all that is necessary to stop that working as well.


I thought there may be a marshalling problem so I tried adding some
marshalling code, again copying from book examples and tutorials, resulting the
following.


 


<font color="#0000FF">STDMETHODIMP CAnalysis::NotifyAnalysisComplete()
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState())

    // Manual marshalling
    IAnalysis *pAnalysis = this;

    LPSTREAM pStream = NULL;
    HRESULT hr = CoMarshalInterThreadInterfaceInStream(IID_IAnalysis, pAnalysis, &pStream);

    // Create a thread that will wait for the processing to complete.
    m_hThread = ::CreateThread(
        NULL,
        0,
        (LPTHREAD_START_ROUTINE) &CAnalysis::DoAnalysis,
        pStream,
        0,
        &m_dwThreadId);

    // Return an error code if the thread could not be created.
    if ( m_hThread == NULL )
        return S_FALSE;

    // Return success.
    return S_OK;
}


DWORD WINAPI CAnalysis::DoAnalysis(LPVOID pParam)
{
    // Neither alternative makes the fire function work
    // CoInitializeEx(NULL, COINIT_MULTITHREADED);
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

    CAnalysis *pCaller = NULL;

    // Manual unmarshalling.
    LPSTREAM pStream = (LPSTREAM)pParam;
    HRESULT hr = CoGetInterfaceAndReleaseStream(pStream, IID_IAnalysis, (LPVOID *)&pCaller);

    ... do the hard work of the analysis here ...

    // Signal back to the instigator that the process is complete
    pCaller->Fire_AnalysisComplete();

    return 0;
}</font>

But this just made things worse because when single stepping through the Fire_AnalysisComplete
function it is clear that the pCaller pointer is invalid as the routine seems to
think that I have 1.7 million connections to that object, i.e. the memory it
points to is obviously not the data area of a valid CAnalysis object.


At this point I am puzzled by two questions. Why did my unmodified code
appear to do all the right things even after the change to COINIT_APARTMENTTHREADED
but no fired event ever made it back to the client? And what's wrong with my
marshalling code?


I am willing to concede that someone could prove to me that the code that
worked for two years only works by luck.


Another thought has recently come to my mind as well. Is the pCaller pointer
invalid because the original object has been deleted? Am I missing an AddRef/Release
pair somewhere that would keep the object available until the spawned thread
needs to use it?


Any guidance anyone can give would be useful.


AnswerRe: Where have my connection point fired events gone? Pin
Michael P Butler19-Oct-03 23:29
Michael P Butler19-Oct-03 23:29 
GeneralRe: Where have my connection point fired events gone? Pin
Colin Foster20-Oct-03 23:16
Colin Foster20-Oct-03 23:16 
GeneralStatusbars in dialog based application Pin
tareqsiraj15-Oct-03 18:16
tareqsiraj15-Oct-03 18:16 
GeneralRe: Statusbars in dialog based application Pin
tareqsiraj17-Oct-03 2:56
tareqsiraj17-Oct-03 2:56 
GeneralRe: Statusbars in dialog based application Pin
rbeckett16-Nov-03 14:56
rbeckett16-Nov-03 14:56 
GeneralDirectory only dialog box Pin
Leo Smith15-Oct-03 16:32
Leo Smith15-Oct-03 16:32 
GeneralRe: Directory only dialog box Pin
tareqsiraj15-Oct-03 18:23
tareqsiraj15-Oct-03 18:23 
GeneralWidth and Height property Pin
El'Cachubrey15-Oct-03 0:17
El'Cachubrey15-Oct-03 0:17 
Generalmap erase issue Pin
Yu Zhiquan12-Oct-03 23:55
Yu Zhiquan12-Oct-03 23:55 
GeneralRe: map erase issue Pin
Abebe13-Oct-03 0:46
Abebe13-Oct-03 0:46 
GeneralRe: map erase issue Pin
Michael Dunn13-Oct-03 6:36
sitebuilderMichael Dunn13-Oct-03 6:36 
GeneralRe: map erase issue Pin
Joaquín M López Muñoz13-Oct-03 9:17
Joaquín M López Muñoz13-Oct-03 9:17 
GeneralRe: map erase issue Pin
miropl14-Oct-03 9:38
miropl14-Oct-03 9:38 
GeneralRe: map erase issue Pin
Joaquín M López Muñoz14-Oct-03 10:11
Joaquín M López Muñoz14-Oct-03 10:11 
GeneralRe: map erase issue Pin
miropl15-Oct-03 7:15
miropl15-Oct-03 7:15 
GeneralRe: map erase issue Pin
ZoogieZork15-Oct-03 8:03
ZoogieZork15-Oct-03 8:03 
GeneralRe: map erase issue Pin
souldog15-Oct-03 21:39
souldog15-Oct-03 21:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.