<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      厚積薄發
      海納百川,有容乃大
      經常有人問關于模態對話框和系統菜單內部實現原理方面的問題, 因為系統通過API隱藏了太多細節,這2個問題確實令初學者甚至是有經驗的開發者困擾, 下面是我個人的一些經驗總結。

      先說模態對話框,外部看模態對話框其實就是Dialog彈出以后函數(或者說調用棧call stack)不直接返回, 而是要讓你做出選擇后關閉Dialog, 然后程序再繼續往下執行。在你關閉Modal Dialog之前, 你不能做其他操作。
      下面是我自己模擬模態對話框行為的代碼:
      #define MODAL_DLG_EXIT_NOTIFY    _T("modal_dialog_can_exit_now")
      #define MODAL_DLG_EXIT_VALUE     _T("this_is_the_exit_code")

      int RunModal(HWND hWnd)
      {
          int nRet(-1);
          
          HWND hWndOwner = GetWindow(hWnd, GW_OWNER);
          BOOL bDisableOwner = FALSE;
          if(hWndOwner != GetDesktopWindow())
          {
              _ASSERT(!(::GetWindowLong(hWndOwner, GWL_STYLE) & WS_CHILD));
              EnableWindow(hWndOwner, FALSE);
              bDisableOwner = TRUE;
          }
          
          MSG msg = {0};
          while(GetMessage(&msg, 0, 0, 0))
          {
              TranslateMessage (&msg);
              DispatchMessageW (&msg);
              
              if(GetProp(hWnd, MODAL_DLG_EXIT_NOTIFY) != 0)
              {
                  nRet = (int)GetProp(hWnd, MODAL_DLG_EXIT_VALUE);
                  break;
              }
          }
          
          if(bDisableOwner)
          {
              EnableWindow(hWndOwner, TRUE);
          }
          
          DestroyWindow(hWnd);
          
          return nRet;
      }

      BOOL ExitModal(HWND hWnd, int nExitCode)
      {
          BOOL bRet = SetProp(hWnd, MODAL_DLG_EXIT_NOTIFY, (HANDLE)1);
          SetProp(hWnd, MODAL_DLG_EXIT_VALUE, (HANDLE)nExitCode);

          PostMessage(hWnd, WM_NULL, 0, 0);

          return bRet;
      }
      可以看到,其實原理很簡單, 主要就是Disable對話框的Owner窗口, 然后進入消息循壞, 直到你調用ExitModal (EndDialog) 才退出消息循壞。 現在你也應該知道為什么不能用DestroyWindow,而是一定要調用EndDialog來關閉模態對話框的原因了, 因為你直接DestroyWindow就沒有機會Enable它的Owner窗口了。

      下面我們再說菜單的實現原理, 相信菜單的原理即使對很多有經驗的開發者也不一定清楚。
      我們知道菜單其實也是一個普通的窗口,首先菜單窗口其實和模態對話框一樣, 在我們關閉菜單,對菜單做出選擇之前函數是不會返回的。 菜單窗口的特殊之處在于,菜單彈出的時候我們可以看到它下面的窗口還是保持激活狀態, 也就是說當前的得到焦點的窗口其實是菜單的Owner窗口, 但是菜單窗口同時又能響應鍵盤消息(我們可以通過上下鍵或是Enter和Esc做出選擇)。從窗口機制的原理上說兩者是矛盾的,一個沒有獲得焦點的窗口怎么能夠響應鍵盤消息呢? 下面是我自己對彈出菜單行為的模擬:
      #define MENU_EXIT_NOTIFY        _T("menu_loop_can_exit_now")
      #define MENU_EXIT_COMMAND_ID    _T("this_is_the_menu_command_id")

      int RunMenu(HWND hWnd)
      {
          int nRet(-1);
          
          BOOL bMenuDestroyed(FALSE);
          BOOL bMsgQuit(FALSE);
          HWND hWndOwner = GetWindow(hWnd, GW_OWNER);
          _ASSERT(GetForegroundWindow() == hWndOwner);
          
          while(TRUE)
          {
              if(GetProp(hWnd, MENU_EXIT_NOTIFY) != 0)
              {
                  nRet = (int)GetProp(hWnd, MENU_EXIT_COMMAND_ID);
                  break;
              }

              if(GetForegroundWindow() != hWndOwner)
              {
                  break;
              }

              MSG msg = {0};
              if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
              {
                  if(msg.message == WM_KEYDOWN
                      || msg.message == WM_SYSKEYDOWN
                      || msg.message == WM_KEYUP
                      || msg.message == WM_SYSKEYUP
                      || msg.message == WM_CHAR
                      || msg.message == WM_IME_CHAR)
                  {
                      //transfer the message to menu window
                      msg.hwnd = hWnd;
                  }
                  else if(msg.message == WM_LBUTTONDOWN
                      || msg.message == WM_RBUTTONDOWN
                      || msg.message == WM_NCLBUTTONDOWN
                      || msg.message == WM_NCRBUTTONDOWN)
                  {
                      //click on other window
                      if(msg.hwnd != hWnd)
                      {
                          DestroyWindow(hWnd);
                          bMenuDestroyed = TRUE;
                      }
                  }
                  else if(msg.message == WM_QUIT)
                  {
                      bMsgQuit = TRUE;
                  }

                  TranslateMessage (&msg);
                  DispatchMessageW (&msg);
              }
              else
              {
                  MsgWaitForMultipleObjects (0, 0, 0, 10, QS_ALLINPUT);
              }

              if(bMenuDestroyed) break;

              if(bMsgQuit)
              {
                  PostQuitMessage(msg.wParam);
                  break;
              }
          }
          
          if(!bMenuDestroyed) DestroyWindow(hWnd);
          
          return nRet;
      }

      BOOL ExitMenu(HWND hWnd, int nCommandID = -1)
      {
          BOOL bRet = SetProp(hWnd, MENU_EXIT_NOTIFY, (HANDLE)1);
          SetProp(hWnd, MENU_EXIT_COMMAND_ID, (HANDLE)nCommandID);

          return bRet;
      }
      從代碼可以看到,如果我們可以自己控制整個Windows消息循環,那么中間我們就有很多事可以做了,包括攔截和轉發任何消息, 比如我們可以把原來系統發給A窗口的消息直接轉發給B窗口。

      簡單總結下,Windows的API封裝了太多細節, 盡管大部分時候我們只要知道如何使用它們,而不用關心它們的內部如何實現。 但是當你寫一些相對底層的東西,比如開發自己的DirectUI界面庫時, 還是需要真正理解某些API的內部實現原理,才能繼續深入下去。

      注:因為沒有Windows源碼,上面的代碼只是個人的猜測和模擬,如有不正確的地方歡迎指正。

      完整測試源碼:ModalDialog&Menu Test
      posted on 2013-04-07 22:24  Richard Wei  閱讀(2014)  評論(1)    收藏  舉報

      主站蜘蛛池模板: 国产无遮挡裸体免费久久| 国产成人久久综合一区| 国产精品美女自慰喷水| 亚洲V天堂V手机在线| 人妻换着玩又刺激又爽| 国产精品国产三级国产a| 中文字幕国产原创国产| 久久一日本综合色鬼综合色| 午夜自产精品一区二区三区| 精品无码久久久久久尤物| 国产精品国产三级国产午| 香蕉久久夜色精品国产成人| bt天堂新版中文在线| 亚洲av成人一区在线| 精品人妻一区二区三区蜜臀 | 国产 一区二区三区视频| 国产特级毛片aaaaaa毛片| 亚洲熟妇在线视频观看| 91亚洲国产成人久久精品| 国产av亚洲精品ai换脸电影| 国产粉嫩美女一区二区三| 国产一区二区在线有码| 精品久久8x国产免费观看| 狠狠躁夜夜躁人人爽天天5| 国产精品制服丝袜第一页| 久久综合久色欧美综合狠狠| 旅游| 一区二区三区四区五区自拍| 人妻丝袜无码专区视频网站| 扒开粉嫩的小缝隙喷白浆视频| 99国产欧美另类久久久精品| 视频一区二区三区四区不卡| 成人精品一区日本无码网| 亚洲一区二区约美女探花| 国产一级黄色片在线观看| 国产在线观看黄| 人妻系列无码专区免费| 亚洲最大成人美女色av| 国产97人人超碰CAO蜜芽PROM| 日韩有码国产精品一区| 国产在线国偷精品产拍|