青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

牽著老婆滿街逛

嚴(yán)以律己,寬以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

Timers Tutorial

轉(zhuǎn)載自:http://www.codeproject.com/KB/system/timers_intro.aspx

Table of Contents

Introduction

The goal of this article is to show the practical use of different kinds of timers. First, we will see how to use the "standard" Win32 timers, then switch to multimedia timers, mention waitable timers and queue timers. I will try to make some general comparison between these solutions. So called high-resolution timer based on functions QueryPerformanceFrequency and QueryPerformanceCounter will not be taken into account because it can be used only to measure time intervals, and not to fire events in regular time intervals.

According to MSDN, An application uses a timer to schedule an event for a window after a specified time has elapsed. It means that if we create a timer and specify a time interval of uElapse milliseconds, it will do "something" every uElapse milliseconds, until we destroy it. It is up to us to specify what "something" is.

Timers are a useful feature offered to programmers by the OS. However, they can be easily misused: using timers for different kinds of polling (e.g. check every 200 milliseconds if the user entered some value into the edit box), is almost never a good idea. Good candidates for using timers are applications which do not depend that much on users' actions, but rather on time flow.

It is important to understand that the accuracy of timers is limited. Windows is not a real-time operating system (except Windows CE) and it is not reasonable to expect timers to handle very small time intervals (10 ms, for instance).

Standard Win32 Timers

When the term timer is used, it is almost always referred to this kind of timer. I use the term Win32 timer in this article simply to make a distinction between them and other timers. In some texts, these timers are called user timers because they are not kernel objects, unlike waitable and queue timers.

How do Win32 timers work? First, we create a timer, specify its elapse time, and (optionally) attach it to a window. After the timer is created, it sendsWM_TIMER messages to the window message queue, or if no window was specified, to the application queue. We can process this message to call the code that we want to be executed in regular time intervals. The timer will send WM_TIMER messages until it is destroyed.

To create a timer, we will use a Win32 function:

 Collapse
UINT_PTR SetTimer(HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc);

or its MFC equivalent:

 Collapse
UINT CWnd::SetTimer(UINT_PTR nIDEvent, UINT nElapse, 
	void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT_PTR, DWORD)); 

Arguments

  • hWnd - The handle of the window to which the timer is associated; may be NULL, in which case nIDEvent is ignored, and the return value serves as the timer identifier.
  • nIDEvent - A nonzero timer identifier.
  • uElapse - Timer's time-out interval in milliseconds.
  • lpTimerFunc - An application-defined callback function that processes WM_TIMER messages. May be NULL (more often than not, it is).

Return Value

  • The timer identifier. If hWnd is non-NULL, than it is equal to nIDEvent. In case of error, it is zero.

At some point, we will want to stop the "ticking" of the timer. We can do this by destroying it:

 Collapse
BOOL KillTimer(HWND hWnd, UINT_PTR uIDEvent);

or its MFC equivalent:

 Collapse
BOOL CWnd::KillTimer(UINT_PTR nIDEvent);

Arguments

  • hWnd - The same value as in the call to SetTimer
  • uIDEvent - The timer identifier

Return Value

  • If the function succeeds, TRUE; if it fails, FALSE

A typical use of Win32 timers from a CWnd - derived class looks like this:

 Collapse
void CTimersDlg::OnButtonBegin()
{
	.
	.
	.
    // create the timer

    SetTimer (m_nTimerID, uElapse, NULL);
}

void CTimersDlg::OnButtonStop()
{
    // destroy the timer
    KillTimer(m_nTimerID);
}

void CTimersDlg::OnTimer(UINT nIDEvent)  // called every uElapse milliseconds
{
	// do something, but quickly
	.
	.
	.

	CDialog::OnTimer(nIDEvent);
}

If we need to check our Inbox for new mail every half an hour, Win32 timers are all we want. However, for more accurate time measurement (elapsed time less than 1 sec), these timers are hardly the solution. The main reason is that timer posts WM_TIMER messages to a message queue, and we can never be sure when this message will be processed. Now, you might think that setting lpTimerFunc is a solution to this problem, but that is not the case. If you specify lpTimerFunc, the default window procedure calls it only when it processes WM_TIMER. So, we will still wait for WM_TIMER to be processed.

