通过异步程序调用(APC)实现的定时功能

转载 2016年05月31日 09:51:19

定时器是一个在特定时间或者规则间隔被激发的内核对象。结合定时器的异步程序调用可以允许回调函数在任何定时器被激发的时候执行。本文的例子代码显示了如何实现。
  使用本定时器时,你需要把常量_WIN32_WINNT定义为0x0400,并且此常量应该在包之前定义,以确保声明合适的定时器原型函数。
   通过调用CreateWaitableTimer()可以创建一个定时器,此函数返回一个指向内核对象的句柄。若定时器已经存在,你可以通过使用 OpenWaitableTimer()获得一个进程相关的句柄。无论是通过CreateWaitableTimer() 还是通过OpenWaitableTimer()获得的句柄,在不需要定 时器时必须释放,方法是使用函数CloseHandle()。
  定时 的时间通过调用SetWaitableTimer()来设置,可以设置为一个特定的时刻(如December 16, 1999 at 9:45 PM)或者一个相对的时间(如从现在起每五分钟)。函数SetWaitableTime()定时的时间参数要求LARGE_INTEGER类型。这个值应 该符合在结构体FILETIME中描述的格式。如果值是正的,代表一个特定的时刻。如果值是负的,代表以100纳秒为单位的相对时间。后面的示例代码中使 用的是相对时间。在调用SetWaitableTimer()函数后,定时器将在每5秒被激发一次。
  你也可以将定时器设置为周期性的自我激 发,方法是向SetWaitableTimer()的第三个参数传递一个周期参数(以毫秒为单位)。在CreateWaitableTimer()的第二 个参数传递FALSE可以产生一个自动归零的定时器。本例设置周期为两秒的定时器。
  当设置了定时器之后,你就可以将APC与其结合起来。这里把APC函数称作完全例程。完全例程的地址作为SetWaitableTimer()的第四个参数。第五个参数是一个空类型的指针,你可以使用它来传递完全例程的参数。
  在所有的APC中,要执行一个完全例程则线程必须处于监听状态。完全例程将总是被调用SetWaitableTimer()的相同的线程执行,所以此线程必须将必须其自身置于监听状态。可以调用下面的任何一个监听函数来完成监听状态的设置:

  • SleepEx();
  • WaitForSingleObjectEx();
  • WaitForMultipleObjectsEx();
  • MsgWaitForMultipleObjectsEx();
  • SignalObjectAndWait();

  任何一个线程都有一个APC队列。在调用上面的任何一个函数时,如果线程的APC队列中有实体,则此线程不会进入休眠状态,取而代之要做的是将实体从APC队列中取出,然后调用相应的完全例程。
   如果在APC队列中不存在实体,那么线程将会被挂起,直至等待条件满足为止。满足等待条件的有:一个实体加入到APC队列中,超时,激活句柄等,以及在 调用MsgWaitForMultipleObjectsEx()情况下,一个消息进入到线程的一个消息队列中。若等待条件满足的是APC队列中的一个实 体,那么线程会被激活,并且执行完全例程,这种情况下的函数的返回值是 WAIT_IO_COMPLETION.

【重要提示】

1、在执行完一个完全例程之后,系统会检查在APC中剩下的实体以处理。一个监视函数仅仅在处理完所有APC实体后才返回。因此,如果实体加入到 APC队列的速度比处理的更快的话,则调用这些函数可能永远也不能返回。特别当定时等待的时间比起要求执行完全例程的时间更短的话,这种情况更容易发生。 
2、当使用APC来实现定时器时,设置定时的线程不应该等待定时器的句柄。如果等待定时器的句柄的话,则唤起这个线程的原因是定时器被激活,而 不是有实体加入到APC队列中。这时线程将不再处于监听状态,所以完全例程也不会被调用。在本例中,Sleep()被用于将线程置于监听状态。在定时器激 活后,如果有实体被加入到此线程的APC队列中时,Sleep()就会唤醒此线程。

【示例代码】

#define _WIN32_WINNT 0x0500

#include <windows.h>
#include <stdio.h>

#define _SECOND 10000000

typedef struct _MYDATA {
TCHAR *szText;
DWORD dwValue;
} MYDATA;

VOID CALLBACK TimerAPCProc(
LPVOID lpArg, // Data value
DWORD dwTimerLowValue, // Timer low value
DWORD dwTimerHighValue ) // Timer high value

{
MYDATA *pMyData = (MYDATA *)lpArg;

printf( "Message: %s/nValue: %d/n/n", pMyData->szText,
pMyData->dwValue );
MessageBeep(0);

}

