How to handle COM events in console application with ATL - client's suspensions

1

I'm totally new in COM programming.

I have big problem. I’m trying to handle events from CANoe application via COM Server. Firstly I tried do it in native C++ but without results. Now I’m trying it by using ATL. I'm doing something wrong but I don’t have any idea what. When the event occurs, my client application suspends itself and CANoe. After closing client application CANoe works fine. So I know that my client application handles Events from CANoe but it can’t serve it. The commented parts of code in my source code were also used but with the same results.

#import "CANoe.tlb" //importing CANoe type library
#include "stdafx.h"
#include <atlbase.h> //COM Server methods
#include <iostream>
#include <atlhost.h>

using namespace CANoe;
using namespace std;

_ATL_FUNC_INFO infoZero = { CC_STDCALL, VT_EMPTY, 0, 0};
_ATL_FUNC_INFO infoOne = { CC_STDCALL, VT_EMPTY, 1, { VT_I4 } };

class CANoeComClient : 
    //IDispEventSimpleImpl<1, CANoeComClient, &__uuidof(_IMeasurementEvents)>,
    //IDispEventSimpleImpl<2, CANoeComClient, &__uuidof(_IEnvironmentVariableEvents)>
    IDispEventSimpleImpl<1, CANoeComClient, &__uuidof(_IApplicationEvents)>
{
    IApplicationPtr pApp; //Pointer to the Application Object
    IMeasurementPtr pMeasure; //Pointer to the Measurement object
    _IMeasurementEventsPtr pMEvent; //Pointer to the Measurement's Events
    IEnvironmentPtr pEnvironment; //Pointer to the Environment Object
    IEnvironmentVariable2Ptr pEnvironmentVar; //Pointer to the Environment Variable Object
    ICAPL2Ptr pCAPL; //Pointer to the CAPL Object
    CLSID clsid; //globally unique identifier that identifies a COM class object
    HRESULT result; //results of COM functions

public:
    //typedef IDispEventSimpleImpl<2, CANoeComClient, &__uuidof(CANoe::_IEnvironmentVariableEvents)> EnvVarEventsHandler;
    //typedef IDispEventSimpleImpl<1, CANoeComClient, &__uuidof(CANoe::_IMeasurementEvents)> MeasurementEventsHandler;  
    typedef IDispEventSimpleImpl<1, CANoeComClient, &__uuidof(CANoe::_IApplicationEvents)> ApplicationEventsHandler;
    void __stdcall OnStart(void);
    void __stdcall OnStop(void);
    void __stdcall OnOpen(void);
    void __stdcall OnQuit(void);

    BEGIN_SINK_MAP(CANoeComClient)
        //SINK_ENTRY_INFO(1, __uuidof(CANoe::_IMeasurementEvents), 0x02, OnStart, &info)
        //SINK_ENTRY_INFO(1, __uuidof(CANoe::_IMeasurementEvents), 0x03, OnStop, &infoZero)
        SINK_ENTRY_INFO(1, __uuidof(CANoe::_IApplicationEvents), 0x01, OnOpen, &infoZero)
        SINK_ENTRY_INFO(1, __uuidof(CANoe::_IApplicationEvents), 0x02, OnQuit, &infoZero)
    END_SINK_MAP()

    void __stdcall OnChange();
/*
    BEGIN_SINK_MAP(CANoeComClient)
        SINK_ENTRY_INFO(2, __uuidof(CANoe::_IEnvironmentVariableEvents), 0x01, OnChange, &info)
    END_SINK_MAP()*/

    CANoeComClient(_bstr_t configPath);
    HRESULT createEventConnection();
    HRESULT startMeasurement();
    HRESULT stopMeasurement();
};
void CANoeComClient::OnStart()
{
    cout << "kurka wodna 1" << endl;
}
void CANoeComClient::OnStop()
{
    cout << "kurka wodna 2" << endl;
}
void CANoeComClient::OnChange()
{
    cout << "kurka wodna 2" << endl;
}
void CANoeComClient::OnOpen()
{
    cout << "kurka wodna 1" << endl;
}
void CANoeComClient::OnQuit()
{
    cout << "kurka wodna 1" << endl;
}
CANoeComClient::CANoeComClient(_bstr_t configPath)
{
    /* Initialization COM library: */
    if (FAILED(CoInitialize(NULL))) 
    {
        cerr << "Initialization COM Library error" << endl;
        system("pause");
        return;
    }

    /* Actualization clsid variable with CANoe.Application path: */
    if((result = CLSIDFromProgID(L"CANoe.Application", &clsid)) != S_OK) 
    {
        cerr << "Problem with opening application" << endl;
        system("pause");
        return;
    }

    /*Opening CANoe application: */
    result = pApp.CreateInstance(__uuidof(CANoe::Application));
    if(result != S_OK)
    {
        cerr << "pApp fault" << endl;
        return;
    }
    else
        cout << "CANoe opened succesfully" << endl;

    /* Opening CANoe configuration: */
    result = pApp->Open(configPath, FALSE, TRUE); //Opening test.cfg file
    if(result != S_OK)
    {
        cerr << "Opening configuration fault" << endl;
        return;
    }
    else 
        cout << "Configuration loaded succesfully" << endl;

    /*Definitions of all objects: */
    //pMeasure.CreateInstance(__uuidof(CANoe::Measurement));
    pEnvironment = pApp->Environment;
    pEnvironmentVar = pEnvironment->GetVariable(L"env_ClientReq");
    pCAPL = pApp->CAPL;
    result = ApplicationEventsHandler::DispEventAdvise(pApp);
//  result = MeasurementEventsHandler::DispEventAdvise(pMeasure);
    //result = EnvVarEventsHandler::DispEventAdvise(pEnvironmentVar);
    if(result != S_OK)
    {
        cerr << "Creating connection fault" << endl;
        return;
    }
    else 
        cout << "Creating conenction succesfully" << endl;
}