Note that with a Win32 timer event processing is done from the UI thread. The nice aspect of this fact is that we don't need to worry about corrupting our data from a timer event handler; on the flip side, the time spent in a WM_TIMER handler will affect the responsiveness of the UI. If you don't believe me, try calling something like ::Sleep(10000); within CTimersDlg::OnTimer().

Multimedia Timers

In the original version of this article, written 8 years ago, I described the multimedia timers in detail. In the meantime, they have become deprecated in favor of queue timers. If you are interested about the reasons, check out this Larry Osterman's blog post. Anyway, even at the time I originally wrote the article, the only reason to prefer multimedia timers over the queue timers was the fact that the later were introduced with Windows 2000 which was a relatively new system.

The multimedia timer is a high-resolution timer that does not post any messages to message queues. Instead, it calls the specified callback function directly on a separate thread (or, alternatively, it can set or pulse the specific event, but that option will not be covered in this article). Therefore, it is more accurate than the standard Win32 timer, but also more dangerous. Here, we do not have a message queue to protect us if we specify short elapse time.

To use multimedia timers in your projects, you should include Mmsystem.h, and link it with Winmm.lib.

The first step when using multimedia timers is setting the timer resolution. What is timer resolution? It determines the accuracy of the timer. For instance, if elapse time is 1000, and resolution is 50, multimedia timer will "tick" every 950 - 1050 milliseconds.

That sounds great. Why don't we just set the timer resolution to zero, and have an absolutely accurate timer? That's because different systems support different minimum values for the multimedia timer resolution. We can obtain this minimum value by calling:

 Collapse
MMRESULT timeGetDevCaps(LPTIMECAPS ptc, UINT cbtc); 

Arguments

  • ptc - Pointer to a TIMECAPS structure. It is filled with information about the resolution of the timer device
  • cbtc - Size of TIMECAPS (sizeof (TIMECAPS)).

Return Value

  • TIMERR_NOERROR if successful or TIMERR_STRUCT if it fails

TIMECAPS is pretty simple:

 Collapse
typedef struct {
    UINT wPeriodMin;
    UINT wPeriodMax;
	} TIMECAPS;
  • wPeriodMin - Minimum supported resolution
  • wPeriodMax - Maximum supported resolution

We need to pick our minimum resolution to be in this range. Now, when we have it, let's set the resolution. We will do it by calling the function:

 Collapse
MMRESULT timeBeginPeriod(UINT uPeriod);

Arguments

  • uPeriod - Minimum timer resolution

Return Value

  • TIMERR_NOERROR if successful or TIMERR_NOCANDO if the resolution specified in uPeriod is out of range

Now that we set the resolution, let's create the timer. The multimedia timer equivalent of SetTimer, looks like this:

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

Arguments

  • uDelay - Event delay, in milliseconds. Pretty much the same as uElapse in SetTimer
  • uResolution - Resolution of the timer event, in milliseconds.
  • lpTimeProc - Pointer to the callback function that we want to be called periodically
  • dwUser - User data passed to the callback function
  • fuEvent - Timer event type. May be either TIME_ONESHOT, in which case the callback function is called only once, or TIME_PERIODIC for periodic calling

Return Value

  • An identifier for the timer event if successful or NULL if it fails

Let's take a look at the callback function. It is declared like this:

 Collapse
void CALLBACK TimeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); 

Arguments

  • uID - Timer ID, returned by timeSetEvent
  • uMsg - Reserved
  • lpTimeProc - Pointer to the callback function that we want to be called periodically
  • dwUser - User data passed to the callback function
  • dw1, dw2 - Reserved

Eventually, we will need to destroy the timer. We can accomplish this by a call to the function:

 Collapse
MMRESULT timeKillEvent(UINT uTimerID);

Argument

  • uTimerID - Timer ID

Return Value

  • TIMERR_NOERROR if successful or MMSYSERR_INVALPARAM if the specified timer event does not exist

Remember setting the timer resolution? Well, after we are finished with the timer, we should "reset" the timer resolution with a call to:

 Collapse
MMRESULT timeEndPeriod(UINT uPeriod);

