1. 程式人生 > >Windows核心程式設計筆記(二十) 視窗與訊息2

Windows核心程式設計筆記(二十) 視窗與訊息2

喚醒一個執行緒

執行緒的掛起與喚醒

(1)當執行緒呼叫GetMessage或WaitMessage,而訊息佇列中又沒有訊息出現時,執行緒會被掛起。

(2)當訊息被“Post”(也可以是執行緒間的“Send”)到訊息佇列時,相應的Wake標誌位會被設定,以表明該執行緒可被排程。

 查詢佇列的狀態:DWORD GetQueueStatus(UINT fuFlags)

(1)fuFlags引數,可以是下列標誌或組合,表示要查詢的哪個(些)標誌位的狀態

要查詢的標誌位

佇列中的訊息

QS_KEY

如果有一條WM_KEYUP、WM_KEYDOWN、WM_SYSKEYUP或WM_SYSKEYDOWN在訊息佇列中,則該標誌被設定。

QS_MOUSEMOVE

一條WM_MOUSEMOVE在“Input -Queue”佇列中(只要佇列中存在一個未處理的WM_MOUSEMOVE訊息,這個標誌被設定,當最後一條WM_MOUSEMOV訊息被從佇列刪除時,標誌被關閉)。

QS_MOUSEBUTTON

一條滑鼠訊息(如WM_?BUTTON*訊息等)

QS_MOUSE

相當於QS_MOUSEMOVE|QS_MOUSEBUTTON的組合

QS_INPUT

相當於QS_MOUSE|QS_KEY的組合

QS_PAINT

一條WM_PAINT訊息(該執行緒建立的視窗中,只要視窗存在無效區時,該位被設定。只有當執行緒建立的所有視窗都有效時,這個標誌才關閉)。

QS_TIMER

一條WM_TIMER訊息(當定時器報時,QS_TIMER被設定,當GetMessage返回WM_TIMER訊息後,該標誌被關閉)

QS_HOTKEY

一條WM_HOTKEY訊息

QS_POSTMESSAGE

post的訊息(不同於硬體輸入事件)。如果佇列在期望的訊息過濾範圍內沒有post的訊息,該標誌被關閉。只要Posted-Message佇列中有一條訊息,該標誌就被設定。

QS_ALLPOSTMESSAGE

Post的訊息(不同於硬體輸入事件)。如果Posted-Message佇列中完全沒有訊息,這個標誌被清除。

QS_ALLEVENTS

相當於QS_INPUT|QS_POSTMESSAGE|QS_TIMER|QS_PAINT|QS_HOTKEY

QS_QUIT

呼叫了PostQuitMessage函式,該標誌被設定,但系統並不向執行緒新增WM_QUIT訊息。(這個標誌是未公開的,只供系統內部使用,GetQueueStatus不返回這個標誌的狀態)

QS_SENDMESSAGE

由另一個執行緒傳送的訊息,即Send-Message佇列中有訊息時被設定,沒有時被清除。

(2)返回值:

  ①高位字表示佇列裡當前訊息的型別,即如果呼叫GetMessage將取出來的那條訊息。(如WM_SIZE、WM_DESTROY等)。但這與fuFlags指定的查詢狀態有關。它總是返回是所想要的標誌集的子集。例如,對下面的呼叫:BOOL fPaintMsgWaiting = HIWORD(GetQueueStatus(QS_TIMER)) & QS_PAINT; fPaintMsgWaiting的值總是FALSE,不論佇列中是否有一個WM_PAINT訊息在等待,因為GetQueueStatus的引數中沒有將QS_PAINT指定為一個標誌。

  ②低位字表示下個訊息的型別,即上次呼叫GetQueuestatus,GetMessage或PeekMessBge以來加入佇列並仍然在佇列裡的訊息的型別。

  ③QS_標誌出現在返回值裡,並不保證以後呼叫函式GetMessage或PeekMessage會返回一個訊息。GetMesssge和PeekMesssge執行某些內部過濾會導致訊息被內部處理。因此,GetQueueStatus的返回值只能被看作是否呼叫GetMessage或PeekMessage的提示。

 從執行緒的佇列中提取訊息的演算法

 