HRESULT CANoeComClient::startMeasurement()
{
    return pMeasure->Start();
}

HRESULT CANoeComClient::stopMeasurement()
{
    return pMeasure->Stop();
}

int _tmain(int argc, _TCHAR* argv[])
{
    int tmp = 0; //temporary variable to used to get envVar values
    HRESULT result; //results of COM functions
    CANoeComClient client(L"C:\\test\\test.cfg");
    while(1);
}

Here is description of Measurement Object and Event from OLE-COM object viewer:

[
  uuid(CD866FB6-44BF-11D3-8538-00105A3E017B),
  helpstring("Measurement Class")
]
coclass Measurement {
    [default] interface IMeasurement2;
    [default, source] dispinterface _IMeasurementEvents;
};

 [
  odl,
  uuid(A844C1E0-F5CE-11D3-8612-00105A3E017B),
  helpstring("IMeasurement2 Interface"),
  dual,
  oleautomation
]
interface IMeasurement2 : IMeasurement {
    [id(0x0000000a), propget, helpstring("property Running")]
    HRESULT Running([out, retval] VARIANT_BOOL* pVal);
};

[
  uuid(A844C1E0-F5CE-11D3-8612-00105A3E017B),
  helpstring("IMeasurement2 Interface"),
  dual
]
dispinterface IMeasurement2 {
    properties:
    methods:
        [id(0x00000001), propget, helpstring("property Application")]
        IDispatch* Application();
        [id(0x00000002), propget, helpstring("property Parent")]
        IDispatch* Parent();
        [id(0x00000003), helpstring("method Start")]
        void Start();
        [id(0x00000004), helpstring("method Stop")]
        void Stop();
        [id(0x00000005), helpstring("method Step")]
        void Step();
        [id(0x00000006), helpstring("method Animate")]
        void Animate();
        [id(0x00000007), helpstring("method Break")]
        void Break();
        [id(0x00000008), helpstring("method Reset")]
        void Reset();
        [id(0x00000009), propget, helpstring("property AnimationDelay")]
        long AnimationDelay();
        [id(0x00000009), propput, helpstring("property AnimationDelay")]
        void AnimationDelay([in] long rhs);
        [id(0x0000000a), propget, helpstring("property Running")]
        VARIANT_BOOL Running();
};

[
  uuid(CD866FB7-44BF-11D3-8538-00105A3E017B),
  helpstring("_IMeasurementEvents Interface")
]
dispinterface _IMeasurementEvents {
    properties:
    methods:
        [id(0x00000001), helpstring("method OnInit")]
        HRESULT OnInit();
        [id(0x00000002), helpstring("method OnStart")]
        HRESULT OnStart();
        [id(0x00000003), helpstring("method OnStop")]
        HRESULT OnStop();
        [id(0x00000004), helpstring("method OnExit")]
        HRESULT OnExit();
};

I attach CANoe.tlb: http://www.sendspace.com/file/j2zloj

Thank you for your attention Damian

events
visual-c++
com
atl
com-server
asked on Stack Overflow Dec 3, 2011 by meler

2 Answers

1
  1. You are initializing STA and you're doing while(1);, however note that you are expected to implement a message pump on the thread (hint: a quick check if this is the reason is to show a message box instead of endless loop)
  2. Why SINK_ENTRY_INFO and not SINK_ENTRY as any tutorial would suggest?
  3. Did you have a chance to see if the event handler is executed, and it reached pMeasure-> call there; did you check if it freezes inside this call, or it returns a failure result (or the event handler is called on wrong thread).
answered on Stack Overflow Dec 3, 2011 by Roman R.
0

As console application has no Window (which handles events queue used by STA), you have either to call

CoInitializeEx(NULL,COINIT_MULTITHREADED); 

to use MTA instead of STA

or implement Message pump

MSG msg;
while(GetMessage(&msg,0,0,0))
  DispatchMessage(&msg);  

I also had to add following code to stdafx.h to prevent crash in ATL classes:

class CDummyModule : public CAtlExeModuleT<CDummyModule> {};
CDummyModule _Module;
answered on Stack Overflow Sep 14, 2017 by Tomas

User contributions licensed under CC BY-SA 3.0