Argument

  • uPeriod - The same as in timeBeginPeriod

Return Value

  • TIMERR_NOERROR if successful or TIMERR_NOCANDO if fails

The multimedia timer version of the example from the previous chapter:

 Collapse
void CTimersDlg::OnButtonBegin()
{
	.
	.
	.
    // Set resolution to the minimum supported by the system

    TIMECAPS tc;
    timeGetDevCaps(&tc, sizeof(TIMECAPS));
    m_uResolution = min(max(tc.wPeriodMin, 0), tc.wPeriodMax);
    timeBeginPeriod(resolution);

    // create the timer

    m_idEvent = timeSetEvent(
        m_elTime,
        resolution,
        TimerFunction,
        (DWORD)this,
        TIME_PERIODIC);
}

void CTimersDlg::OnButtonStop()
{
    // destroy the timer
    timeKillEvent(m_idEvent);

    // reset the timer
    timeEndPeriod (m_uResolution);
}

void CTimersDlg::MMTimerHandler(UINT nIDEvent) // called every elTime milliseconds
{
// do what you want to do, but quickly
	.
	.
	.
}

void CALLBACK TimerFunction(UINT wTimerID, UINT msg,
    DWORD dwUser, DWORD dw1, DWORD dw2)
    {
    // This is used only to call MMTimerHandler

    // Typically, this function is static member of CTimersDlg

    CTimersDlg* obj = (CTimersDlg*) dwUser;
    obj->MMTimerHandler(wTimerID);
    } 

The example shown above is written in a way to resemble the handling of standard Win32 timers. In practice, however, I wrap the functionality of multimedia timers in a separate class, and I recommend you to do the same.

As I mentioned before, a multimedia timer runs on its own thread.

Waitable Timers

Waitable timers were introduced with Windows 98 and Windows NT 4.0. and they were designed to work with threads that need to block for some times. These timers are kernel objects which are signaled in the specified time, or in regular time intervals. They can specify the callback function (actually, an asynchronous procedure call, or APC) which gets called when timer gets signaled. This callback function is usually called a completion routine.

In order to enable execution of the completion routine, the thread must be in alertable state (executing SleepEx()WaitForSingleObjectEx() ,WaitForMultipleObjectsEx()MsgWaitForMultipleObjectsEx() , SignalObjectAndWait() functions). In practice, this means that our main thread will be blocked while we are using waitable timers.

To start working with a waitable timer, we must open an existing timer, or create the new one. Creating can be accomplished with a call to:

 Collapse
HANDLE CreateWaitableTimer(LPSECURITY_ATTRIBUTES lpTimerAttributes, 
	BOOL bManualReset, LPCTSTR lpTimerName); 

Arguments

  • lpTimerAttributes - Pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the waitable timer object. Can be NULL
  • bManualReset - Specifies whether the waitable timer is manual-reset or auto-reset
  • lpTimerName - The name of the new timer. Can be NULL

Return Value

  • A handle to the waitable timer object

Another possibility is to open an existing named waitable timer.

Now, when we have a handle to the waitable timer object, we can do something useful with it. To set it, we will use the function:

 Collapse
BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER *pDueTime, 
	LONG lPeriod, PTIMERAPCROUTINE pfnCompletionRoutine, 
	LPVOID lpArgToCompletionRoutine, BOOL fResume); 

Arguments

  • hTimer - A handle to the timer object
  • pDueTime - Specifies when the state of the timer is to be set to signaled
  • lPeriod - The period of the timer in milliseconds, like uElapse in SetTimer()
  • pfnCompletionRoutine - The pointer to a completion routine. Can be NULL
  • fResume - Specifies whether to restore a system in suspended power conservation mode when the timer state is set to signaled.

Return Value

  • Nonzero if the function succeeds

Finally, here is a function that stops the waitable timer:

 Collapse
BOOL CancelWaitableTimer(HANDLE hTimer); 

Argument

  • hTimer - A handle to the timer object

Return Value

  • Nonzero if the function succeeds

The example will be a little different this time:

 Collapse