(1)呼叫GetMessage(或PeekMessage)獲取下一條訊息的演算法過程

  ①如果QS_SENDMESSAGE標誌被設定,系統向相應的視窗傳送訊息。Get/PeekMessage函式在內部進行這種處理,並且在視窗過程處理完訊息之後不返回到執行緒。相反,Get/PeekMessage函式會等待其他訊息以便處理(可以是任何的訊息,包括本執行緒或其他執行緒send或post過來的訊息)。

  ②系統檢視“posted-message”佇列,如果“posted-message”佇列有訊息,Get/PeekMessage會填充MSG結構體,然後返回。線上程的訊息迴圈中通常會呼叫DispatchMessage,將這個訊息分派到相應的視窗過程中去處理。

  ③如果QS_QUIT標誌被設定,Get/PeekMessage會返回一條WM_QUIT訊息(在訊息的wParam引數中指出了退出程式碼),並關閉QS_QUIT標誌。

  ④如果線上程的虛擬“Input-Queue”有訊息,Get/PeekMessage會返回硬體輸入訊息(如鍵盤或滑鼠訊息)。

  ⑤如果QS_PAINT標誌被設定,Get/PeekMessage會返回一條WM_PAINT訊息給相應的視窗。

  ⑥如果QS_TIMER標誌被設定,Get/PeekMessage會返回一條WM_TIMER訊息。

(2)為什麼 “posted-message”佇列在“虛擬Input-Queue”佇列之前被檢查?

  ①由於某個硬體事件可能引起其他的軟體事件,所以系統要等使用者讀取下一個硬體事件之前,先處理這些軟體事件。

  ②比如,呼叫TranslateMessage函式。這個函式會檢查是否有一個WM_KEYDOWN(或WM_SYSKEYDOW硬體訊息)從“Input-Queue”佇列中被取出。如果有,系統會檢查虛鍵資訊能否轉化為等價的字元,當可以轉換時TranslateMessage會呼叫PostMessage將一個WM_CHAR(或WM_SYSCHAR)的軟體訊息投遞到“posted-message”佇列。下次呼叫GetMessage時,系統首先會檢查“posted-message”佇列,將這個WM_CHAR(或WM_SYSCHAR)訊息取出。直到這個佇列為空,再去檢查“input-queue”佇列。所以才會生成WM_KEYDOWN→WM_CHAR→WM_KEYUP這樣的訊息序列。

(3)為什麼QS_QUIT的檢測在檢查“post-message”佇列之後。原因有兩個:

使用QS_QUIT標誌可以讓執行緒在結束訊息迴圈前,處理完所有“posted-message”佇列中的訊息,比如下面程式碼:

case WM_CLOSE:

   PostQuitMessage(0);//雖然比後面的PostMessage更早呼叫,但不會更早退出!
   PostMessage(hwnd,WM_USER,0,0);// 會先取出“posted-message”佇列中的
                                 //WM_USER,佇列空時再檢查QS_QUIT標誌。
   Break;

(4)為什麼PostQuitMessage只設置QS_QUIT而不將WM_QUIT訊息投遞到“posted-message”佇列?

  ①呼叫PostQuitMessage類似於(但不相同)呼叫PostThreadMessage(,WM_QUIT,…)。後者會將訊息新增到“posted-message”佇列的尾端,並使訊息在系統檢查“input-queue”前被處理。但前者只會設定QS_QUIT標誌位,而不會將WM_QUIT訊息放入“posted-queue”佇列。因為當低記憶體時,post一個訊息到“posted-message”可能會失敗,如果一個程式想退出,即使是低記憶體時也應該被允許,但postQuitMessage函式呼叫不會失敗(其返回值為void),因為它只改變QS_QUIT標誌。

  ②儘管PostQuitMessage不會向訊息佇列投遞WM_QUIT訊息,但當系統檢測到QS_QUIT標誌被設定時,會先填充MSG結構體,將uMsg欄位設為WM_QUIT,然後設定GetMessage的返回值為FALSE。由於系統內部自動填充了這個帶有WM_QUIT資訊的MSG結構體,讓人感覺GetMessage好象從posted-message佇列取出了一條WM_QUIT訊息。但實際上這條訊息並不是從“posted-message”佇列中取出的,而是系統偽造的一個MSG結構體。

(5)WM_PAINT優先順序低於鍵盤訊息,WM_TIMER的優先順序比WM_PAINT更低(防止用定器時在短時間內重複的WM_PAINT)。

