现在的位置: 首页 > 综合 > 正文

miranda-APC(异步过程调用)分析

2013年11月28日 ⁄ 综合 ⁄ 共 4095字 ⁄ 字号 评论关闭

APC : asynchronous procdure call 异步过程调用
    Alertable IO(告警IO)提供了更有效的异步通知形式。ReadFileEx / WriteFileEx在发出IO请求的同时,
    提供一个回调函数(APC过程),当IO请求完成后,一旦线程进入可告警状态,回调函数将会执行。
    以下五个函数能够使线程进入告警状态:
    SleepEx
    WaitForSingleObjectEx
    WaitForMultipleObjectsEx
    SignalObjectAndWait
    MsgWaitForMultipleObjectsEx
    线程进入告警状态时,内核将会检查线程的APC队列,如果队列中有APC,将会按FIFO方式依次执行。
    如果队列为空,线程将会挂起等待事件对象。以后的某个时刻,一旦APC进入队列,线程将会被唤醒
    执行APC,同时等待函数返回WAIT_IO_COMPLETION。
    QueueUserAPC可以用来人为投递APC,只要目标线程处于告警状态时,APC就能够得到执行。
    使用告警IO的主要缺点是发出IO请求的线程也必须是处理结果的线程,如果一个线程退出时还有
    未完成的IO请求,那么应用程序将永远丢失IO完成通知。

//APC的回调函数,该函数不做任何工作,目的是通过向一个处于告警状态的线程投递该异步调用,
//激活该线程继续运行
static void __stdcall DummyAPCFunc(DWORD dwArg)
{
 /* called in the context of thread that cleared it's APC queue */
 return;
}

//另外一个APC的回调函数,在NotifyEventHooks中有调用
static void CALLBACK HookToMainAPCFunc(DWORD dwParam)
{
 THookToMainThreadItem *item=(THookToMainThreadItem*)dwParam;

 item->result=CallHookSubscribers(item->hookId,item->wParam,item->lParam);
 SetEvent(item->hDoneEvent);
 return;
}

//创建异步操作的隐藏窗口
//int LoadSystemModule(void)
//{
// InitCommonControls();
//  //创建一个异步操作的隐藏窗口
// hAPCWindow=CreateWindowEx(0,_T("STATIC"),NULL,0, 0,0,0,0, NULL,NULL,NULL,NULL); // lame
// SetWindowLong(hAPCWindow,GWL_WNDPROC,(LONG)APCWndProc);
// hStackMutex=CreateMutex(NULL,FALSE,NULL);
//  ......
//}

//销毁异步操作窗口
//int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
//{
//  ......
// CloseHandle(hStackMutex);
// CloseHandle(hMirandaShutdown);
// CloseHandle(hThreadQueueEmpty);
//  //销毁异步操作窗口
// DestroyWindow(hAPCWindow);
// return 0;
//}

//向hAPCWindow post WM_NULL,使主线程调用SleepEx()进入告警状态
// int NotifyEventHooks(HANDLE hEvent,WPARAM wParam,LPARAM lParam)
// {
//  extern HWND hAPCWindow;
//
//  if(GetCurrentThreadId()!=mainThreadId)
//     {
//         //假如是在子线程里触发的NotifyEvent,那么
//         //在这里同时通知主线程也触发该NotifyEvent
//
//   THookToMainThreadItem item;
//
//   item.hDoneEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
//   item.hookId=(int)hEvent-1;
//   item.wParam=wParam;
//   item.lParam=lParam;
//
//         //向主线程投递APC HookToMainAPCFunc
//   QueueUserAPC(HookToMainAPCFunc,hMainThread,(DWORD)&item);
//         //向hAPCWindow post WM_NULL,使主线程调用SleepEx()进入告警状态
//         //从而使主线程调用HookToMainAPCFunc,使主线程也触发该NotifyEvent
//   PostMessage(hAPCWindow,WM_NULL,0,0); // let it process APC even if we're in a common dialog
//   WaitForSingleObject(item.hDoneEvent,INFINITE);
//   CloseHandle(item.hDoneEvent);
//   return item.result;
//  }
//  else
//   return CallHookSubscribers((int)hEvent-1,wParam,lParam);
// }

//向hAPCWindow post WM_NULL,使主线程调用SleepEx()进入告警状态
//int CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam)
//{
//
// extern HWND hAPCWindow;
//
// if (name==NULL) return CALLSERVICE_NOTFOUND;
// // the service is looked up within the main thread, since the time it takes
// // for the APC queue to clear the service being called maybe removed.
// // even thou it may exists before the call, the critsec can't be locked between calls.
// if (GetCurrentThreadId() != mainThreadId) {
//  TServiceToMainThreadItem item;
//  item.wParam = wParam;
//  item.lParam = lParam;
//  item.name = name;
//  item.hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
//  QueueUserAPC(CallServiceToMainAPCFunc, hMainThread, (DWORD) &item);
//  PostMessage(hAPCWindow,WM_NULL,0,0); // let this get processed in its own time
//  WaitForSingleObject(item.hDoneEvent, INFINITE);
//  CloseHandle(item.hDoneEvent);
//  return item.result;
// }
//
//   return CallService(name, wParam, lParam);
//}

//向hAPCWindow post WM_NULL,使主线程调用SleepEx()进入告警状态
//int CallFunctionAsync( void (__stdcall *func)(void *), void *arg)
//{
// extern HWND hAPCWindow;
// int r = 0;
// r=QueueUserAPC((void (__stdcall *)(DWORD))func,hMainThread,(DWORD)arg);
// PostMessage(hAPCWindow,WM_NULL,0,0);
// return r;
//}

//异步操作的隐藏窗口
HWND hAPCWindow=NULL;
//异步操作窗口过程,通过向hAPCWindow
DWORD CALLBACK APCWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 if (msg==WM_NULL)
        SleepEx(0,TRUE);
 return DefWindowProc(hwnd,msg,wParam,lParam);
}

******************************************************************************************
* 以上APC过程只是miranda主程序里的,其他的模块也有类似的APC过程,调用方法与此类似
* 都是向其用QueueUserAPC投递一个APC,然后,马上再post一个WM_NULL消息,
* 其WndProc处理WM_NULL的时候往往会调用SleepEx进入告警状态,从而使投递的APC函数得以执行
****************************************************************************************** 

抱歉!评论已关闭.