void CTimersDlg::OnButtonBegin()
{
	.
	.
	.
    // create the timer

    timerHandle = CreateWaitableTimer(NULL, FALSE, NULL);
    // set the timer

	LARGE_INTEGER lElapse;
	lElapse.QuadPart = - ((int)elapse * 10000);
	BOOL succ = SetWaitableTimer(timerHandle, &lElapse, elapse, TimerProc,
                this, FALSE);

	for (int i = 0; i < 10; i++)
		SleepEx(INFINITE, TRUE);
	CancelWaitableTimer(timerHandle);
	CloseHandle (timerHandle);
}

void CTimersDlg::WaitTimerHandler() // called every elTime milliseconds
{
// do what you want to do, but quickly
	.
	.
	.
}

void CALLBACK (LPVOID lpArgToCompletionRoutine,
                                DWORD dwTimerLowValue,
                                DWORD dwTimerHighValue)
    {
    // This is used only to call WaitTimerHandler
    // Typically, this function is static member of CTimersDlg
    CTimersDlg* obj = (CTimersDlg*) lpArgToCompletionRoutine;
    obj->WaitTimerHandler();
    } 

As you can see, we don't have OnButtonStop() now. As soon as we set the timer, we must put our calling thread into alertable state, and wait. This means that we cannot do anything in the main thread until we finish with the timer. Of course, nothing prevents us from launching a separate worker thread which won't be blocked.

What can we conclude about waitable timers? They do not spend much CPU time and they do not need a message queue. The main problem is that the thread which sets the waitable timer must put itself in an alertable state, or the completion routine will never be called.

Queue Timers

The last kind of Windows - supported timers that we are going to read about in this article is queue timers. They were introduced with Windows 2000.

Queue timers are lightweight kernel objects that reside in timer queues. Like most timers, they enable us to specify the callback function to be called when the specified due time arrives. In this case, the operation is performed by a thread in the Windows thread pool.

Here, for the sake of simplicity, we are not going to create our timer queues. Instead, we will put our queue timers into default timer queue, provided by the OS.

First, we need to create a timer and add it to the default timer queue. For this, we'll make a call to:

 Collapse
BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue , 
	WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, 
	DWORD Period, ULONG Flags); 

Arguments

  • phNewTimer - Pointer to a handle; this is an out value
  • TimerQueue - Timer queue handle. For the default timer queue, NULL
  • Callback - Pointer to the callback function
  • Parameter - Value passed to the callback function
  • DueTime - Time (milliseconds), before the timer is set to the signaled state for the first time
  • Period - Timer period (milliseconds). If zero, timer is signaled only once
  • Flags - One or more of the next values (table taken from MSDN):
WT_EXECUTEINTIMERTHREADThe callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations.
WT_EXECUTEINIOTHREADThe callback function is queued to an I/O worker thread. This flag should be used if the function should be executed in a thread that waits in an alertable state.

The callback function is queued as an APC. Be sure to address reentrancy issues if the function performs an alertable wait operation.

WT_EXECUTEINPERSISTENTTHREADThe callback function is queued to a thread that never terminates. This flag should be used only for short tasks or it could affect other timer operations.

Note that currently no worker thread is persistent, although no worker thread will terminate if there are any pending I/O requests.

WT_EXECUTELONGFUNCTIONSpecifies that the callback function can perform a long wait. This flag helps the system to decide if it should create a new thread.
WT_EXECUTEONLYONCEThe timer will be set to the signaled state only once.

Return Value

  • Nonzero if the function succeeds

The callback function is really pretty simple:

 Collapse
VOID CALLBACK WaitOrTimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired); 

Arguments

  • lpParameter - Pointer to user-defined data
  • TimerOrWaitFired - always TRUE for timer callbacks

To cancel a queue timer, use the function:

 Collapse
BOOL DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent); 

Arguments

  • TimerQueue - A handle to the (default) timer queue
  • Timer - A handle to the timer
  • CompletionEvent - A handle to an optional event to be signaled when the function is successful and all callback functions have completed. Can be NULL.

Return Value

  • Nonzero if the function succeeds

The example for queue timers is given below:

 Collapse
void CTimersDlg::OnButtonBegin()
{
	.
	.
	.
    // create the timer

	BOOL success = ::CreateTimerQueueTimer(
		&m_timerHandle,
		NULL,
		TimerProc,
		this,
		0,
		elTime,
		WT_EXECUTEINTIMERTHREAD);
}