(6)Get/PeekMessage只檢查喚醒標誌和呼叫執行緒的訊息佇列。這意味不能從其他執行緒的訊息佇列中取得訊息,包括同一程序內的其他執行緒的訊息

利用核心物件或佇列狀態標誌喚醒執行緒(與UI相關或無關的執行緒)

(1)當核心物件觸發或某種訊息到達時喚醒執行緒,既可以兼顧讓執行緒執行一個長時間的操作時,又可以響應介面操作,防止介面出現一種“假死”的現象。

(2)MsgWaitForMultipleObjects(Ex)函式

引數

描述

DWORD nCount

要等待的控制代碼的數量

PHANDLE phObjects

核心物件的控制代碼陣列

BOOL fWaitAll

該引數MsgWaitForMultipleObjects才有的。

TRUE表示所有事件(核心物件和指定訊息)觸發;FALSE表示核心物件或訊息任一被觸發時,執行緒被喚醒。

DWORD dwMilliseconds

要等待的毫秒數

DWORD dwWakeMask

何時觸發事件,即要等待哪些訊息到達,與GetQueueStatus的引數一致。帶QS_字首的一個或多個常數。

DWORD dwFlags

該引數MsgWaitForMultipleObjectsEx才有。可以是以下標誌的組合,如果不要指定任何標誌,可以設為0

①MWMO_WAITALL:等待所有核心物件觸發且指定訊息到達。如果不使用該標誌,則只有任一核心物件觸發或訊息到達,執行緒就被喚醒。

②MWMO_ALERTABLE:讓執行緒處於可警告狀態

③MWMO_INPUTAVAILABLE:只要佇列中有指定的訊息就被喚醒(不一定要等指定的、新的訊息到達才喚醒)

返回值

核心物件:WAIT_OBJECT_0~WAIT_OBJECT_nCount-1

訊息觸發:WAIT_OBJECT_nCount

超時:WAIT_TIMEOUT

……

(3)使用MsgWaitForMultipleObjects(Ex)的幾個注意點

  ①nCount的最大值為MAXIMUM_WAIT_OBJECTS-1,即63。

  ②如果fWaitAll為FALSE,當任一核心物件或指定的訊息到達時,函式會立即返回。如果fWaitAll為TRUE時,函式必須等到所有核心物件被觸發且指定的訊息到達時才返回。

  ③預設下,當呼叫這兩個函式時,它們都會檢查是否有指定的新的訊息的到來,比如現在佇列是有兩個鍵盤訊息,如果現在用帶QS_INPUT標誌的引數呼叫MsgWaitForMultipleOjbects(Ex)函式,執行緒會被喚醒。當第1個鍵盤訊息被取出並處理完後,如果繼續呼叫MsgWaitForMultipleOjbects(Ex)函式,則執行緒會掛起。此時雖然佇列中還有一個鍵盤訊息,但他不是新的。為了解決這個問題,在MsgWaitForMultipleOjbectsEx函式中增加了一個MWMO_INPUTAVAILABLE標誌,只要佇列中有指定訊息,函式就立即返回,但這個標誌只能在MsgWaitForMultipleOjbectsEx中使用。

【使用MsgWaitForMultipleOjbectsEx的訊息迴圈】

複製程式碼
BOOL fQuit = FALSE; //是否要退出訊息迴圈

while(!fQuit){
     //當核心物件觸發或UI訊息到達時喚醒執行緒
     DWORD dwResult = MsgWaitForMultipleObjectsEx(1,&hEvent,
                               INFINITE,QS_ALLEVENTS,//所有事件
                               MWMO_INPUTAVAILABLE); //只有訊息佇列還有訊息,就返回
    
     switch(dwResult)
     {
     case WAIT_OBJECT_0://核心物件被觸發

         break;       
     case WAIT_OBJECT_0+1://訊息出現在訊息佇列中
         //分派訊息
         MSG msg;
         //與GetMessage不同,PeekMessage的返回值表示有無獲取到訊息。
         //GetMessage:如果函式取得WM_QUIT之外的其他訊息,返回非零值.
         //           如果函式取得WM_QUIT訊息,返回值是零
         //PeekMessage:TRUE表示獲取到一條訊息。FALSE表示佇列中無訊息
         while (PeekMessage(&MSG,NULL,0,0,PM_REMOVE))
         {
              if (msg.message == WM_QUIT)
              {
                   fQuit = TRUE;//退出訊息迴圈
              }else{
                   //翻譯和分派訊息
                   TranslateMessage(&msg);
                   DispatchMessage(&msg);
              }
         }//佇列己空
         break;
     }
}//迴圈結束
複製程式碼

 利用視窗訊息在程序之間傳送資料