void main( void )
{
HANDLE hTimer;
BOOL bSuccess;
__int64 qwDueTime;
LARGE_INTEGER liDueTime;
MYDATA MyData;
TCHAR szError[255];

MyData.szText = "This is my data.";
MyData.dwValue = 100;

if ( hTimer = CreateWaitableTimer(
NULL, // Default security attributes
FALSE, // Create auto-reset timer
"MyTimer" ) ) // Name of waitable timer
{
__try
{
// Create an integer that will be used to signal the timer
// 5 seconds from now.
qwDueTime = -5 * _SECOND;

// Copy the relative time into a LARGE_INTEGER.
liDueTime.LowPart = (DWORD) ( qwDueTime & 0xFFFFFFFF );
liDueTime.HighPart = (LONG) ( qwDueTime >> 32 );

bSuccess = SetWaitableTimer(
hTimer, // Handle to the timer object
&liDueTime, // When timer will become signaled
2000, // Periodic timer interval of 2 seconds
TimerAPCProc, // Completion routine
&MyData, // Argument to the completion routine
FALSE ); // Do not restore a suspended system

if ( bSuccess )
{
for ( ; MyData.dwValue < 1000; MyData.dwValue += 100 )
{
SleepEx(
INFINITE, // Wait forever
TRUE ); // Put thread in an alertable state
}

}
else
{
wsprintf( szError, "SetWaitableTimer failed with Error /
%d.", GetLastError() );
MessageBox( NULL, szError, "Error", MB_ICONEXCLAMATION );
}

}
__finally
{
CloseHandle( hTimer );
}
}
else
{
wsprintf( szError, "CreateWaitableTimer failed with Error %d.",
GetLastError() );
MessageBox( NULL, szError, "Error", MB_ICONEXCLAMATION );
}
}

异步过程调用(APC)——操作系统实现异步的原理

apc可以看成就是内核里的定时器,为了给自己一个在本函数返回后还能执行的一次机会,有很多操作是需要在函数返回后才能执行. 类似于析构函数但不完全是。 apc的最大特点就是在本函数返回后才执行,而且是在...
  • suhuaiqiang_janlay
  • suhuaiqiang_janlay
  • 2015年08月15日 10:18
  • 1810

异步过程调用(APC)——操作系统实现异步的原理

apc可以看成就是内核里的定时器,为了给自己一个在本函数返回后还能执行的一次机会,有很多操作是需要在函数返回后才能执行. 类似于析构函数但不完全是。 apc的最大特点就是在本函数返回后才执行,而且是在...
  • suhuaiqiang_janlay
  • suhuaiqiang_janlay
  • 2015年08月15日 10:18
  • 1810

Windows异步过程调用(APC)

原文转载自:http://blog.sina.com.cn/s/blog_6c617ee301017nhr.html,感谢原作者。 apc可以看成就是内核里的定时器,为了给自己一个在本函数返回...
  • mfcing
  • mfcing
  • 2015年03月01日 10:09
  • 1722

用户模式异步过程调用(APC)

以前对User-Mode APC不甚了解, 最近在看一个开源项目时看到了对APC的使用。看来多看代码的确是有好处地:)废话不多说。我们来看看APC的真面目吧。 APC即asynchronous pr...
  • chenlycly
  • chenlycly
  • 2013年05月26日 16:12
  • 1286

通过异步程序调用(APC)实现的定时功能

转自http://hi.baidu.com/aiscanf/blog/item/fb00e9fd34d98f1e08244dc7.html 通过异步程序调用(APC)实现的定时功能 定时器是一个在...
  • cmh360
  • cmh360
  • 2011年09月01日 10:42
  • 696

Windows内核对象 - 通过异步程序调用(APC)实现的定时功能

Windows内核对象 - 通过异步程序调用(APC)实现的定时功能
  • trojanpizza
  • trojanpizza
  • 2011年08月09日 23:15
  • 1323

异步过程调用(APC)——操作系统实现异步的原理

apc可以看成就是内核里的定时器,为了给自己一个在本函数返回后还能执行的一次机会,有很多操作是需要在函数返回后才能执行. 类似于析构函数但不完全是。 apc的最大特点就是在本函数返回后才执行,而且是在...
  • suhuaiqiang_janlay
  • suhuaiqiang_janlay
  • 2015年08月15日 10:18
  • 1810

通过异步过程调用(APC)注入DLL

关于APC的介绍,可以参考MSDN对Asynchronous Procedure Calls的介绍(索引APCs),下面是简单翻译的一段文字。 APC(Asynchronous Procedure C...
  • jackey3Lin
  • jackey3Lin
  • 2015年11月20日 15:44
  • 288

apc 异步过程调用无法抛到主线程的问题跟踪

引发:使用项目的ipc时发现不能向主线程抛apc消息,后编写测试代码经反复测试发现测试代码也无法发送:主线程:子线程:下面的内核调试记录对本次问题查找本无益处,但还是花费了大量时间,需要记录: 因为...
  • zamzll
  • zamzll
  • 2015年06月08日 09:05
  • 912

注入(2)--APC(Asynchronous Procedure Call)注入(异步过程调用)

APC(Asynchronous Procedure Call,异步过程调用)是在一个特定线程环境下被异步执行的函数,分为用户模式APC和内核模式APC。每个线程都有一个APC队列。在用户模式下,当线...
  • sinat_34260423
  • sinat_34260423
  • 2016年10月02日 00:02
  • 303
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:通过异步程序调用(APC)实现的定时功能
举报原因:
原因补充:

(最多只允许输入30个字)