void CTimersDlg::OnButtonStop()
{
    // destroy the timer
	DeleteTimerQueueTimer(NULL, m_timerHandle, NULL);
	CloseHandle (m_timerHandle);
}

void CTimersDlg::QueueTimerHandler() // called every elTime milliseconds
{
// do what you want to do, but quickly
	.
	.
	.
}

void CALLBACK TimerProc(void* lpParametar,
                                    BOOLEAN TimerOrWaitFired)
    {
    // This is used only to call QueueTimerHandler
    // Typically, this function is static member of CTimersDlg
    CTimersDlg* obj = (CTimersDlg*) lpParametar;
    obj->QueueTimerHandler();
    } 

As you can see, queue timers are pretty easy to use. I can also add that they are very accurate, and "resource friendly".

As I noted at the beginning of this chapter, queue timers are supported on Windows 2000 and later. If you do not want to support older Windows versions, they are perfect, and should be used instead of multimedia timers.

Conclusion

What's the moral of the whole story?

When you decide that you need a timer in your application, choosing between the different timer variants should not be that hard. Follow these simple rules:

  1. If you want your application to work on every 32 bit Windows platform, you do not need high precision, and the callback operation is fast enough not to disrupt the UI responsiveness, use a standard Win32 timer.
  2. If you want your application to work on every 32 bit Windows platform, and you need high precision, use the multimedia timer.
  3. If you want your application to work on Windows 98/NT4 and later, you need low system overhead, and can afford to block the calling thread, use the waitable timer.
  4. If you want a high-precision, low-overhead, non-blocking timer that will work on Windows 2000 and later, use the queue timer.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


