熱點推薦:
您现在的位置: 電腦知識網 >> 電腦常識 >> 正文

WINDOWS應用程序組織及實例

2022-06-13   來源: 電腦常識 
Windows應用程序的面向對象認識
  面向對象作為一種方法學要求將程序中的數據和操作(代碼)歸結到某些對象名下將數據看作對象的屬性要改變這些屬性必須通過操作來進行
  進行面向對象的程序設計最好使用面向對象的語言如C++SamllTalk等面向對象的語言的語言所起的作用就是給程序員們提供一些進行面向對象的程序設計時必需的約束使數據和操作的銜接有一種顯式的描述並進行一些技術性的事務管理但是如果我們能理解面向對象程序設計的原理和方法即使不使用面向對象的語言也能實現面向對象的程序設計
  Windows本身並不是一個面向對象的程序設計環境但Windows的某些部分還是明顯地受到面向對象的軟件的概念的影響從某種程度上說在進行Windows程序設計時程序員是在進行面向對象的程序設計理解Windows的面向對象的思想和應用程序設計的面向對象方法對設計結構合理的應用程序會有很大的幫助
  前面已給出了對象的定義每個對象包含有數據和代碼代碼描述了對象可執行的一系列預定義的動作而數據是對象私有的它們由相關的可執行代碼存取預定義的動作和私有數據的結合稱為封裝在C中我們使用一個函數來封裝一個對象的私有數據和動作使用switch語句來定義預定義的動作這些動作只存取為該函數本身所知道的數據
  Windows和Windows應用程序是怎樣發送消息的呢?在Windows及其應用程序中消息被表示為一個數據結構並能在對象之間傳遞發送消息等價於執行其參數表示消息數據的函數調用參數之一是一個標識該消息的預定義的消息標識符當一個對象接受到一條消息時消息標識符決定該對象執行何種動作消息傳遞是以函數調用的形式來實現的這種調用可以發生在程序的任何地方
  Windows程序員必須清楚用消息引發動作的技術不同的對象能以不同的動作響應同樣的消息這樣一個特定的消息可代表一個通用事件例如按鍵操作移動鼠標或繪制用戶區等而任何一個特定的消息可以在不同的對象中引發不同的動作例如不同的窗口對象以不同的動作處理同樣的WM_KEYDOWNWM_MOUSEMOVE或WM_PAINT消息
  一個消息可以有一個對象發送到另一個對象或由Windows發送到某個對象例如WM_KEY_DOWN之類的消息是由Windows產生的有些消息在對象的窗口函數對其處理完畢後就消失了而有些消息在處理時有產生新的消息一個對象通過向其它對象或自己發送一條或多條消息來處理一條消息這樣Windows應用程序的控制流程不象MSDOS應用程序那樣易於跟蹤程序的調試也比MSDOS應用程序困難
  除了個別消息以外對象接受消息的順序是不可預知的但對象處理每條消息所采取的動作是顯式定義在窗口函數中的對象並不顯式地定義所有可能消息的動作對於不顯示處理的消息都交由DefWindowProc進行缺省處理
  消息傳遞的途徑很簡單從一個對象傳遞到另一對象但由於DefWindowProc對有些消息提供了缺省處理因此程序員在設計程序時必須考慮在一個窗口函數中捕獲某條消息時是否還應交給DefWindowProc函數作進一步的處理DefWindowProc能處理所有的消息但對大部消息只是簡單地廢棄之不作具有實際意義的處理在窗口函數捕獲這些廢棄消息是安全的若要捕獲其它消息則必須了解DefWindowProc是怎樣處理這條消息的並在窗口函數的處理代碼中能提供類似的處理(或將該消息交由DefWindowProc作進一步的處理)
  現在我們討論窗口函數對對象的私有數據的處理問題窗口類也說明了對象的私有數據當調用CreateWindow創建一個窗口對象時Windows為創建的窗口對象分配私有數據存儲區其中存儲有窗口的實例句柄父窗口句柄窗口函數的地址和其它Windows用於管理窗口對象的數據對這些私有數據的的操作只能使用GetWindowWord/GetWindowLong等函數對於程序中說明的變量如何在窗口函數中將它們與相關的對象銜接在一起就比較復雜因為窗口函數為該類的所有對象共享該類的所有對象在接收到消息時都執行相同的代碼
  在過去Windows推薦使用的程序設計語言是C由於C語言不具備將一個對象的私有數據和操作這些私有數據的代碼銜接在一起的語言成份(面向對象的語言的事務性工作之一就是為程序完成這個工作)這個工作只能由程序員來作程序員心中必須清楚程序中所說明或分配的變量私有於哪個對象並采用合適的數據結構來表示它們以便程序在使用它們時能根據不同的對象將它們區別開來
  有幾種方法可用於區分對象的私有數據