WM_SETTEXT/WM_GETTEXT訊息的傳送過程

(1)WM_SETTEXT訊息的處理過程

  ①當呼叫SendMessage時,函式會檢查是否要傳送WM_SETTEXT訊息。如果是,就將以零結尾的字串從呼叫程序的地址空間放入到一個記憶體映像檔案(可在程序間共享)。

  ②然後再發送訊息到共他程序的執行緒。

  ③當接收執行緒己準備好處理WM_SETTEXT訊息時,它在自己的地址空間中找到上述記憶體對映檔案(該檔案包含要傳送的文字),並讓lParam指向那個文字,然後訊息被分派到指定的視窗過程去進行處理。

  ④處理完訊息之後,記憶體映像檔案被刪除。

(2)WM_GETTEXT訊息的處理過程

  ①SendMessage時,系統檢測到WM_GETTEXT訊息時,實際上會發送兩個訊息。首先系統向那個視窗傳送WM_GETTEXTLENGTH訊息,以獲得文字的長度。

  ②系統利用這個長度建立一個記憶體映像檔案,用於在兩個程序間共享。然後再發送訊息來填充它。

  ③然後轉到呼叫SendMessage的程序,從記憶體映像檔案中複製文字到指定的緩衝區(由lParam引數指定)

  ④最後SendMessage函式返回。

WM_COPYDATA訊息傳送過程

(1)COPYDATASTRUCT結構體:

typedef struct tagCOPYDATASTRUCT
{
    ULONG_PTR dwData;//備用的資料項,可以放任何值
    DWORD cbData;    //要向另一個程序傳送的位元組數
    PVOID lpData;    //指向要傳送的第1個位元組(地址在傳送程序的地址空間中)
} COPYDATASTRUCT;

(2)WM_COPYDATA訊息的處理過程

  ①當SendMessage看到要傳送一個WM_COPYDATA訊息時,會建立一個記憶體映像檔案,大小是cbData位元組。

  ②從傳送程序的地址空間中將資料複製到這個記憶體映像檔案。

  ③然後向目標視窗傳送訊息

  ④接收執行緒在處理這個訊息時,lParam引數己指定接收程序地址空間中的一個COPYDATASTRUCT結構體。這個結構的lpData成員指向了接收程序地址空間中的共享記憶體映像檔案的檢視。

(3)使用WM_COPYDATA訊息,應注意的3個重要問題。

  ①只能SendMessage這個訊息,而不能PostMessage。因為在接收訊息的視窗過程處理完訊息後,系統必須釋放記憶體映像檔案。但因PostMessage是立即返回的,它不知道這個訊息何時被處理完,也就不能釋放這個記憶體塊。

  ②系統從另外的程序地址空間中複製資料要花費一些時間。所以不應讓傳送程式中執行的其他執行緒在SendMessage返回前修改這個記憶體塊。

  ③WM_COPYDATA訊息,可以實現16位到32位之間的通訊,也能實現32位到64位之間的通訊。但Win98以下沒有WM_COPYDATA訊息和COPYDATASTRUCT結構本的定義,須參考msdn自己定義。

【27_CopyData程式】演示如何使用WM_COPYDATA訊息從一個程式向另一個程式傳送一個數據塊。

 

複製程式碼
/************************************************************************
Module: CopyData.cpp
Notices: Copyright(c) 2008 Jeffrey Ritcher & Christophe Nasarre
************************************************************************/

#include "../../CommonFiles/CmnHdr.h"
#include "resource.h"
#include <malloc.h>
#include <tchar.h>

//////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnCopyData(HWND hWnd,HWND hWndFrom,PCOPYDATASTRUCT pcds){
    Edit_SetText(GetDlgItem(hWnd,pcds->dwData?IDC_DATA2:IDC_DATA1),
        (PTSTR)pcds->lpData);
    return (TRUE);
}

//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnInitDialog(HWND hWnd,HWND hWndFocus,LPARAM lParam){
    chSETDLGICONS(hWnd,IDI_COPYDATA);

    //初始化編輯框
    Edit_SetText(GetDlgItem(hWnd,IDC_DATA1),TEXT("測試資料"));
    Edit_SetText(GetDlgItem(hWnd,IDC_DATA2),TEXT("其他測試資料"));
    return (TRUE);
}

