Solved

ActiveX/COM Timers IE4

Posted on 1998-08-31
13
336 Views
Last Modified: 2013-11-25
I built a timer object (ATL wizard), that has methods
resume(), stop(),etc. and event doit().
All I want for now is that this control
fires doit at every say 10 seconds. I used VC++5.0
and build a simple object(dll, apartment), it works fine with EXCEL VBA but not with IE4. I also did it with VC++ 6.0 (easier to do, since object wizard does put events in IDL file, and proxy generation is done in the class view) again works with VBA, not with IE4 (VBScript). I also did try it as Full control, same result. proxy class
invokes the doit but nothing happens on the IE4 side ??

my HTML file is follows:

<HTML>
<HEAD>
<TITLE>ATL 3.0 test page for object SimpleTimer</TITLE>
</HEAD>
<BODY>
<OBJECT ID="SimpleTimer" CLASSID="CLSID:30ECD052-40DC-11D2-8D97-00E0290E3128"></OBJECT>
<script LANGUAGE="VBSCRIPT">
      Sub StartTimer()
            SimpleTimer.period = 6000
            SimpleTimer.resume
      End Sub
      Sub StopTimer()
            SimpleTimer.stop
      End Sub
      Sub SimpleTimer_doit()
            msgbox "handled in vb"
      End Sub
</script> <input LANGUAGE="VBScript" TYPE="BUTTON"
VALUE="   Start  " ONCLICK="call StartTimer()" NAME="btn"> <input LANGUAGE="VBScript"
TYPE="BUTTON" VALUE="   Stop   " ONCLICK="call StopTimer()" NAME="btn"> </p>
</BODY>
</HTML>

**************************************
idl file as follows :


// Simple_Timer.idl : IDL source for Simple_Timer.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (Simple_Timer.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";
      [
            object,
            uuid(30ECD051-40DC-11D2-8D97-00E0290E3128),
            dual,
            helpstring("ISimpleTimer Interface"),
            pointer_default(unique)
      ]
      interface ISimpleTimer : IDispatch
      {
            [propget, id(1), helpstring("property period")] HRESULT period([out, retval] long *pVal);
            [propput, id(1), helpstring("property period")] HRESULT period([in] long newVal);
            [propget, id(2), helpstring("property loop")] HRESULT loop([out, retval] long *pVal);
            [propput, id(2), helpstring("property loop")] HRESULT loop([in] long newVal);
            [id(3), helpstring("method resume")] HRESULT resume();
            [id(4), helpstring("method stop")] HRESULT stop();
            [id(5), helpstring("method pause")] HRESULT pause();
      };

[
      uuid(30ECD045-40DC-11D2-8D97-00E0290E3128),
      version(1.0),
      helpstring("Simple_Timer 1.0 Type Library")
]
library SIMPLE_TIMERLib
{
      importlib("stdole32.tlb");
      importlib("stdole2.tlb");

      [
            uuid(30ECD053-40DC-11D2-8D97-00E0290E3128),
            helpstring("_ISimpleTimerEvents Interface")
      ]
      dispinterface _ISimpleTimerEvents
      {
            properties:
            methods:
            [id(1), helpstring("method doit")] HRESULT doit();
      };

      [
            uuid(30ECD052-40DC-11D2-8D97-00E0290E3128),
            helpstring("SimpleTimer Class")
      ]
      coclass SimpleTimer
      {
            [default] interface ISimpleTimer;
            [default, source] dispinterface _ISimpleTimerEvents;
      };
};
*************************************************
header:

// SimpleTimer.h : Declaration of the CSimpleTimer

#ifndef __SIMPLETIMER_H_
#define __SIMPLETIMER_H_