程序員編制額外的代碼來判斷一個對象應使用哪些數據

使用窗口附加字節
使用屬性表   當使用第一種方法時程序實際是使用對象句柄作索引來檢索與該對象相關的私有數據Windows也使用這種方法使用句柄來檢索一張表這個表中存儲著該句柄所標識的對象的私有數據Windows的許多函數需要一個對象的句柄作為第一參數其原因就是為區分對象的私有數據以便使用相同的函數處理不同的對象(的數據)
  後兩種方法與第一種方法本質是一樣的(我們會將在後面的章節對其進行介紹)只是Windows提供了一些相關的函數來簡化程序的工作
  由於C沒有繼承這種語言成分因為也就不能形成對象的等級結構繼承是面向對象語言的另一個重要成分繼承使得程序中的對象形成一個分層次的對象結構低層次的對象可以將它不處理的消息發送到高層對象上進行缺省處理由於在C中不能(或說很難)建立對象的這種等級結構但為了簡化應用程序的設計又必須要求支持消息的缺省處理(否則應用程序要定義一個窗口對象可能接收到的所有消息的處理代碼)因此只能使用DefWindowProc提供消息的缺省處理這就要求對一個窗口對象所有消息的處理定義在一個函數中就帶來了定義窗口函數的返回值和參數類型時使用了一種較難為人理解的方法因為不同的消息可以帶有不同類型和個數的參數並且返回數據的類型也不相同Windows的設計者采用了一個折中的方法為消息規定一個十六位的參數和一個位的參數將返回類型指定為LRESULT這種類型的長度能容下C中所有預定義類型的數據
  由於不同類的窗口對象定義有自己的窗口函數但C語言不具備根據接受消息的對象自動決定調用該對象的窗口函數的能力(在面向對象的語言中這種能力被稱為多態性)因此向不同的窗口對象發送消息時使用函數SendMessage對窗口函數作間接調用由Windows根據該函數調用中所使用的對象標識符來調用該對象的窗口函數
  在程序設計中由於窗口函數的限制需經常進行各種各樣的數據類型轉換例如

  SendMessage(hwnd WM_USER (WPARAM) MAKELPARAM( ));

  在這個例子中為了組建一個LPARAM類型的數據使用了宏MAKEPARAM它將兩個十六位的數據組裝成一個位的數據(低位字為MAKEPARAM的第一個參數高位字為第二個參數)當需要從一個LPARAM類型的數據中分離出低位字和高位字時使用宏LOWORD和HIWORD例如處理上個例子中所發送的WM_USER消息的窗口函數的代碼可能為

  WORD wStart = LOWORD(lParam);
  WORD wStart = LOWORD(lParam);

  宏MAKELRESULT與MAKELPARAM類似它被用於裝配LRESULT類型的數據宏MAKELONG用於裝配LONG類型的數據當需要從LRESULT或LONG類型的數據中分離出高位字和低位字時使用宏HIWORD和LOWORD
  基於上面的介紹我們在設計Windows應用程序時要明確程序中存在哪些對象對象之間是如何通過消息傳遞程序控制的哪些數據是對所有對象公有的 哪些數據是私有於某一個對象的公有數據和對象的私有數據必須是存儲在靜態生存期的變量中(局部生存期的變量在窗口函數返回後就消失了不能在下次調用該函數時保存上次的值換句話說存儲對象的數據的變量的生存期不應小於對象的生存期)
  由於Windows應用程序各個模塊之間主要是通過消息傳遞控制因此Windows應用程序的邏輯結構就不同於MSDOS應用程序的邏輯結構如圖所示從圖可以看出Windows應用程序的各個模塊通過消息傳遞被聯系在一起因此如果正確地組織程序程序的模塊性和結構較MSDOS應用程序要好