//////////////////////////////////////////////////////////////////////////
void Dlg_OnCommand(HWND hWnd,int id,HWND hWndCtrl,UINT codeNotify){
    switch(id){
    case IDCANCEL:
        EndDialog(hWnd,id);
        break;

    case IDC_COPYDATA1:
    case IDC_COPYDATA2:
        if (codeNotify !=BN_CLICKED)
           break;

        //獲取相應的文字框
        HWND hWndEdit = GetDlgItem(hWnd,
            (id == IDC_COPYDATA1)?IDC_DATA1:IDC_DATA2);

        //準備一個COPYDATASTRUCT結構體,其內容會被Copy到
        //一個記憶體映像檔案中
        COPYDATASTRUCT cds;

        //指示哪個資料域的內容要被髮送(0=ID_DATA1,1=ID_DATA2)
        cds.dwData =(DWORD)((id == IDC_COPYDATA1)?0:1);

        //獲取文字的長度(位元組)
        cds.cbData = (Edit_GetTextLength(hWndEdit)+1)*sizeof(TCHAR);

        //在棧中分配一個內容以儲存字串內容
        cds.lpData = _alloca(cds.cbData);

        //將編輯框裡的字串存到cbs.lpData中
        Edit_GetText(hWndEdit,(PTSTR)cds.lpData,cds.cbData);

        //獲取應用程式的標題
        TCHAR szCaption[100];
        GetWindowText(hWnd,szCaption,sizeof(szCaption)/sizeof(szCaption[0]));

        //列舉所有具有相同標題的頂層視窗
        HWND hWndT = NULL;
        do 
        {
            hWndT = FindWindowEx(NULL,hWndT,NULL,szCaption);
            if (hWndT != NULL)
            {
                FORWARD_WM_COPYDATA(hWndT,hWnd,&cds,SendMessage);
            }
        } while (hWndT != NULL);
        break;
    }
}

//////////////////////////////////////////////////////////////////////////
INT_PTR WINAPI Dlg_Proc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
    
    switch(uMsg){
        chHANDLE_DLGMSG(hWnd,WM_INITDIALOG,Dlg_OnInitDialog);
        chHANDLE_DLGMSG(hWnd,WM_COMMAND,Dlg_OnCommand);
        chHANDLE_DLGMSG(hWnd,WM_COPYDATA,Dlg_OnCopyData);
    }
    
    return (FALSE);
}

//////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain(HINSTANCE hInstExe,HINSTANCE hPrev,PTSTR lpCmdLIne,int nShowCmd){
    DialogBox(hInstExe,MAKEINTRESOURCE(IDD_COPYDATA),NULL,Dlg_Proc);
    return 0;
}
複製程式碼

//resource.h

複製程式碼
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含檔案。
// 供 27_CopyData.rc 使用
//
#define IDD_COPYDATA                    101
#define IDC_DATA1                       1001
#define IDC_DATA2                       1002
#define IDC_COPYDATA1                   1003
#define IDC_COPYDATA2                   1004
#define IDI_COPYDATA                    1005

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1004
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif
複製程式碼

//27_CopyData.rc

複製程式碼
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(簡體,中國) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_COPYDATA DIALOGEX 0, 0, 239, 45
STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "CopyData應用程式"
FONT 9, "宋體", 400, 0, 0x86
BEGIN
    LTEXT           "資料1:",IDC_STATIC,13,9,29,8
    LTEXT           "資料2:",IDC_STATIC,12,26,29,8
    EDITTEXT        IDC_DATA1,42,6,63,14,ES_AUTOHSCROLL
    EDITTEXT        IDC_DATA2,42,23,63,14,ES_AUTOHSCROLL
    PUSHBUTTON      "將資料1傳送到其他視窗",IDC_COPYDATA1,115,6,110,14
    PUSHBUTTON      "將資料2傳送到其他視窗",IDC_COPYDATA2,115,23,110,14
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    IDD_COPYDATA, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 232
        TOPMARGIN, 3
        BOTTOMMARGIN, 40
    END
END
#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_COPYDATA            ICON                    "CopyData.ico"
#endif    // 中文(簡體,中國) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
複製程式碼

27.6 Windows如何處理ANSI/Unicode字串