#include "resource.h"       // main symbols
#include "Simple_TimerCP.h"
#include <mmsystem.h>
/////////////////////////////////////////////////////////////////////////////
// CSimpleTimer
class ATL_NO_VTABLE CSimpleTimer :
      public CComObjectRootEx<CComSingleThreadModel>,
      public CComCoClass<CSimpleTimer, &CLSID_SimpleTimer>,
      public IConnectionPointContainerImpl<CSimpleTimer>,
      public IDispatchImpl<ISimpleTimer, &IID_ISimpleTimer, &LIBID_SIMPLE_TIMERLib>,
      public IProvideClassInfo2Impl<&CLSID_SimpleTimer, &DIID__ISimpleTimerEvents, &LIBID_SIMPLE_TIMERLib>,
      public CProxy_ISimpleTimerEvents< CSimpleTimer >
{
public:
      CSimpleTimer()
      {
            m_period = 10000;
            m_loop = -1;
            m_loopcount = 0;
            m_go = false;
      }

DECLARE_REGISTRY_RESOURCEID(IDR_SIMPLETIMER)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CSimpleTimer)
      COM_INTERFACE_ENTRY(ISimpleTimer)
      COM_INTERFACE_ENTRY(IDispatch)
      COM_INTERFACE_ENTRY(IConnectionPointContainer)
      COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
      COM_INTERFACE_ENTRY(IProvideClassInfo)
      COM_INTERFACE_ENTRY(IProvideClassInfo2)

END_COM_MAP()
BEGIN_CONNECTION_POINT_MAP(CSimpleTimer)
CONNECTION_POINT_ENTRY(DIID__ISimpleTimerEvents)
END_CONNECTION_POINT_MAP()


// ISimpleTimer
public:
      STDMETHOD(pause)();
      STDMETHOD(stop)();
      STDMETHOD(resume)();
      STDMETHOD(get_loop)(/*[out, retval]*/ long *pVal);
      STDMETHOD(put_loop)(/*[in]*/ long newVal);
      STDMETHOD(get_period)(/*[out, retval]*/ long *pVal);
      STDMETHOD(put_period)(/*[in]*/ long newVal);
      void perform();

protected:
      long m_period;
      long m_loop;
      long m_loopcount;
      bool m_go;
      UINT uTimerID;
      DWORD dwUser;
};
***************************************************
wizard generated proxcy class
#ifndef _SIMPLE_TIMERCP_H_
#define _SIMPLE_TIMERCP_H_

template <class T>
class CProxy_ISimpleTimerEvents : public IConnectionPointImpl<T, &DIID__ISimpleTimerEvents, CComDynamicUnkArray>
{
      //Warning this class may be recreated by the wizard.
public:
      HRESULT Fire_doit()
      {
            CComVariant varResult;
            T* pT = static_cast<T*>(this);
            int nConnectionIndex;
            int nConnections = m_vec.GetSize();
            
            for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
            {
                  pT->Lock();
                  CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
                  pT->Unlock();
                  IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
                  if (pDispatch != NULL)
                  {
                        VariantClear(&varResult);
                        DISPPARAMS disp = { NULL, NULL, 0, 0 };
                        pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
                  }
            }
            Beep(3000,1000);
            return varResult.scode;
      
      }
};
#endif
***************************************************
and finally implementation is
// SimpleTimer.cpp : Implementation of CSimpleTimer
#include "stdafx.h"
#include "Simple_Timer.h"
#include "SimpleTimer.h"

/////////////////////////////////////////////////////////////////////////////
// global callback
void CALLBACK TimeProc(UINT uID,UINT uMsg,DWORD dwUser,DWORD dw1,DWORD dw2)
{
      CSimpleTimer* pCurrent = (CSimpleTimer*) dwUser;
      pCurrent->perform();
}
// CSimpleTimer


STDMETHODIMP CSimpleTimer::get_period(long *pVal)
{
      // TODO: Add your implementation code here
      *pVal = m_period;
      return S_OK;
}

STDMETHODIMP CSimpleTimer::put_period(long newVal)
{
      // TODO: Add your implementation code here
      m_period = newVal;
      return S_OK;
}

STDMETHODIMP CSimpleTimer::get_loop(long *pVal)
{
      // TODO: Add your implementation code here
      *pVal = m_loop;
      return S_OK;
}

STDMETHODIMP CSimpleTimer::put_loop(long newVal)
{
      // TODO: Add your implementation code here
      m_loop = newVal;
      return S_OK;
}