DOS應用程序與Windows應用程序邏輯結構的比較示意說明

  Windows程序的組織
  將節介紹的程序按照C/C++語言的要求組織起來就得到一個完整的Windows程序一個Windows程序必須有一個名為WinMain的主函數

  // c 代碼片段
  #include <windowsh>

  LRESULT CALLBACK WndProc(HWND UINT WPARAM LPARAM);

  int PASCAL WinMain(
    HINSTANCE hInstance   // 應用程序的實例句柄
    HINSTANCE hPrevInstance // 該應用程序前一個實例的句柄
    LPSTR lpszCmdLine    // 命令行參數串
    int nCmdShow)       // 程序在初始化時如何顯示窗口
  {
     char szAppName[] = Window;
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;
    if (!hPrevInstance) {
      // 該實例是程序的第一個實例注冊窗口類
      wndclassstyle = CS_VREDRAW | CS_HREDRAW;
      wndclasslpfnWndProc = WndProc;
      wndclasscbClsExtra = ;
      wndclasscbWndExtra = ;
      wndclasshInstance = hInstance;
      wndclasshIcon = LoadIcon(hInstance IDI_APPLICATION);
      wndclasshCursor = LoadCursor(NULL IDC_ARROW);
      wndclasshbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
      wndclasslpszMenuName = NULL;
      wndclasslpszClassName = szAppName;

      if (!RegisterClass(&wndclass))
        // 如果注冊失敗
        return FALSE;
    }

    // 對每個實例創建一個窗口對象
    hwnd = CreateWindow(
      szAppName
      Sample Program
      WS_OVERLAPPEDWINDOW
      CW_USEDEFAULT CW_USEDEFAULT
      CW_USEDEFAULT CW_USEDEFAULT
      NULL
      NULL
      hInstance
      NULL);

    ShowWindow(hwnd nCmdShow);
    UpdateWindow(hwnd);

    while(GetMessage(&msg NULL )) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    return msgwParam;
  }

  WinMain函數是Windows應用程序開始執行時的入口點它的返回類型為intWinMain函數的作用十分類似於MSDOS中的C應用程序的main函數
  WinMain帶有四個參數參數hInstance和hPrevInstance是程序的實例句柄在Windows環境下可以運行同一個程序的多個拷貝每一個拷貝都是該應用程序的一個句柄每個實例使用一個實例句柄進行標識hInstance是標識當前程序的實例的句柄它的值不會為NULL如果在此之前Windows中已經運行了該程序的另一個實例則這個實例的句柄由參數hPrevInstace給出如果在運行該程序時Windows環境中不存在該程序的另一個實例則hPrevInstance為NULL
  我們曾經說過對同一個類不能向Windows注冊一次以上在這個程序中通過判別hPrevInstance的值是否為NULL來決定是否應向Windows注冊窗口類這樣的程序邏輯保證了只在該程序的第一個實例中注冊窗口類
  參數lpszCmdLine中包含有運行程序時傳遞給程序的命令行參數例如若以這樣的命令運行該程序Sampleexe Programming Windows則lpszCmdLine將指向字符串Programming Windows
  最後一個參數nCmdShow是一個int類型的整數用以說明在程序被裝如內存時Windows以何種方式顯示這個程序的窗口根據運行程序的方式不同該參數被設置為SW_SHOWNORMAL或SW_SHOWMINNOACTIVESW的含義是Show Window(顯示窗口)這兩個參數的含義在後面介紹

在程序SampleCPP中有幾個函數我們未曾介紹給出了這些函數的說明

   ShowWindow 函數 用 途 顯示或改變給定的窗口 原 型 BOOL ShowWindow(     HWND hWnd 指定一個窗口對象   int nCmdShow 指定窗口的顯示方式 );   返回值 返回該窗口更新前的窗口狀態對先前可見的窗口其值為非零對先前隱藏的窗口其值為零
  顯示方式(nCmdShow)可以是下列常量之一

