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)
{
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函數得以執行
******************************************************************************************