STDMETHODIMP CSimpleTimer::resume()
{
      // TODO: Add your implementation code here
      if(!m_go)
      {
            dwUser = (DWORD) this;
            uTimerID = timeSetEvent(m_period,100,
                  (LPTIMECALLBACK) &TimeProc,dwUser,TIME_PERIODIC);
            m_go = true;
            m_loopcount = 0;
      }

      return S_OK;
}

STDMETHODIMP CSimpleTimer::stop()
{
      // TODO: Add your implementation code here
      if(m_go)
      {
            timeKillEvent(uTimerID);
            m_go = false;
            m_loopcount = m_loop + 1;
      }
      return S_OK;
}

STDMETHODIMP CSimpleTimer::pause()
{
      // TODO: Add your implementation code here
      Sleep(m_period);
      return S_OK;
}
void CSimpleTimer::perform()
{
            if(m_loopcount < m_loop || m_loop < 0)
            {
                  Fire_doit();
                  m_loopcount++;
            }
            else stop();
}

I will increase points if you can help me solve the problem.
PS. i KNOW THERE IS A TIMER CLASS already which works, but
I need this one for some purpose, thanks
0
Comment
Question by:hasmet
  • 7
  • 4
  • 2
13 Comments
 
LVL 1

Author Comment

by:hasmet
Comment Utility
please send a comment before locking the question. unless
you have it worked and sure about it. if a comment helps
i will ask you to reply for the full credit, and I may increase the points also. thanks
0
 
LVL 1

Expert Comment

by:eburley
Comment Utility
you might want to check the return value of the event's your firing.  I would add code like ASSERT(SUCCEEDED(yourhresult)); after calls to Fire_Doit().  I suspect that may yield an error.
0
 
LVL 1

Author Comment

by:hasmet
Comment Utility
no it gives 0 return, succeded
0
 
LVL 1

Expert Comment

by:eburley
Comment Utility
no idear, sorrry.
0
 
LVL 2

Expert Comment

by:milenvk
Comment Utility
I built your project and found out that the call to the sink interface's Invoke in the proxy returns 0x8000ffff, which on my machine translates to "Catastrophic failure"!!!!! This is not the varResult.scode, but the HRESULT returned by the Invoke itself.
Whatever that means it's scary... And it really seems strange. I've developed that kind of scripted components for IE 4.0 before and I never had problems, though I don't recall of a component of mine that supported events. I'll continue the tests tomorrow... I hope I can sleep tonight - "Catastrophic failure"!!! Microsoft are original, aren't they?
0
 
LVL 1

Author Comment

by:hasmet
Comment Utility
you are right, I PUT THE FOLLOWING
if (pDispatch != NULL)
{
 VariantClear(&varResult);
 DISPPARAMS disp = { NULL, NULL, 0, 0 };
 HRESULT hr = pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
 char sil[50];
 MessageBox(NULL,itoa(hr,sil,16),"hresult",MB_OK);
}
Although varResult.scode is zero, hr is 8000ffff which is defined
in winerror.h as


// MessageId: E_UNEXPECTED
//
// MessageText:
//
//  Catastrophic failure
//
#define E_UNEXPECTED                     _HRESULT_TYPEDEF_(0x8000FFFFL)

but yes, how ? by the way the same timer works in VBA EXCEL
and hr = 0 not a Catastrophic failure !
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 1

Author Comment

by:hasmet
Comment Utility
if modify resume as follows:
***************************
STDMETHODIMP CSimpleTimer::resume()
{
      // TODO: Add your implementation code here
      if(!m_go)
      {
            dwUser = (DWORD) this;
            uTimerID = timeSetEvent(m_period,100,
                  (LPTIMECALLBACK) &TimeProc,dwUser,TIME_PERIODIC);
            m_go = true;
            m_loopcount = 0;
      }
      dwUser = (DWORD) this;
      TimeProc(uTimerID,0,dwUser,NULL,NULL);
      return S_OK;
}
***************************************
namely add two lines
      dwUser = (DWORD) this;
      TimeProc(uTimerID,0,dwUser,NULL,NULL);
