2011-08-30 67 views
4

我的C++應用程序有一個無窗口計時器來定期清理永遠不會(也永遠不會)完全處理的潛在通信數據。問題是回調函數從未被調用過。我的類的構造函數執行下面的代碼,就在它返回:SetTimer回調從未被稱爲

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); 
} 

任何想法,爲什麼這功能是沒有得到叫什麼名字?我是否需要在線程內創建計時器?

+0

我希望這不是太不相關的,我不只是錯過了一些東西,但是你怎麼需要摧毀並重新創建計時器? – 2011-08-30 21:32:34

+0

'reinterpret_cast (MessagesThreadFn)'看起來很可疑。我無法想象這可能是正確的。 –

回答

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); 
3

我可能會錯誤地記住這個....

但是計時器仍需要一個Windows消息泵。如果你想讓定時器在給定線程中觸發,那麼該線程需要抽取消息或者它永遠不會被調用。

同樣,在我看來,你正在創建一個進入無限循環的定時器回調。該函數需要退出或下一個計時器永遠不會被調用。

+0

謝謝。有沒有一種方法可以製作一個計時器,只需發出一個事件的信號?這會簡單得多。 –

+0

創建一個調用Sleep(n)然後觸發事件的線程將是一個簡單的方法。如果你擔心睡眠是一個不準確的計時器,那麼你可以通過一些較小的增量睡眠,然後使用QueryPerformanceCounter來檢查所需的時間量已經過去,並且它是否觸發了事件(或者甚至直接從該線程執行工作)。如果沒有足夠的時間,然後再次睡眠。 – Goz

+1

聽起來像你想[等待定時器](http://msdn.microsoft.com/en-us/library/ms687012.aspx),吉姆。 –

1

在你的函數中,我看到你正在關閉舊計時器並開始新計時器,而且看起來相當多。我相信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; 
} 
+0

它工作,因爲你有一個消息泵;) – Goz

+1

哦,這是爲什麼? :3我的不好。 – 2011-08-30 21:53:33