第14章 Windows API 的跨平臺開發

14.1 Windows API 跨平臺開發概述
  Windows API是Miscrosoft公司在推出其Windows系列操作系統時同時提供給廣大用戶的一個應用程序接口,在這套應用程序接口中定義了數以千計的可用於面向Windows程序設計的函數。用戶使用這些函數可以輕易地完成諸如窗口生成並顯示,消息循環和處理等各種工作。由於其良好的可重用性、Windows操作系統的緊密集成以及強大的功能,因此 Windows API構成了面向 Windows應用開發的一個基礎。
  雖然Windows API是用C語言開發的,它卻能同時用於很多的開發平臺。但是由於不同開發平臺在實現方式上的差異,使得 Windows API在其他開發工具中的應用和在VisualC++中的應用既有相似之處,又有不同之處。
  現在的主流開發工具除了Microsoft的Visual Studio之外,還有Inprise公司開發的Delphi和C++Builder,這兩款開發工具的最大特色就是引入了強大的可視化組件庫,這些組件庫在很大程度上緩解了程序員在界面設計上的負擔,因此自從它們推出之後,得到了廣大程序員的熱烈歡迎,並迅速流行開來。現在,這兩款開發工具的最新版本分別爲 Delphi 5和 C++Builder 4。
  本章介紹Windows API在Delphi和C++Builder中的應用,主要是希望通過這部分內容使讀者對 Windows API的認識能得到進一步的提高,同時也希望通過它能使讀者對Inprise公司的 Delphi和 C++Builder有一個新的認識。
14.2 Windows API 與 Delphi
  Delphi是一個用以開發Windows應用軟件和數據庫類軟件的一個開發工具。Windows API 作爲Windows操作系統不可分割的一部分,在Delphi中得到了全面的支持。
14.2.1 Delphi概述
  Delphi融合了當今世界先進的軟件技術,採用可視化組件庫,支持多種數據庫,如Orcale、SQL Server等。作爲數據庫的前臺開發工具,它提供了友好的開發界面,使用戶能用較少的工作量就能開發出美觀實用的 Windows應用程序。 Delphi的前幾個版本均由Inprise公司開發成功,並獲得了廣大用戶的信賴和好評。
  目前由Inprise公司推出的Delphi 5在原有版本的基礎上,增加了動態數組、方法重載、默認參數等等。項目管理器允許用戶將協同工作的項目放到同一個項目組中,同時允許用戶定製自己的桌面。在數據庫引擎方面,不僅強化了BDE的性能,而且引進了Microsoft的ADO。在網絡應用方面提供了對XML的支持。Delphi支持可擴展的數據庫技術,執行最優化的源代碼。用戶開發的 Delphi應用程序可直接運行在 Windows 95和Windows NT4.0上,並與 C++的 DLL和 VBX兼容。
  儘管Delphi具有上述所介紹的強大功能,但是它也離不開Windows API這個基礎。在Delphi中使用Windows API進行程序設計,與Visual C+十的不同之處僅在於C+十與Object Pascal這二者之間的語法差別而已。
14.2.2 Delphi開發 Windows API應用程序的步驟
  下面以 Delphi 5爲例,介紹在Delphi中如何實現基於 Window API的程序設計。
  首先,單擊 File菜單下的 New命令,這時將彈出如圖所示的對話框。

在圖中,選擇 Console Wizard圖標,然後單擊OK按鈕,這時,Delphi的主界面如圖所示。

  從圖 14-2可以看出, Delphi的對象監視器爲空,也就是說,當前應用程序沒有使用到表單和其他的可視化組件。事實上,這時Delphi爲用戶只生成了一個項目文件,而沒有生成相應的單元文件,這和基於Delphi可視組件庫的應用程序是有一定差別的。
  在圖中的源文件編輯器中編輯自己的基於Windows API的程序源碼,完成後,編譯運行即可,這一步和Delphi中基於可視組件庫的應用程序是一致的。