then hr = 0 not a Catastrophic failure !
when starts, but when timer callsback TimeProc, it is Catastrophic failure, namely if I call it directly, it works
but not as a callback ??? a thread problem ??? or what ? I use
apartment

0
 
LVL 1

Author Comment

by:hasmet
Comment Utility
in the proxygen class, I tried

*************************
if (pDispatch != NULL)
{
IDispatch* pdisp;
HRESULT hr = pDispatch->QueryInterface(IID_IDispatch,
            (void **)&pdisp);
char sil[50];
MessageBox(NULL,itoa(hr,sil,16),"hresult0",MB_OK);
if(pdisp != pDispatch) MessageBox(NULL,"upset","disp",MB_OK);
VariantClear(&varResult);
DISPPARAMS disp = { NULL, NULL, 0, 0 };
hr = pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
MessageBox(NULL,itoa(hr,sil,16),"hresult",MB_OK);
}
***************************
when timerproc called directly in resume, no upset, all fine,
but when timerproc is called from multimedia timer, then
upset, that means I loose the reference, dispatch interface
is disconnected, some how somewhere IE4 release the interface,
what to I do, AddRef some how ? what is best ?
0
 
LVL 2

Accepted Solution

by:
milenvk earned 250 total points
Comment Utility
Hey hasmet,

I think I have an answer to your problem, that's why I lock the question. I don't know why the timer you are using does not work with IE 4.0. I suppose that it messes up with some internal stuff of IE, maybe some thread issue, or IE uses this timer also and there's kind of conflict. It's obvious that your dispatch is not valid anymore after starting the timer. But here's a simple solution: use another timer, like the windows timer functionality. I tested your project with the windows timer and it works! Here's the changed code - you can test it yourself :-)

I changed SimpleTimer.cpp

//********************************** SimpleTimer.cpp**************
// SimpleTimer.cpp : Implementation of CSimpleTimer

#include "stdafx.h"
#include "Simple_Timer.h"
#include "SimpleTimer.h"

#pragma warning(disable : 4786)

#include <map>  // The STL map class

/////////////////////////////////////////////////////////////////////////////
// global timer map
typedef std::map<UINT, CSimpleTimer*> IDtoSimpleTimerMap;
IDtoSimpleTimerMap g_timersMap;

/////////////////////////////////////////////////////////////////////////////
// Global timer map manipulation
UINT DeleteTimerFromMap(CSimpleTimer* pt)
{
  UINT ret = 0;
  IDtoSimpleTimerMap::iterator it;
  for(it = g_timersMap.begin(); it != g_timersMap.end(); it++)
  {
    if(it->second == pt)
    {
      ret = it->first;
      g_timersMap.erase(it);
      break;
    }
  }

  return ret;
}

/////////////////////////////////////////////////////////////////////////////
// global callback
void CALLBACK MyTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
  CSimpleTimer* pCurrent = (CSimpleTimer*) idEvent;
  IDtoSimpleTimerMap::iterator it = g_timersMap.find(idEvent);
  if(it != g_timersMap.end() && it != NULL)
    it->second->perform();
}

// CSimpleTimer
STDMETHODIMP CSimpleTimer::get_period(long *pVal)
{
  *pVal = m_period;
  return S_OK;
}

STDMETHODIMP CSimpleTimer::put_period(long newVal)
{
  m_period = newVal;
  return S_OK;
}

STDMETHODIMP CSimpleTimer::get_loop(long *pVal)
{
  *pVal = m_loop;
  return S_OK;
}

STDMETHODIMP CSimpleTimer::put_loop(long newVal)
{
  m_loop = newVal;
  return S_OK;
}

STDMETHODIMP CSimpleTimer::resume()
{
  if(!m_go)
  {
    UINT timer = ::SetTimer(NULL, (UINT)this, m_period, MyTimerProc);
    if(timer == 0)
    {
      MessageBox(NULL, "Failed to create a timer", "", MB_OK);
      return S_OK;
    }
    g_timersMap.insert(IDtoSimpleTimerMap::value_type(timer, this));
    m_go = true;
    m_loopcount = 0;
  }
      
  return S_OK;
}

