天天看點

miranda-APC(異步過程調用)分析

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

****************************************************************************************** 

繼續閱讀