posted on 2010-10-12 23:03 楊粼波 閱讀(1247) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            美脚丝袜一区二区三区在线观看| 国产日韩欧美三区| 一本色道婷婷久久欧美| 91久久国产综合久久蜜月精品| 欧美一区二区性| 久久影视三级福利片| 免费观看国产成人| 亚洲日本va午夜在线电影| 99视频在线观看一区三区| 亚洲欧美区自拍先锋| 久久久www成人免费毛片麻豆| 久久久久国产精品一区| 欧美国产先锋| 国产精品香蕉在线观看| 在线成人激情| 亚洲免费影视第一页| 久久久久久一区| 亚洲精品免费网站| 久久gogo国模啪啪人体图| 久久超碰97中文字幕| 鲁大师影院一区二区三区| 亚洲国产成人精品久久| 一本综合精品| 久久久亚洲精品一区二区三区| 欧美精品福利在线| 国产视频久久网| 一本久久青青| 久久裸体艺术| 夜夜嗨av一区二区三区四季av | 亚洲国产欧美不卡在线观看| 一区二区日韩| 欧美顶级艳妇交换群宴| 国产情人综合久久777777| 亚洲乱码精品一二三四区日韩在线 | 欧美国产日韩精品| 国产一区二区三区四区三区四| 99综合在线| 欧美成人国产一区二区| 小辣椒精品导航| 国产精品久久久久久久久久久久 | 欧美成人精品福利| 国产午夜精品一区理论片飘花| 夜夜嗨网站十八久久| 欧美本精品男人aⅴ天堂| 午夜久久久久久| 国产精品久久久久毛片大屁完整版| 亚洲精品国产精品乱码不99| 老司机一区二区| 欧美在线观看一区| 国产亚洲精品一区二555| 午夜精品视频网站| 亚洲视频1区| 国产精品国产精品国产专区不蜜| 亚洲人成网站在线播| 欧美成人免费在线观看| 久久久久五月天| 尤物九九久久国产精品的特点 | 欧美成人精品福利| 亚洲韩日在线| 亚洲高清av| 欧美黄色日本| 日韩午夜中文字幕| 亚洲国产精品久久久久| 美女在线一区二区| 亚洲欧洲中文日韩久久av乱码| 欧美 日韩 国产精品免费观看| 午夜精品久久久久影视| 久久久女女女女999久久| 亚洲在线视频观看| 国产亚洲欧美一区二区| 久久婷婷丁香| 免费观看一级特黄欧美大片| 最新亚洲一区| 夜夜嗨av一区二区三区| 国产精品视频福利| 久久综合久色欧美综合狠狠| 久久综合图片| 亚洲网站视频| 欧美亚洲综合另类| 亚洲国产精品v| 99国产精品久久久| 国产日韩精品久久| 欧美成人免费在线观看| 欧美日韩国产小视频| 欧美亚洲三区| 久久午夜电影| 亚洲深夜福利在线| 欧美一区国产一区| 亚洲美女性视频| 亚洲一区免费在线观看| 国产综合久久久久影院| 亚洲国产精品成人综合色在线婷婷 | 午夜亚洲性色福利视频| 亚洲电影在线免费观看| 日韩午夜免费视频| 激情亚洲成人| 这里是久久伊人| 狠狠色综合网站久久久久久久| 亚洲国产日韩精品| 国产欧美日韩中文字幕在线| 亚洲国产精品第一区二区三区| 国产欧美精品一区aⅴ影院| 亚洲盗摄视频| 国内成+人亚洲| 亚洲午夜精品视频| 日韩一级免费观看| 久久九九国产精品| 羞羞答答国产精品www一本| 欧美黄在线观看| 麻豆九一精品爱看视频在线观看免费 | 日韩系列在线| 亚洲国产va精品久久久不卡综合| 亚洲神马久久| 在线亚洲一区二区| 久久久精品国产一区二区三区| 亚洲视频在线观看视频| 久久尤物视频| 久久久免费av| 国产精品亚洲欧美| 亚洲裸体视频| 一本久久综合| 欧美久久影院| 亚洲国产精品久久久| 一区二区在线视频观看| 亚久久调教视频| 亚洲综合日韩在线| 国产精品精品视频| 91久久精品www人人做人人爽 | 亚洲人屁股眼子交8| 欧美在线看片| 欧美在线精品一区| 国产精品私房写真福利视频| 99re热这里只有精品免费视频| 亚洲乱码国产乱码精品精可以看 | 一区二区三区四区国产精品| 亚洲精品小视频在线观看| 久热精品视频在线观看| 美女精品视频一区| 亚洲成色777777在线观看影院| 欧美在线亚洲| 美女999久久久精品视频| 1024国产精品| 欧美国产日韩在线| 一区二区不卡在线视频 午夜欧美不卡在 | 久热精品在线视频| 黑人巨大精品欧美一区二区| 欧美一区亚洲二区| 免费一级欧美在线大片| 在线免费观看日本一区| 你懂的国产精品| 日韩视频二区| 校园激情久久| 在线日韩视频| 欧美日韩视频第一区| 99人久久精品视频最新地址| 亚洲综合激情| 激情久久影院| 欧美精选一区| 亚洲免费影视| 欧美高清不卡| 亚洲欧美激情一区二区| 国内一区二区在线视频观看| 欧美国产视频一区二区| 亚洲一区久久久| 欧美激情精品久久久久久变态| 一本色道久久综合狠狠躁篇的优点 | 欧美成人激情视频免费观看| 亚洲理论在线| 国产美女诱惑一区二区| 久久久久久久久蜜桃| 91久久国产自产拍夜夜嗨| 欧美在线观看视频一区二区三区| 亚洲国产另类久久精品| 国产精品国产a级| 久久综合电影| 亚洲电影免费观看高清完整版在线观看 | 激情五月婷婷综合| 欧美日韩国产黄| 久久国产精品黑丝| 日韩视频一区二区在线观看| 久久久激情视频| 一区二区三区视频在线观看| 国产综合久久久久久鬼色| 欧美午夜不卡在线观看免费 | 欧美一区二区三区免费看| 亚洲第一中文字幕| 国产伦精品一区二区三区在线观看| 免费亚洲电影在线| 欧美一区二区精品在线| 日韩亚洲一区二区| 亚洲丁香婷深爱综合| 久久午夜精品一区二区| 亚洲综合久久久久| 一本色道久久加勒比精品| 亚洲国产另类久久久精品极度 | 亚洲一区中文字幕在线观看| 亚洲激情视频在线| 伊人精品在线| 激情欧美丁香| 激情欧美亚洲|