Обратный вызов SetTimer никогда не вызывается - PullRequest
4 голосов
/ 31 августа 2011

В моем приложении 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 ]

3 голосов
/ 31 августа 2011

Используйте 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 );
3 голосов
/ 31 августа 2011

Возможно, я помню это неправильно ....

Однако для таймера все еще требуется насос сообщений Windows. Если вы хотите, чтобы этот таймер работал в заданном потоке, то этот поток должен качать сообщения, иначе он никогда не будет вызван.

Мне также кажется, что вы создаете обратный вызов таймера, который входит в бесконечный цикл. Функция должна выйти или следующий таймер не может быть вызван.

1 голос
/ 31 августа 2011

В вашей функции я вижу, что вы убиваете старый таймер и запускаете новый, и это выглядит довольно избыточно. Я считаю, что Гоз прав, когда говорит, что вы создаете бесконечный цикл.

Я написал короткую программу, использующую те же неопределяемые неоконные таймеры, которые вы используете, и она прекрасно работает для меня.

#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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...