14.2.3 Windows AP在 Delphi中的應用實例
下面是一個在 Delphi中採用 Windows API進行程序設計的一個實例。
例 基於 Windows API的 Delphi應用程序該程序是一個用 Windows API編寫的簡單的應用程序,主要用它來說明如何在 Delphi 中調用 Windows API函數,如何設計基於 Windows API的應用程序。
  由於使用的是Windows API和消息循環來構造應用程序,因此在程序中的uses部分包含了Windows 和Messages兩個單元。uses子句和Visual C++中的include很類似,其功能也是爲了應用程序能調用在某個文件中定義的函數和常量。在 Delphi中,所有的Windows API函數的原型和數據結構及常量的定義均保存在Windows和Messages這兩類文件中。在 Delphi的 Lib目錄下可以打開這兩個文件,其文件名分別爲 Windows.dcu和Messages.dcu,用戶可以從這兩個文件中獲得準確的函數原型、數據結構和常量的定義。
  程序中定義了一個全局變量wClass來代表最終的窗口。這個變量是TWndClass類型的,TWndClass實際上是一個結構體,它是 Visual C++中的 WndClass在 Delphi中的實現。通過對這個結構體變量的各個域進行不同的設置,可以獲得不同的窗口風格和界面特徵。在程序中可通過如下語句實現對wClass變量的初始化:
with wClass do //初始化窗口類
begin
  Style:=CS_PARENTDC;
  hlcon:=LoadIcon(hInst,'MAINICON');
  lpfnWndProc:=@WindowProc;
  hlnstance:=hlnst;
  hbrBackground:=COLOR_BTNFACE+1;
  lpszClassName:='Sample Class';
  hCursor:=LoadCursor(O,IDC_ARROW);
end;
  上述程序不論是在功能L還是在實現方式上和 Visual C+十中對 wClass進行的初始化都是類似的。例如,指定hCursor域的值時,在上述程序中是通過調用API函數LoadCursor來實現的,其中指定的光標標識符IDC ARROW是在Windows系統中預定義的箭頭光標。不過,由於 Delphi用的編程語言是 Object Pascal,因此在指定 IpfnWndProc時,不僅僅需要指定窗口函數的名稱,而且必須在其前面添加一個“@”號,這個號表示傳遞給lpfnWndProc是窗口函數WindowProc的人口地址。
  在完成對變量wClass的初始化之後,接下來應該對窗口類進行註冊,這個功能是通過調用函數RegisterClass來實現的。
  在完成對窗口類的註冊後,就可以調用函數CreateWindow來創建窗口,程序中對CreateWindow函數的調用如下:
Handle:=CreateWindow(
  'SampleClass', //窗口類名
  '加密器一Windows API在Delphi中的應用', //窗口標題
  WS_OVERLAPPEDWINDOW or WS_VISIBLE, //窗口風格
  10, //左邊界座標
  10, //上邊界座標
  400, //寬度
  300, //高度
  0, //父窗口句柄
  0, //菜單句柄
  hlnst, //應用程序實例
  nil //創建窗口的附加參數
);
  其中,參數 Sample Class用於指定生成窗口的窗口類的名稱,也就是通過 RegisterClass函數註冊的用戶自定義的窗口類。如果CreateWindow函數執行成功,則返回新建的窗口句柄並將這個句柄賦值給全局變量Handle。這裏的窗口句桶和Visual C++中的窗口句柄的含義是完全一致的。
  程序中除了創建主窗口外,還通過調用CreateWindow函數創建了兩個按鈕、一個編輯框和一個靜態文本框。窗口類型都是由Windows系統預定義的,所以不用再註冊就可以直接使用。這些窗口又被稱爲子窗口,因爲它們是隸屬於一個父窗口的,在這裏就是隸屬於由句柄Handle標識的窗口。正是這些子窗口構成了和用戶進行交互的界面。
  在本程序中,還通過調用API函數CreateFont創建了一個字體,程序中對CreateFont函數的調用如下:
