天天看點

多媒體定時器(timeSetEvent)及使用pDlg->UpdateData()錯誤

之前一直使用SetTimer和OnTimer來定時,知道今天碰到了一個應用,運作結果反應SetTimer定時不準,研究了timeSetEvent/timeKillEvent的使用,其可以實作1ms的定時精度。

  1. 使用這兩個函數時,需要在頭部添加如下語句:

        #include <MMSystem.h>

        #pragma comment(lib, “Winmm.lib”)

  2. timeSetEvent的函數聲明如下:

      MMRESULT timeSetEvent(UINT uDelay, UINT uResolution, LPTIMECALLBACK lpTimeProc, WORD dwUser, UINT fuEvent);

    參數說明:

        uDelay:指定定時的時間,機關是毫秒;

        uResolution:以毫秒指定延時的精度,數值越小,定時器事件的分辨率越高,預設值為1ms;

        lpTimeProc:指向一個回調函數,該回調函數包含需要定時執行的代碼;

        dwUser:存放使用者提供的回調資料;

        fuEvent:指定定時器事件的類型:TIME_ONESHOT表示uDelay毫秒後隻産生一次事件,TIME_PERIODIC表示每隔uDelay毫秒周期性地産生事件。

  3. 回調函數的形式如下:

    void CALLBACK CXXXDlg::MMTimeProc(UINT uID, UINT uMsg, DWORD dwUsers, DWORD dw1, DWORD dw2)

    {

      CXXXDlg pDlg = (CXXXDlg)(dwUsers);

      //添加你的處理代碼

      …

    }

    當回調函數是類裡面的函數時,一定要聲明為靜态函數。

  4. 例子:

    MMRESULT timer_id = timeSetEvent(100, 1, (LPTIMECALLBACK)MMTimeProc, (DWORD)this, TIME_PERIODIC);

      這條語句設定了一個定時器事件,定時精度是1ms,每隔100ms執行一次MMTimeProc指定的回調函數。

      timer_id辨別該定時器事件,當不再使用時,可以使用timeKillEvent結束該定時器事件,即timeKillEvent(timer_id)。

  5. 對回調函數的編寫需要注意,如果不是類的成員函數,則可以直接這樣定義

    void CALLBACK MMTimeProc(UINT uID, UINT uMsg, DWORD dwUsers, DWORD dw1, DWORD dw2)

    {

      //添加你的處理代碼

      …

    }

    但是由于在MFC中使用,聲明為外部函數會導緻通路類成員的困難。是以可以将其定義為類的成員函數,但是需要将其定義為靜态成員函數,如此一來,仍舊無法直接通路類的其他成員函數,因為對非靜态成員函數的通路需要類對象。

    注意到timeSetEvent的第四個參數,為使用者提供的回調資料,是以可以将目前對象的this指針傳遞給回調函數,然後從回調函數的第三個參數dwUsers解析出來。這樣,不論将其聲明為外部函數還是類的靜态成員函數,均可以對其中的成員資料或函數進行通路。

  6. pDlg->UpdateData()報錯

    當我将回調函數聲明為類的靜态成員函數,并将this指針傳遞給回調函數時,發現在回調函數内pDlg->UpdateData(TRUE)的執行出現如下圖所示錯誤

    多媒體定時器(timeSetEvent)及使用pDlg-&gt;UpdateData()錯誤
    針對這一錯誤,存在兩種較為簡單的解決方式:
  • 使用control變量或直接擷取控件指針對控件的變量進行更新:

    pDlg->m_editCtrl.SetWindowText(pDlg->m_value2);

    pDlg->GetDlgItem(IDC_EDIT2)->SetWindowText(pDlg->m_value2);

  • 最簡單的辦法,将Debug改為Release即可正常運作。