類型 說明 SW_HIDE 隱藏該窗口(並是另一個窗口激活) SW_MINIMIZE 使窗口變成圖標(並激活窗口管理表的頂層窗口) SW_SHOW 激活一個窗口並根據其當前的尺寸和位置顯示該窗口 SW_SHOWMAXIMIZED 激活並以全屏方式顯示一個窗口 SW_SHOWMINIMIZED 激活並以圖標方式顯示一個窗口 SW_SHOWMINNOACTIVE 以圖標方式顯示一個窗口當前活動的窗口仍保持活動 SW_SHOWNA 以當前狀態顯示一個窗口當前活動的窗口仍保持活動 SW_SHOWNOACTIVE 以最近的大小和位置顯示一個窗口當前活動的窗口仍保持活動 SW_SHOWNORMAL 激活並顯示一個窗口若其為圖標或全屏方式顯示則恢復為它的原始大小和位置 SW_RESTORE 同SH_SHOWNORMAL
   UpdateWindow 函數 用 途 若應用程序的消息隊列中存在WM_PAINT消息(繪制用戶區消息)則該函數使Windows立即調用窗口函數向其傳遞WM_PAINT否則該函數不作為任何動作 原 型 VOID UpdateWindow(     HWND hWnd 標識被刷新的窗口的句柄 );   返回值
   GetMessage 函數 用 途 從應用程序中的消息隊列中檢索一條消息 原 型 BOOL GetMessage(     LPMSG lpMsg 指向MSG類型的變量的遠指針它包含有從應用程序消息隊列中檢索到的一條消息的數據   HWND hWnd 指定為哪個窗口檢索消息如果hWnd為NULL則檢索調用該函數的應用程序的所有的消息(不檢索屬於其它應用程序的消息)   UINT wMin     UINT wMax 以下兩個基本參數指定檢索在wMin和wMax范圍內的消息如果這兩個參數都為零該函數檢索所有的可用的消息 );   返回值 在檢索出WM_QUIT消息時返回零值在其它情況下返回非零值
   DispatchMessage 函數 用 途 將消息發送到指定的窗口對象上(窗口函數被調用) 原 型 LRESULT DispatchMessage(     LPCMSG lpMsg 指向MSG類型變量的遠指針該變量中存儲有來自應用程序消息隊列中的消息 );   返回值 若有一個WM_CHAR消息被放到應用程序的消息隊列中返回非零否則返回零該函數不改變lpMsg所指向的變量中存儲的消息數據
  Windows的主函數都是首先以初始化(注冊類創建對象等)這一步開始而且緊跟著就是消息循環運行這一步這些步驟對所有的Windows應用程序都大同小異Windows應用程序主要的不同點在窗口函數的定義上由於一個應用程序所解決的任務不同它的窗口函數對消息的處理方式也就不相同因而每個應用程序需要定義不同的窗口函數

  LRESULT CALLBACK WndProc (HWND hwnd UINT message WPARAM wParam LPARAM lParam)
  {
    switch (message)
    {
      case WM_DESTROY:
        PostQuitMessage():
               return ;
    }
    return DefWindowProc(hwnd message wParam lParam);
  }

  這個窗口函數僅處理一條WM_DESTROY消息這條消息是在用戶關閉了屏幕上的窗口時Windows發送給窗口對象的該函數對這條消息的處理只是簡單地調用Windows函數PostQuitMessage給出了函數PostQuitMessage的說明當主函數的消息循環中的GetMessage函數檢索出WM_QUIT消息時函數GetMessage返回零這樣消息循環終止程序也隨之被終止存儲消息數據的變量msg的wParam域的值是在調用函數PostQuitMessage時所提供的實參的值如果程序正常結束調用PostQuitMessage函數時使用零作為該函數的參數如果需要表示程序由於出現了異常或錯誤而必須終止時使用非零值(一般使用)作為該函數的參數在調用PostQuitMessage使用的參數值被主函數用語句

  return msgwParam;

  返回給Windows供Windows或其它應用程序使用因此我們也稱PostQuitMessage使用的參數為程序的退出碼

   PostQuitMessage 函數 用 途 通知Windows應用程序希望中止它一般用於響應WM_DESTROY消息該函數將消息WM_QUIT消息放入應用程序的消息隊列中 原 型 PostQuitMessage(     int nExitCode 指定應用程序的退出代碼它用作WM_QUIT消息的wParam參數 );   返回值
  小結
  本章首先介紹了圖形用戶界面的優點和面向對象的程序設計方法從某種意義上說Windows是面向對象的它主要建立在把窗口作為一個對象的概念上窗口之間通過消息進行消息傳遞
  Windows支持直接操作技術直接操作是對屏幕對象的操作數據和函數的封裝允許該對象自己響應它們接收到的消息在用戶界面上發生的任何事件被作為消息發送給窗口對象程序員在設計程序時只須關心一個對象要接受哪些消息和怎樣處理這些消息消息傳遞工作由Windows負責因而使用Windows操作環境可以極大地方便程序開發用戶界面的工作並使程序的結構合理模塊化程序高更重要的是支持直接操作技術的Windows支持用戶進行有創造性的界面設計


From:http://tw.wingwit.com/Article/Common/201311/5234.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.