hFont:=CreateFont(
  12, //高度
  0, //寬度
  0, //旋轉角度
  0, //方向
  0, //線條寬度
  0, //斜體
  0, //下劃線
  0, //加粗
  DEFAULT_CHARSET, //字符集
  OUT_DEFAULT_PRECIS, //精度
  CLIP_DEFAULT_PRECIS, //剪裁方式
  DEFAULT_QUALITY, //渲染質量
  DEFAULT_PITCH or FF_DONTCARE, //字體族
  'MS Sans Serif' //字體名
);
  CreateFont函數的參數比較複雜,每個參數描述字體的某一個屬性,CreateFont的功能就是用指定的字體屬性創建一種字體。創建的字體由CreateFont返回的字體句柄來標識,例如,在向按鈕 Encrypt發送 WM-SETFONT消息的同時,把 hFont作爲 SendMessnge的wParam參數,實現將按鈕Encrypt中的標題字體設置爲上述程序中新建的字體。
  SendMessnge用於向各個窗口發送消息,例如,上面的設置字體的消息就是通過如下語句發送的:
SendMessage(hEncrypt,WM_SETFONT,hFont,0); //發送消息
SendMessage(hDecrypt,WM_SETFONT,hFont,O);
SendMessage(hEdit,WM_SETFONT,hFont,O);
SendMessage(hPW,WM_SETFONT,hFont,0);
SendMessage(hLabel,WM_SETFONT,hFont,O);
  上述語句將所有子窗口標題的字體都設置爲由CreateFont新建的字體。
  爲了實現消息處理,程序中首先調用函數GetWindowLong,取得了Encrypt按鈕的默認消息處理函數的入口地址,然後把這個地址轉換成指針類型,並把最終的值傳遞給dEncrypt變量。再調用函數 SCtwilldOWLOflg給 EllCrypt按鈕指定由用戶定製的消息處理函數的人口地址。
  在調用函數SetFocus把默認的焦點置於Encrypt按鈕之上後,主程序就進入了消息循環。在Delphi中的消息循環如下:
while(GetMessage(Msg,Handle,0,0))do //消息循環
begin
  TranslateMessage(Msg); //翻譯消息
  DispatchMessage(Msg); //發送消息
end;
  其中,函數GetMessage用於從操作系統的消息隊列中獲得消息,然後通過函數TranslateMessage對消息進行翻譯,最後通過函數DispatchMessage把所有的消息分發出去。只有當消息循環接收到一條 WM-NESTROY消息時纔會結束。
  主窗口的消息處理函數如:
function WindowProc(hWnd,Msg,wParam,lParam:Longint);Longint;stdcali;
begin
  Result:=DefWindowProe(hWnd,Msg,wParam,lParam); //消息默認處理
  case Msg of
  WM_SIZE: //改變窗口大小
    Resize;
  WM_COMMAND: //處理命令
    if lParam=hEncrypt then
      Encrypt //加密
    else
      if IParam=hDecrypt then
        Decrypt; //解密
  WM_DESTROY: //結束應用程序
    ShutDown;
  end;
end;
  其中,消息處理函數的參數列表和Visual C+十是一致的,但是在參數的數據類型上有一些細微的差別,在 Delphi中四個參數的類型都是 Longint。
  和Visual C+十中的消息處理類似,在Delphi中也是通過一個大的分支結構來實現對不同的消息進行不同的處理。例如,當窗口接收到 WMSIZE消息時,就調用 Resize函數對窗口進行重畫。Resize是自定義的一個函數,其完整的定義如下:
procedure Resize;
var
  RCT:TRect;
begin
  GetWindowRect(Handle,RCT); //獲得窗口矩形區域
  MoveWindow(hPW,230,5,RCT.Right-RCT.Left-245,24,True);//移動窗口
  MoveWindow(hEdit,5,34,RCT.Right-RCT.Left-20,RCT.Bottom-RCT.Top-66,True);