27.6.1 問題的由來

(1)對於某些訊息(如WM_SETTEXT),視窗過程可能要求傳入ANSI或Unicode字串。這取決於註冊視窗類時所使用的函式。如果呼叫RegisterClassA,系統認為視窗過程要求所有字串和字元都屬於ANSI。而用RegisterClassW註冊視窗類時,則系統認為視窗過程需要Unicode字串

(2)IsWindowsUnicode(hwnd)可用來判斷視窗過程需要使用哪種字元(串)型別

(3)如果要將一個ANSI字串傳送WM_SETTEXT給一個要求Unicode串的視窗過程,則系統在傳送訊息之前,會自動地轉換字串而無法我們干預。如果將Unicode字串傳送WM_SETTEXT給一個要求ANSI串的視窗過程,就需要我們自己來轉換。

27.6.2 視窗子類化可能出現的問題(假設父類的視窗過程要求Unicode)

(1)子類視窗過程要求的字元型別:

  呼叫SetWindowLongPtrA/W並將nIndex設為GCLP_WNDPROC,dwNewLong設為子類視窗過程的地址。如果呼叫SetWindowLongPtrA表示子類的視窗過程要求是ANSI字串(可以呼叫IsWindowUnicode來判斷子視窗過程驗證是否是ANSI型別的)。相反如果呼叫SetWindowLongPtrW,則表示子類的視窗過程要求Unicode字串。

(2)子類視窗過程中如何知道父類的視窗過程使用的是哪種型別?

相關推薦

Windows核心程式設計筆記 視窗訊息2

喚醒一個執行緒 執行緒的掛起與喚醒 (1)當執行緒呼叫GetMessage或WaitMessage,而訊息佇列中又沒有訊息出現時,執行緒會被掛起。 (2)當訊息被“Post”(也可以是執行緒間的“Send”)到訊息佇列時,相應的Wake標誌位會被設定,以表明該執行緒

Windows核心程式設計筆記 SEH結構化異常

23.2 編譯器層面對系統SEH機制的封裝 23.2.1 擴充套件的EXCEPTION_REGISTRATION級相關結構:VC_EXCEPTION_REGISTRATION (1)VC_EXCEPTION_REGISTRATION結構 struct VC_EX

《Linux核心設計實現》讀書筆記- 補丁, 開發和社群

linux最吸引我的地方之一就是它擁有一個高手雲集的社群, 還有就是如果能=為linux核心中貢獻程式碼, 一定是一件令人自豪的事情. 下面主要總結一些和貢獻程式碼相關的主要內容. 加入社群 編碼風格 提交補丁 總結 1. 加入社群 如果想為linux貢獻程式碼, 那麼加入linux

Windows核心程式設計筆記 執行緒排程 優先順序 關聯性

在搶佔式多工作業系統中,執行緒的執行是有限制的,系統會排程一個執行緒在一個時間塊內佔用CPU,在時間到了之後將執行緒的上下文(CONTEXT結構,儲存執行緒切換前的CPU個暫存器的值)儲存到執行緒核心物件中,從另一個可排程執行緒的CONTEXT中獲取屬於它的CPU各暫存器

Windows核心程式設計筆記5----執行緒排程,優先順序

1、作業系統執行緒排程過程 每個執行緒都有一個上下文CONTEXT結構體,儲存線上程的核心物件中,這個上下文中儲存了執行緒上一次執行時CPU暫存器的狀態。每隔固定時間,Windows會檢視所有當前存在的執行緒核心物件,其中只有一些是可排程的。Windows在可排程的執行緒中

《Android源代碼設計模式解析實戰》讀書筆記

apt 通過 rip idv ber list adaptee 無法 技術分享 第二十章、適配器模式 適配器模式是結構型設計模式之中的一個,它在我們的開發中使用率極高,比方ListView、GridView以及RecyclerView都須要使用A

python學習筆記:異常處理

