永远不会调用SetTimer回调

时间:2011-08-30 21:27:55

标签: c++ winapi

我的C ++应用程序有一个无窗口计时器,可以定期清除从未(并且永远不会)完全处理的潜在通信数据。问题是永远不会调用回调函数。我的类构造函数在它返回之前执行以下代码:

    if ( (this->m_hMsgsThread = ::CreateThread(
        NULL,                           // no security attributes
        0,                              // use default initial stack size
        reinterpret_cast<LPTHREAD_START_ROUTINE>(MessagesThreadFn), // function to execute in new thread
        this,                           // thread parameters
        0,                              // use default creation settings
        NULL                            // thread ID is not needed
        )) == NULL )
    {
        dwError = ::GetLastError();
        TRACE(_T("%s : Failed to create thread.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
        continue;
    }

    if ( (s_pCleanupTimerId = ::SetTimer( NULL, 0, MSGS_CLEANUP_PERIOD, CleanupTimerProc )) == NULL )
    {
        dwError = ::GetLastError();
        TRACE(_T("%s : Failed to create timer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
        continue;
    }

这是我对CleanupTimerProc的定义:

static void CALLBACK CleanupTimerProc( HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime )
{
    CMsgDesc * pobjMsgDesc = NULL;
    DWORD dwError = ERROR_SUCCESS;
    DWORD dwItem;
    DWORD dwList;
    DWORD dwListItems;
    DWORD dwThen, dwNow;
    const DWORD cMAX_LISTS = MSGS_MAX_EVENTS;

    do
    {
        // Kill off the old timer.
        TRACE(_T("%s : Killing cleanup timer.\r\n"), _T(__FUNCTION__));
        ASSERT(s_pCleanupTimerId == idEvent);
        ::KillTimer( hwnd, idEvent );

        // Start a new timer using the same ID.
        TRACE(_T("%s : Restarting cleanup timer.\r\n"), _T(__FUNCTION__));
        if ( (s_pCleanupTimerId = ::SetTimer( NULL, 0, MSGS_CLEANUP_PERIOD, CleanupTimerProc )) == NULL )
        {
            dwError = ::GetLastError();
            TRACE(_T("%s : Failed to create timer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
            continue;
        }

        // Get the current time.
        dwNow = ::GetTickCount();

        // Search through the message descriptor lists
        // looking for descriptors that haven't been touched in a while.
        TRACE(_T("%s : Deleting old message descriptors.\r\n"), _T(__FUNCTION__));
        ASSERT(s_pInterface != NULL);
        ASSERT(s_pInterface->pobjMessages != NULL);
        ::EnterCriticalSection( &s_pInterface->pobjMessages->m_csMsgDescriptors );
        for ( dwList = 0; dwList < cMAX_LISTS; dwList++ )
        {
            dwListItems = s_pInterface->pobjMessages->m_pobjMsgDescriptors[dwList]->GetItemCount();

            for ( dwItem = 0; dwItem < dwListItems; dwItem++ )
            {
                if ( (pobjMsgDesc = (CMsgDesc *)s_pInterface->pobjMessages->m_pobjMsgDescriptors[dwList]->Peek( NULL, dwItem )) == NULL )
                {
                    dwError = ::GetLastError();
                    TRACE(_T("%s : Failed to peek item from list.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
                    continue;
                }

                // Get the last touched time from the descriptor.
                dwThen = pobjMsgDesc->m_dwLastTouched;

                // If the specified time has elapsed, delete the descriptor.
                if ( (dwNow - dwThen) > MSGS_DESC_SHELFLIFE )
                {
                    if ( s_pInterface->pobjMessages->m_pobjMsgDescriptors[dwList]->Remove( NULL, dwItem ) == NULL )
                    {
                        dwError = ::GetLastError();
                        TRACE(_T("%s : Failed to remove item from list.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
                        continue;
                    }

                    delete pobjMsgDesc;
                    TRACE(_T("%s : Deleted old message descriptor.\r\n"), _T(__FUNCTION__));
                }
            }
        }
        ::LeaveCriticalSection( &s_pInterface->pobjMessages->m_csMsgDescriptors );
    }
    while ( 0 );
}

为什么没有调用此函数的任何想法?我是否需要在线程中创建计时器?

3 个答案:

答案 0 :(得分:3)

我可能记错了......

然而,计时器仍然需要Windows消息泵。如果你想在给定的线程中触发该计时器,那么该线程需要抽取消息,否则永远不会被调用。

同样在我看来,你正在创建一个进入无限循环的计时器回调。该函数需要退出或永远不会调用下一个计时器。

答案 1 :(得分:3)

使用CreateWaitableTimer()SetWaitableTimer()代替SetTimer()。等待计时器与WaitFor...()函数系列一起使用,例如MsgWaitForMultipleObjects(),例如:

HANDLE s_pCleanupTimer;

s_pCleanupTimer = CreateWaitableTimer(NULL, FALSE, NULL);
if( !s_pCleanupTimer )
{
    dwError = ::GetLastError();
    TRACE(_T("%s : Failed to create timer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
    continue;
}

LARGE_INTEGER DueTime;
DueTime.LowPart = -(MSGS_CLEANUP_PERIOD * 10000);
DueTime.HighPart = 0;
if( !SetWaitableTimer(s_pCleanupTimer, &DueTime, MSGS_CLEANUP_PERIOD, NULL, NULL, FALSE) )
{
    dwError = ::GetLastError();
    TRACE(_T("%s : Failed to start timer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
    CloseHandle(s_pCleanupTimer);
    s_pCleanupTimer = NULL;
    continue;
}

然后在您的消息循环(或任何其他类型的状态轮询)中:

do
{
    DWORD dwRet = MsgWaitForMultipleObjects(1, &hTimer, FALSE, INFINITE, QS_ALLINPUT);
    if( dwRet == WAIT_FAILED )
        break;

    if( dwRet == WAIT_OBJECT_0 ) // timer elapsed
        CleanupTimerProc();

    else if( dwRet == (WAIT_OBJECT_0+1) ) // pending message
        ProcessPendingMessages();
}
while( true );

答案 2 :(得分:1)

在你的功能中,我看到你正在杀掉旧计时器并启动新计时器,它看起来相当多余。我相信当Goz说你正在创造一个无限循环时,Goz是对的。

我使用你正在使用的相同的非基于窗口的未识别定时器编写了一个简短的程序,它对我来说很好。

#include <windows.h>

VOID CALLBACK TimerProc(HWND hWnd, UINT uMessage, UINT_PTR uEventId, DWORD dwTime)
{
  UNREFERENCED_PARAMETER(hWnd);
  UNREFERENCED_PARAMETER(uMessage);
  UNREFERENCED_PARAMETER(uEventId);
  UNREFERENCED_PARAMETER(dwTime);

  MessageBox(NULL, "lol", "lol", MB_OK);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
  switch (uMessage)
  {
  case WM_CREATE:
    SetTimer(NULL, 0, 1000, TimerProc);
    break;

  case WM_CLOSE:
    DestroyWindow(hWnd);
    break;

  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  }
  return DefWindowProc(hWnd, uMessage, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCommandLine, int nShowCommand)
{
  UNREFERENCED_PARAMETER(hPrevInstance);
  UNREFERENCED_PARAMETER(lpszCommandLine);
  UNREFERENCED_PARAMETER(nShowCommand);

  WNDCLASSEX wce;

  ZeroMemory(&wce, sizeof(wce));
  wce.cbSize = sizeof(wce);
  wce.hInstance = hInstance;
  wce.lpfnWndProc = WndProc;
  wce.lpszClassName = "test";

  if (RegisterClassEx(&wce) > 0)
  {
    if (CreateWindow("test", "test", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL) != NULL)
    {
      MSG msg;

      while (GetMessage(&msg, NULL, 0, 0) > 0)
      {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
      return static_cast<int>(msg.wParam);
    }
  }
  return EXIT_FAILURE;
}