Receiving Of COM Events within a C program

Hello:

I have the below C code.  I need for the OnUpdateComplete() and OnDownloadComplete() methods (shown in the code below) to be fired when the respective events for them are fired from the #imported COM program.

Here is the program.  What extra code do I need to do this.  I believe I need to be able to handle connection point sinks and I have found the following URL:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/vcconSupportingIDispEventImpl.asp,

but I am wondering if I can get some extra help here as I have no experience with COM, yet I have to get this to work.

Here is my code that compiles but I need to add the necessary connection point sinks for this to work.  Can anybody help me with this?  Also, please supply any #includes that I may need.

Thank you,

Dan



#import "c:\Program Files\Common Files\InstallShield\UpdateService\agent.exe" named_guids no_namespace

#include <atlbase.h>
#include "CBstrImpl.h"
#include <iostream>

bool downloadComplete = false;

bool updateComplete = false;

using std::cout;



void OnDownloadComplete(long nResult)
{

      downloadComplete = true;
      
}

void OnUpdateComplete(long nReturnCode)
{

      updateComplete = true;
      
}


void errormsg(HRESULT hr, LPCTSTR desc=NULL)
{
    LPVOID lpMsgBuf = NULL;
        TCHAR pszError[1024];

        if (desc == NULL)
        {
                // get system description for the error
                ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                                                FORMAT_MESSAGE_FROM_SYSTEM |
                                                FORMAT_MESSAGE_IGNORE_INSERTS,
                                                NULL,
                                                hr,
                                                0,
                                                reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, NULL);

                if(lpMsgBuf)
                {
                        ::wsprintf(pszError, "%s", reinterpret_cast<char*>(lpMsgBuf));

                        ::LocalFree(lpMsgBuf);
                }
                else
                {
                        ::wsprintf(pszError, "Error: %X", hr);
                }
        }
        else
                wsprintf(pszError, "Error: Code:0x%x, %s", hr, desc);
        ::MessageBox(0, pszError, "Error", MB_OK);
        exit(0);
}

int main(int argc, char* argv[])
{
/*
            // Used for testing DownloadEx()
            char buf[1000];
            GetTempPath(1000, buf);  // Get DOS style path name of temp directory
            char buf1[1000];
            GetLongPathName(buf, buf1, 1000);  // Get long Windows style path for temp directory.

*/

        ::CoInitialize(NULL);
            CComPtr<IUpdate> m_spUpdate;


        CComPtr<IAgentEx> m_spAgent;
        CComPtr<IUpdates> m_spUpdates;
        //CComPtr<IUpdate> m_spUpdate;
        CComBSTR m_ProductCode ("{FE4ACC45-03DE-4CD0-A202-B67826A1F8CD}");

        try
{
                HRESULT hr = m_spAgent.CoCreateInstance(CLSID_Agent);

                if (!SUCCEEDED(hr))
                       errormsg(hr);


                m_spUpdates = m_spAgent->EnumUpdates(m_ProductCode.m_str);

                if (m_spUpdates->Count == 0)
                         ::MessageBox(0, "No Updates found", "Info", MB_OK);


                for (long i = 0; i < m_spUpdates->Count; i++)
                {
                        CComVariant vtItem;
                        hr = m_spUpdates->get_Item(i+1, &vtItem);

                        if (!SUCCEEDED(hr))
                                errormsg(hr);

                        m_spUpdate= com_cast<IUpdate>(vtItem.punkVal);

                                                    
                        CComBSTR title=m_spUpdate->QueryValue(Title).copy();
                                    CComBSTR startDate = m_spUpdate->QueryValue(StartDate).copy();
                                    /*
                                    CComBSTR url = m_spUpdate->QueryValue(DownloadUrl).copy();
                                    */
                                    CBstr bstrTitle( title.Copy(), true);
                                    CBstr bstrDate (startDate.Copy(), true);

                                    if (bstrTitle.Find( _T("BaseData")) >= 0 && (bstrDate.Left(10)).CompareNoCase(_T("2006-05-31")) > 0)
                                    {
                                          
                                          //numInstallCounter++;

                                          m_spUpdate->Download(VARIANT_TRUE);
                                                
                                          while (! downloadComplete)
                                          {
                                                cout << "Download Not Complete";
                                                Sleep(3000);
                                          }

                                          cout << "Download Complete!";


                                          m_spUpdate->Execute();

                                          while (! updateComplete)
                                          {
                                                cout << "Update Not Complete";
                                                Sleep(3000);
                                          }
                                    
                                          cout << "Update Complete!";
                                          /*
                                          CComBSTR sz=m_spUpdate->QueryValue(DownloadSize).copy();
                                          CComBSTR msg="Update Title: ";
                                          msg.AppendBSTR(title);
                                          msg.AppendBSTR(sz);
                                          msg.Append(", Download Size:");
                                          msg.Append(" KB");
                                          msg.Append(", Date: ");
                                          msg.AppendBSTR(startDate);
                                          msg.Append("URL: ");
                                          msg.AppendBSTR(url);
                                          ::MessageBoxW(0, msg, L"Title", MB_OK);
                                          */
                                          
                                    }

                              
                }

                        

        } catch (_com_error e)
        {
                USES_CONVERSION;
                CComPtr<IErrorInfo> spError;
                CComBSTR serr;

                if (S_OK == ::GetErrorInfo(0, &spError))
                {
                        spError->GetDescription(&serr);
                        errormsg(e.Error(), W2CA(serr));
                }
                errormsg(e.Error(), e.ErrorMessage());
        }
        ::CoUninitialize();
        return 0;
}
danw11Asked:
Who is Participating?
 