end;
  其中,定義了一個用於描述當前窗口所佔據的區域的TRect變量RCT,函數GetWindowRect可以獲得窗日當前所在的矩形區域,MoveWindow可以使窗口移動,事實上,調用MoveWindow函數的目的是爲了當窗日大小發生改變時還能保持窗口中按鈕、編輯框等界面元素的相對位置。
  用戶單擊窗口中的某一個接或將觸發一條WM-COMMAND消息,同時在IParam參數中將記錄下觸發消息的組件句柄。例如,當用戶單擊Encrypt按鈕時,消息處理函數就通過對IParam參數的判斷,發現消息來自於Encrypt按鈕,於是就調用函數Encrypt加以處理。Encrypt函數是自定義的一個函數,其完整定義如下:
procedure Encrypt;
var
  x,i,
  sText,sPW:Integer;
  Text,PW:PChar;
begin
  sText:=GetWindowTextLength(hEdit+1; //Edit編輯框中的文本長度加1
  sPW:=GetWindowTextLength(hPW)+1; //密碼框中的文本長度加1
  GetMem(Text,sText; //分配內存
  GetMem(PW,sPW); //釋放內存
  GetWindowText(hEdit,Text,sText); //獲取Edit編輯框中的文本
  GetWindowText(hPW,PW,sPW); //獲取密碼框中的文本
  x:=0;
  for i:=0 to sText-2 do
  begin
    Text[i]:=Chr(Ord(Text[i])+Ord(Pw[x])); //加密
    Inc(x);
    if x=(sPW-1)then x:=0;
  end;
  SetWindowText(hEdit,Text); //重新設置Edit編輯框中的文本
  FreeMem(Text);
  FreeMem(PW);
end;
在Encrypt中,實現對文本框中輸入的文本進行加密。其核心語句是:
Text[i]:=Chr(Ord(Text[i])+Ord(PW[x])); //加密
  其中,函數Ord用於取得某一個字符的ASCII碼,然後再加上來自密碼框中某一個字符的ASCII碼,將相加的和賦給原來的字符單元,這樣顯示出來的文本和最初的文本就不一樣了。函數Ord的原型定義如下所示:
function Ord(X):Longint;
關於Ord函數的使用還可以參考下面的例子:
USCS Dialogs;
type
  Colors=(RED,BLUE,GREEN);
Var
  S:string;
begin
  S:='BLUE has all ordinal value of'+IntToStr(Ord(BLUE))+#13#10;
  S:=S+'The ASCII code for "c" is'+IntToStr(Ord('c'))+'decimal';
  MessageDlg(S,mtInformation,[mbOk],0);
end;
  函數GetMem和FreeMem分別用於爲字符串分配內存和釋放內存;函數GetWindow Text和setWindowText分別用於獲取和設置編輯框中的文本。
  與加密類似,如果用戶單擊了Decrypt按鈕,則調用函數Decrypt來執行相應的解密操作。通過減去密碼框中相應字符的ASCII碼就可以恢復文本框中的文本。實現解密功能的核心語句如下:
Text[i]:=Chr(Ord(Text[i])-ord(PW[x])); //解密
在關閉應用程序時,程序中調用shutDown來完成所有的善後事宜。函數shutDown的完整定義如下:
procedure ShutDown;
begin
  DeleteObject(hFont); //刪除字體句柄
  UnRegisterClass('Sample Class',hlnst); //註銷窗口類
  ExitProcess(hInst); //結束應用程序
end;
  由於句柄是一種系統資源,因此在關閉應用程序時,應該刪除hFont句柄並註銷對Sample Class的註冊。   ExitProcess是一個 API函數,這個函數用於結束當前進程。
  編譯並運行上例,用戶將得到如圖所示的應用程序主窗口。

  單擊 Encrypt按鈕,則編輯框中的文本將被加密,如圖所示。
  當然,用戶單擊按鈕 Decrypt,相應的文本將會被解密,重新得到如圖習中所示的文本。

14.3 Windows API與C++ Builder
  可以說,基於圖形界面的Windows系列操作系統的推出是計算機真正進入各個應用領域的一個具有里程碑意義的事件。
  在Windows取得了巨大的成功的同時,廣大程序員也必須面對一個問題,那就是開發基於Windows的應用程序。Windows操作系統的一個核心思想就是圖形界面的思想,通過爲用戶提供一個基於圖形的,而不是基於字符串命令的用戶界面,同時爲了減輕用戶在操作上的困難,提供的圖形界面中也儘可能地採用一致的圖形界面元素,例如對話框、按鈕、菜單等均採用統一的風格,提供一致的外觀。
  深入瞭解Windows API的好處在於可以有助於理解C++Builder和操作系統是如何工作的。如果理解了創建Windows程序的核心技術,那麼就可以從這些信息中獲得C++Builder的工作原理,以及它能做什麼,它不能做什麼。
  許多人使用C++Builder,是因爲C++Builder可以在相對輕鬆的情況下較快捷地建立複雜的應用程序。這是C++Builder的一大優點。但是如果要真正創建一些強有力的程序或者組件,那麼僅僅瞭解高級工具顯然是不夠的,還應該瞭解C++Builder是如何實現和Windows進行交互的,在Windows底層是如何實現的?這裏介紹的關於C++Builder中如何進行 Windows API程序設計的內容希望能爲讀者朋友理解上述問題提供一點幫助。
  但必須指出的是, C++Builder能通過 Windows API進行程序設計,但是這並不是C++Builder的強處。基於VCL並結合Windows API等多種手段才能充分發揮C++Builder的編程能力,並編寫出高質量的應用程序來。
12.3.1 C++Builder開發 Windows API應用程序的步驟
  C++Builder是Inprise開發的Delphi的姊妹產品,因此C++Builder無論是在界面特徵還是在功能實現上都和 Delphi非常類似。在開發基於 Windows API的應用程序的步驟上,兩者也是非常相似的。
  當啓動C++Builder後,單擊File菜單下的New菜單項,這時將彈出如圖的對話框。

在這個對話框中,選擇 Console Wizard,然後單擊 OK按鈕,這時將得到如圖所示的C++Builder運行界面。

  在圖中, C++Builder自動爲用戶生成了一個Windows API應用程序的框架,如自動將頭文件windows.h包含進項目中,自動生成的WinMain函數等。用戶可以直接在此基礎上開發自己的 Windows API應用程序。
12.3.2 Windows API在 C++Builder中的應用實例
  由於 C++Builder的編程語言是 C++,因此,通過C++Builder開發Windows API應用程序和通過Visual C+十開發Windows API應用程序在語法上、結構上都是非常類似的。事實上,在C++Builder中開發的基於Windows API應用程序在Visual C+十中也能編譯運行,在 Visual C+十中開發的 Windows API應用程序在 C++Builder中也能編譯運行。
  下面是使用C++Builder開發的一個簡單的Windows API應用程序,該程序將顯示一個窗口,並在該窗口的客戶區中用藍色、綠色和紅色三種畫筆畫線。
例 Windows API在 C++Builder中的應用
  程序中首先對窗口消息處理函數WndProc、窗口初始化函數InitWindows的原型進行聲明。然後定義一個全局變量hWndMain記錄主窗口的句柄。
例中的WinMain函數、InitWindows函數和前面介紹過的幾乎相同,這裏就不贅述了。
  下面重點分析一下程序是如何實現繪製直線功能的。
  在消息處理函數WndProc中,首先定義了紅、綠、藍三支邏輯畫筆,然後在對窗口進行重給時,通過 CreatePenIndirect函數創建邏輯畫筆實例,通過 SelectObject函數選擇不同的畫筆到設備描述表中就可以實現繪製不同顏色的直線。
  繪製直線之前,程序先調用函數MoveToEx將畫筆移動到直線的起點,然後通過調用函數LineTo繪製一條直線。
  因爲畫筆句柄耗費系統資源,所以在結束繪畫後,必須調用 DeleteObject函數銷燬畫筆實例。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章