錯誤 fetchall nbsp 如果 info blog months api root 1 def calc(a,b): 2 res=a/b 3 return res 4 def main(): 5 money=input(‘輸入多少

day9-Python學習筆記數據庫備份,

python學習 swd div pan self. 數據 筆記 tmp filename 數據庫備份, import os,datetimeclass BakDb(object): def __init__(self,ip,username,passwd,port

Java開發筆記一維陣列的用法

之前介紹的各類變數都是單獨宣告的,倘若要求定義相同型別的一組變數,則需定義許多同類型的變數,顯然耗時耗力且不宜維護。為此,程式語言引入了陣列的概念,每個陣列都由一組相同型別的資料構成,對外有統一的陣列名稱,對內通過序號區分每個資料元素。陣列型別由基本的變數型別擴充套件而來,在基本型別後面加上一對方括號,便形成

Effective_STL 學習筆記 為指標的關聯容器指定比較型別

  對於 string* 指標的 set,列印 set <string*> ssp 內容: 1  for( set<string*>::const_iterator i = ssp.begin(); i != ssp.end(); i++ ) 2     cout&

機器學習筆記:TensorFlow實戰TensorBoard視覺化

1 - 引言 前面已經介紹到TensorFlow可以實現許多非常常用的神經網路結構,有的網路結構十分複雜,裡面的引數關係更是難以管理。因此,TensorFlow提供了一個視覺化工具TensorBoard。可以有效的展示執行過程中的計算圖、各種指標隨著時間的變化趨勢以及訓練中使用到的影象等

文獻筆記

一、基本資訊 標題:線上考試系統分析與設計 時間:2012年12月 出版源:雲南大學 領域分類:軟體工程 作者:李亦鬆 研究生 二、研究背景 問題定義:高校考試 ,線上考試系統 ,考試管理系 統,資訊系統 相關工作:技術 ,演算法以及實現的功能等方面進行研究 三、研究內容 主要演算法簡介

《完結篇》論文筆記

  到這裡為止,自己看了將近三十篇關於超市管理系統相關的論文了,挑了其中的十九篇做了相應的論文筆記。最後一篇,不想寫文獻筆記了,因為要了解的瞭解的差不多了,不夠了解的,後面接著學。這篇想說總結一下這將近一個多月寫論文筆記的心得。   每一篇論文,自己不見得對於整篇論文看得有多仔細,但對於自己很缺乏的理論知識

《基於UML的中小型超市管理系統分析設計》論文筆記

一、基本資訊 標題:基於UML的中小型超市管理系統分析與設計 時間:2016 來源:湘南學院學報 關鍵詞:中小型連鎖超市; 資訊化建設; Java技術; Java EE技術; 二、研究內容 1.中小型超市管理系統的需求分析:   這個系統面向的使用者主要是超市收銀員

openCV學習筆記 —— 影象濾波 —— 線性濾波方框濾波、均值濾波、高斯濾波

影象濾波簡介 方框濾波——boxFilter()  原理 方框濾波程式  #include<opencv2/opencv.hpp> #include <vector> #include <time.h> using

opencv學習筆記——形態學濾波

開運算 處理過程:先腐蝕後膨脹。 功能:用於消除小物體,在纖細點處分離物體,並且在平滑較大物體的邊界的同時不明顯改變其面積,同時抑制比結構元小的亮細節。 使用例項: #include<opencv2/opencv.hpp> void main

php學習筆記mysqli的stmt的預處理類的使用防止sql注入問題

<?php /** * 處理資料庫的擴充套件庫 * * mysqli的預處理語句 * mysqli_stmt預處理類(推薦使用的類) * 優點:(mysqli和mysqli_result類的相比

模式識別Pattern Recognition學習筆記--BP演算法

1.引言        在無法像線性感知器一樣利用梯度下降學習引數這一問題阻礙了MLP長達25年後的一天,有人給出了一種有效的求解這些引數的方法,就是大名鼎鼎的反向傳播演算法(Back Propagation),簡稱為我們熟知的BP演算法(特別注意,BP演算法是一種演算法,

C#程式設計師整理的Unity 3D筆記:2D Toolkit之官方教程《Whack a Mole》

在上篇部落格中,簡單整理了一下Unity Native 2D功能:《C#程式設計師整理的Unity 3D筆記(十九):Unity 3D的Native 2D》. 本文開始學習2D商用比較廣泛的2D Toolkit外掛. 2D Toolkit外掛在2D中的地位,猶如UI中N

Unity3D學習筆記:Rect、Canvas、Toggle、Slider、ScrollBar

png 批量添加 事件 func 快捷鍵 resource engine 選中 創建 Rect Transform(錨點):圖片中心的四個點,界面以雪花形式顯示 當四個點在一起的時候組成錨點,當四個點分開的時候組成錨框(合則錨點,分則錨框) Anchors: ---