chip3dCommented:
do you know the eventinterface for the IUpdate object, should be something like IUpdateEvents...
This is an example of how you can connect the event sink

//////////////////////// header: SINK.h \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

#include <windows.h>
#include <comip.h>
#include <ocidl.h>

#import "c:\Program Files\Common Files\InstallShield\UpdateService\agent.exe"

typedef _com_ptr_t<_com_IIID<IConnectionPoint, &__uuidof(IConnectionPoint)> > IConnectionPointPtr;
typedef _com_ptr_t<_com_IIID<IConnectionPointContainer, &__uuidof(IConnectionPointContainer)> > IConnectionPointContainerPtr;
typedef _com_ptr_t<_com_IIID<IUnknown, &__uuidof(IUnknown)> > IUnknownPtr;
typedef _com_ptr_t<_com_IIID<IDispatch, &__uuidof(IDispatch)> > IDispatchPtr;

// helper object to connect sink
class CSink
{
    public:

        CSink() : cookie_(0) {}
               
        ~CSink()
        {
              disconnect();
        }

        bool connect(IUnknownPtr connectTo, REFIID riid, IUnknownPtr me)
        {
              if (connectTo == 0 || me == 0) return false;
            disconnect();            // Tear down any existing connection...

            try {
                cpc_ = connectTo;

                if (cpc_ == 0) return false;

                  // Find an appropriate connection point
                  HRESULT hr = cpc_->FindConnectionPoint(riid,&cp_);

                if (hr != S_OK || cp_ == 0) {
                    cpc_.Release();
                    return false;
                }
                  
                  // Connect the sink object
                  hr = cp_->Advise(me, &cookie_);
                if (hr != S_OK)
                {
                    cpc_.Release();
                    cp_.Release();
                    cookie_ = 0;
                    return false;
                }
                  return true;
            } catch (...) {}
            return false;
        }

        bool disconnect()
        {
            if (cookie_ != 0) try {
                HRESULT hr = cp_->Unadvise(cookie_);
                cookie_ = 0;
                cp_.Release();
                cpc_.Release();
                return (hr == S_OK);
            } catch (...) {}
            return false;            
        }

        bool connected() const
        {
              return (cookie_ != 0);
        }

    private:
        DWORD cookie_;
          IConnectionPointPtr cp_;
          IConnectionPointContainerPtr cpc_;
};


// template object implementing a simple IDispatch
// here you have to give your eventinterface as templateparameter
template <class SinkInterface, const GUID *pInterfaceIID = &__uuidof(SinkInterface) > 
class TSink : private CSink, public SinkInterface
{
    public :

        void connect(IUnknownPtr connectTo)
          {
                CSink::connect(connectTo, *pInterfaceIID, this);
          }

        using CSink::disconnect;
        using CSink::connected;

             // IUnknown methods
        ULONG __stdcall AddRef()
        {
            return 2;
        }

        ULONG __stdcall Release()
        {
              return 1;
        }

        HRESULT __stdcall QueryInterface(REFIID riid, void** ppv)
        {
              if(riid == IID_IUnknown)
                    *ppv = (IUnknown*)this;
              else if(riid == IID_IDispatch)
                    *ppv = (IDispatch*)this;
            else if (riid == *pInterfaceIID)
                  *ppv = (SinkInterface *)this;
              else
              {
                    *ppv = NULL;
                    return E_NOINTERFACE;
              }
              AddRef();
              return S_OK;
        }

        // IDispatch
        HRESULT __stdcall GetTypeInfoCount(UINT* pctinfo)
        {
              *pctinfo = 0;
            return S_OK;
        }

        HRESULT __stdcall GetTypeInfo(UINT, LCID,ITypeInfo**)
        {
            return S_OK;
        }

        HRESULT __stdcall GetIDsOfNames(REFIID, LPOLESTR*,UINT, LCID,DISPID*)
        {
              return S_OK;
        }

        virtual HRESULT __stdcall Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
                                                  DISPPARAMS* pDispParams, VARIANT* pvarResult,
                                    EXCEPINFO*  pExcepInfo,  UINT* puArgErr)
        {
            return S_OK;
        }
};

// now we create a specific object to handle your events
class UpdateSink : public TSink<IUpdateEvents>
{
    public :

        HRESULT __stdcall Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
                                                  DISPPARAMS* pDispParams, VARIANT* pvarResult,
                                    EXCEPINFO*  pExcepInfo,  UINT* puArgErr)
        {
            // here you will get the events. The dispidMember tells you what event is calling
            switch (dispidMember)
            {
                case 1:
                    // call e.g. OnDownloadComplete
                    break;
                case 2:
                    // call ...
                    break;
                    // ...
            }
            return S_OK;
            // normally the dispidMember have the same order like the function are defined in the EventInterface
        }
};




///////////////////////// somewhere in your code  \\\\\\\\\\\\\\\\\\\\\\\\\\

UpdateSink sink;
sink.connect(m_spUpdate);

in this sample i suppose that the eventInterface is driven from a IDispatchinterface
if not, please tell me, than there have to be done some little changes...
0
 
chip3dCommented:
Hi danw11,
I think this article could help you to solve your problem...
http://www.codeproject.com/com/JBCOMNotify.asp
0
 
danw11Author Commented:
chip3d:

I really need some specific help on my problem.  I read this article and many others and I just don't have the expertise within the given timeframe to solve this.  I would be willing to pay consulting fee for someone to do this.

Dan
0
 
danw11Author Commented:
Chip:

Thanks.  I will test this out in a few days and see if it works for me.  I will report back and let you know.

Dan
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.