STDMETHODIMP CSimpleTimer::stop()
{
  if(m_go)
  {
    UINT timer = DeleteTimerFromMap(this);
    if(timer != 0)
    ::KillTimer(NULL, timer);
    m_go = false;
    m_loopcount = m_loop + 1;
  }
  return S_OK;
}

STDMETHODIMP CSimpleTimer::pause()
{
  Sleep(m_period);
  return S_OK;
}
void CSimpleTimer::perform()
{
  if(m_loopcount < m_loop || m_loop < 0)
  {
    Fire_doit();
    m_loopcount++;
  }
  else
    stop();
}


By the way - the compiler produced an error in the header file at the line:

COM_INTERFACE_ENTRY(IConnectionPointContainer)

in SimpleTimer.h and I deleted it. You only need one interface entry in the interface map in ATL and IConnectionPointContainer has to be exposed like this:

COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)

Since you have that line also I simply deleted the wrong COM_INTERFACE_ENTRY(IConnectionPointContainer)
line.

I hope that works for you :-)

0
 
LVL 2

Expert Comment

by:milenvk
Comment Utility
Ooops... the line:

UINT timer = ::SetTimer(NULL, (UINT)this, m_period, MyTimerProc);

in the resume() function in my code may seem misleading. It should be:

UINT timer = ::SetTimer(NULL, 0, m_period, MyTimerProc);

See, when hWnd parameter is NULL, the nIDEvent parameter is not used also, so it should be zero. Sorry for that - I was trying some other ways then and I forgot to change the parameter when I posted this. Anyway - it works like this, but it just seems unclear. :-)
0
 
LVL 1

Author Comment

by:hasmet
Comment Utility
Yes, you are right, the timer has problem but I also
developed Full control and use WM_TIMER, that also failed
in IE4. Although it worked in VBA and activex test container also. so your timer do the job, I will quit to look for an answer
why the others dont work. thanks alot for your effort, saved
me go through all that templates to look for addref, and advise's
etc. the other error (COM_INTERFACE_ENTRY) is due to the new
wizard that they added in vc++6.0 (ATL 3.0). I realized it
this morning, and they mention it in www.wrox.com too. thanks again
0
 
LVL 1

Author Comment

by:hasmet
Comment Utility
Dou you think, WM_TIMER with full control shoul work ? then
the problem is, IsWindow(m_hWnd) fails, so I had problems ??

++++++++++++++++++++++++++++++++++

MESSAGE_HANDLER(WM_TIMER, OnTimer)

+++++++++++++++++++++++++++++
LRESULT OnTimer(UINT nIDEvent, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+++++++++++++++++++++++
LRESULT CFullTimer::OnTimer(UINT nIDEvent, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
Fire_doit();
return 0;
}
++++++++++++++++++++++++++++++++
uTimerID = ::SetTimer(m_hWnd,uTimerID, 5000, NULL);
+++++++++++++++++++++++++++++++++++++++++++++++++++
::KillTimer(m_hWnd,uTimerID);
+++++++++++++++++++++++++++++++

so I didnot use callback function when I set it. so it goes to
ontimer. anyway thanks for the lead.


0
 
LVL 2

Expert Comment

by:milenvk
Comment Utility
Yes it should work, but the control should be windowed, i.e. it should have a window. By default ATL full controls are capable of being windowless and any container that supports windowless controls (IE 4.0 is one of them) will display the control in windowless state, which means that you don't have a windows handle. You can easily override this when you add:

  m_bWindowOnly = TRUE;

to your control's constructor. Then the control will always have a window. I hope this helps :-)

0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Suggested Solutions

I made this because I wanted to get e-mail with a attached csv file so I'd would be able to import user input into a MS Excel template, but I also wanted to register/save all inputs from each day in a file on the server. 1st - It creates a temp C…
Before we dive into the marketing strategies involved with creating an effective homepage, it’s crucial that EE members know what a homepage is. In essence, a homepage is the introductory, or default page, of a website that typically highlights the …
The purpose of this video is to demonstrate how to set up the WordPress backend so that each page automatically generates a Mailchimp signup form in the sidebar. This will be demonstrated using a Windows 8 PC. Tools Used are Photoshop, Awesome…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now