本文章最後由 panda11138 於 2013-5-29 23:18 編輯
API是本來是C++用的一種函數,他們包括在一個附加名為DLL的動態連線庫檔案中。用標準的定義來講,API就是Windows的32位套用程式寫程式接口,是一系列很複雜的函數,消息和結構,它使寫程式人員可以用不同類型的寫程式語系編製出的執行在Windows95和Windows NT動作系統上的套用程式。可以說,若果你曾經學過VC,那麼API對你來說不是什麼問題。但是若果你沒有學過VC,或是你對Windows95的結構體系不熟悉,那麼可以說,學習API將是一件很辛苦的事情。
若果你開啟WINDOWS的SYSTEM資料夾,你可以發現其中有很多附加名為DLL的檔案。一個DLL中包括的API函數並不只是一個,數十個,甚至是數百個。我們很難掌握。而我們都沒有掌握的必要,只要重點掌握Windos系統本身自帶的API函數就可以了。但在其中還應當拋開掉同VB本身自有的函數重複的函數。如,VB的etAttr指令可以獲得檔案屬性,SetAttr可以設定檔案屬性。對API來講也有對應的函數GetFileAttributes和SetFileAttributes,效能都差不多。
若果你不依靠API會怎麼樣?我可以跟你說,絕大多是進階寫程式書本,首先提的問題一般大都是從API開始。如果不學API,你大概將停留在初級水平,無法往上攀登。唯一的途徑也許就是向別人求救︰「我快死了,快來救救我呀,這個怎麼辦,那個怎麼辦?」
很多API函數都是很長很長的。想看什麼樣子嗎?如下就是作為例子的API DdeClientTransaction函數︰
Declare Function DdeClientTransaction Lib "user32" (pData As Byte, ByVal cbData As Long, ByVal hConv As Long, ByVal hszItem As Long, ByVal wFmt As Long, ByVal wType As Long, ByVal dwTimeout As Long, pdwResult As Long) As Long
哇!這麼長?若果你從來沒有接觸過API,我想你肯定被嚇住了。你也許考慮,該不該繼續學下去。不過不要擔心,幸運的是Microsoft的設計家們為我們提供了有用的工具,這便是API文字檢視器。
通過API文字檢視器,我們可以方便地尋找程式所需要的函數聲明、結構類型和常量,然後將它複製到剪貼簿,最後再貼上到VB程式的代碼段中。在大多數情況下,只要我們確定了程式所需要的函數、結構和常量這三個方面後,就可以通過對API文字遊覽器的以上動作將他們加入到程式段中,從而程式中可以使用這些函數了。這些是學習API最基本的常識問題,它遠遠佔不到API的龐大的體系內容。現在我們要乎的就是甚麼時候使用什麼函數,什麼時候使用什麼結構類型,什麼時候使用什麼常量。
API函數聲明
在VB中,如何聲明函數呢?我想,若果你正在看此文,那麼你絕對能夠回答得出這個問題。以下便是你應該很熟悉的函數聲明︰
Function SetFocus (ByVal hwnd As Long) As Long
即是這行代碼定義了名為SetFocus的函數,此函數具有一個Long型資料類型的參數,並按值傳遞(ByVal),函數執行後將返回一個Long型資料。
API函數的聲明也很類似,如,API中的SetFocus 函數是這樣寫的︰
Declare Function SetFocus Lib "user32" Alias "SetFocus" (ByVal hwnd As Long) As Long
有點複雜,但我可以告訴你,除了這些多出來的部分,其他部分還是和你以前學到的東西是一樣的。函數在程式中的呼叫也是一樣。如:
Dim dl As Long
dl&=SetFoucs(Form1.Hwnd)
但是有一點是清楚的。它不像你自己寫的程式那樣能夠看到裡面的執行機理,也不像VB自帶的函數那樣,能夠從VB的聯機幫助中查到其用法。唯一的方法就是去學、查VB以外的資料。
Declare 語句用於在模組層級中聲明對動態連結庫 (DLL) 中外部過程的引用。對此,你只要記住任何API函數聲明都必須寫這個語句就可以了。
Iib 指明包括所聲明過程或函數的動態連結庫或代碼資源。也就是說,它說明的是,函數或過程從何而來的問題。
如在上例中,SetFocus Lib "user32"說明 函數 SetFocus 來自 user32.dll檔案。主要的dll動態連線庫檔案有︰
user32.dll Windows管理。生成和管理套用程式的使用者接口。
GDI32.dll 圖形裝置接口。產生Windows裝置的圖形輸出
Kernel32.dll 系統服務。訪問動作系統的電腦資源。
注意,當DLL檔案不在Windows或System資料夾中的時候,必須在函數中說明其出處(路徑)。如,SetFocus Lib "c:\Mydll\user32"
函數聲明中的Alias 是可選的。表示將被呼叫的過程在動態連結庫 (DLL) 中還有另外的名稱(別名)。如,Alias "SetFocus" ,說明SetFocus函數在User32.dll中的另外一個名稱是,SetFocus。怎麼兩個名都一樣呢?當然,也可以是不同的。在很多情況下,Alias說明的函數名,即別名最後一個字元經常是字元A,如SetWindowsText函數的另一個名稱是SetWindowsTextA,表示為Alias "SetWindowsTextA"。這個A只不過是設計家們的習慣的命名約定,表示函數屬於ANSI版本。
那麼,別名究竟有什麼用途呢?從理論上講,別名提供了用另一個名子呼叫API的函數方法。若果你指明了別名,那麼儘管我們按Declare語句後面的函數來呼叫該函數,但在函數的實際呼叫上是以別名作為首要選取的。如以下兩個函數(Function,ABCD)聲明都是有效的,他們呼叫的是同一個 SetFocus函數︰
Declare Function SetFocus Lib "user32" "SetFocus" (ByVal hwnd As Long) As Long
Declare ABCD SetFocus Lib "user32" Alias "SetFocus" (ByVal hwnd As Long) As Long
需要注意的是,選用Alias的時候,應注意別名的大小寫;若果不選用Alias 時的時候,函數名必須注意大小寫,而且不能改動。當然,在很多情況下,由於函數聲明是直接從API文字遊覽器中複制過來的,所以這種錯誤的發生機會是很少的,但您有必要知道這一點。最後提醒你一句,API聲明(內含結構、常量)必須放在窗體或模組的"通用(General Declarations)段。
資料類型與"類型安全"
API函數中使用的資料類型基本上和VB中的一樣。但作為WIN32的API函數中,不存在Integer資料類型。另外一點是在API函數中看不到Boolean資料類型。
Variant資料類型在API函數中是以Any的形式出現,如Data As Any。儘管其含義是容許任意參數類型作為一個該API函數的參數傳遞,但這樣做存在一定的缺點。其原因是,這將會使得對目的參數的所有類型檢查都會被關閉。這自然會給各種類型的參數呼叫帶來了產生錯誤的機會。
為了強制執行嚴格的類型檢查,並避免上面提到的問題,一個辦法是在函數里使用上面提到到Alias技術。如對API函數 GetDIBits 可進行另外一種聲明方法。如下︰
GetDIBits函數的原型︰
Public Declare Function GetDIBits Lib "gdi32" Alias "GetDIBits" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByValnNumScans As Long, lpBits As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long
GetDIBits函數的改型︰
Public Declare Function GetDIBitsLong Lib "gdi32" Alias "GetDIBits" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByValnNumScans As Long, lpBits As Long, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long
通過本課程前面所學到的知識,我們已經可以得知原型 GetDIBits函數也好,改型 GetDIBitsLong函數也好,實際將呼叫的都是Alias所特殊的 GetDIBits原函數。但你應當看到,兩者的區別在於,我們在改型的函數中強制指定lpBits參數為Long形。這樣就會使得函數呼叫中發生的錯誤機率減少到了最小。這種方法叫做"安全類型"聲明。
API函數中經常看到的資料類型有︰Long,String,Byte,Any....
常數
對於API常量來講,沒有什麼太特別的學問。請看VB中的以下代碼︰
Msg = MsgBox("您好", vbOKCancel)
我們知道, vbOKCancel這個常量的值等於1。對上面的代碼我們完全可以這樣寫,而不會影響代碼的功能︰
Msg = MsgBox("您好", 1)
但你大概不太願意選取後一種,因為這會使得看懂代碼費勁起來。這種方法也被API採取了。只是API常量必須在事情之前做好起始化聲明VB本身是看不懂的。其內容仍然來自與API
文字遊覽器。具體形式如下等等︰
Public Const ABM_ACTIVATE = &H6
Public Const RIGHT_CTRL_PRESSED = &H4
Public Const RPC_E_SERVER_DIED = &H80010007
Private Const RPC_S_CALL_FAILED_DNE = 1727&
在常量的起始化中,有些程式使用Global,如Global Const ABM_ACTIVATE = &H6,但我認為Public完全可以代替它。過去我也用過Global,但現在不大用了。一會兒用這個,一會兒用那個,各程式之間不能保持一致性了,起碼看起來彆扭。
結構
結構是C和C++語系中的說法。在VB中一般稱為自訂資料類型。想必很多朋友都已經認識它。在API領域裡,我更喜歡把它叫做結構,因為API各種結構類型根本不是我定義(自訂)的。
在VB中,API結構同樣由TYPE.......END TYPE語句來定義。如,在API中,點(Point)結構的定義方法如下:
Public Type POINTAPI
X As Long '點在X坐標(橫坐標)上的坐標值
Y As Long '點在Y坐標(縱坐標)上的坐標值
End Type
又如,API中矩形(Rect)結構的定義如下︰
Public Type RECT
Left As Long '矩形左上角的X坐標
Top As Long '矩形左上角的Y坐標
Right As Long '矩形右下角的X坐標
Bottom As Long '矩形右下角的Y坐標
End Type
這些內容同樣可以從API文字遊覽器中複制過來。這些結構中的變量名可隨意改動,而不會影響結構本身。也就是說,這些成員變量都是虛擬的。
例如POINTAPI結構可改為如下︰
Public Type POINTAPI
MyX As Long '點在X坐標(橫坐標)上的坐標值
MyY As Long '點在Y坐標(縱坐標)上的坐標值
End Type
不過,一般來講,是沒有這種必要的。結構本身是一種資料類型,因此,使用時必須聲明具體變量為該結構型,才能在程式中真正使用到該結構。結構的聲明方法和其他資料的聲明方法一樣,如,以下語句把變MyPoint聲明為POINTAPI結構類型︰
MyPoint As POINTAPI
引用結構中的成員變量也十分簡單,在結構名後面加上一個".",然後緊接著寫要引用的成員變量即可。這很像VB中的引用一個對象的某個屬性。如,假如我們把上面已經聲明的MyPoint結構中的X變量的值賦給變量Temp&
則代碼如下︰
Temp&=MyPoint.X
但特別注意的是,你千萬不要認為上例中的MyPoint是一個值。它不是值,而是位址(指標)。值和位址是完全不同的概念。結構要求按引用傳遞給WINDOWS函數,即所有API函數中,結構都是按ByRef傳遞的(在Declare語句 中ByRef是預設型)。對於結構的傳遞,你不要試圖採用ByVal,你將一無所獲。由於結構名實際上就是指向這個結構的指標(這個結構的首位址),所以,你也就傳輸特定的結構名就可以了(參見小結,我用紅色字型來突出了這種傳遞模式)。
由於結構傳輸的是指標,所以函數將直接對結構進行讀寫動作。這種特性很適合於把函數執行的結果裝載在結構之中。
小結
以下的程式是為了總結本課國中到的內容而給出的。啟動VB,新增一個項目,加入一個指令按鈕,並把下面的代碼複制到代碼段中,執行它。
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Type POINTAPI '定義點(Point)結構
X As Long '點在X坐標(橫坐標)上的坐標值
Y As Long '點在Y坐標(縱坐標)上的坐標值
End Type
Sub PrintCursorPos( )
Dim dl AS Long
Dim MyPoint As POINTAPI
dl&= GetCursorPos(MyPoint) '呼叫函數,取得螢幕滑鼠坐標
Debug.Print "X=" & Str(MyPoint.X) & " and " & "Y=" & Str(MyPoint.Y)
End Sub
Private Sub Command1_Click()
PrintCursorPos
End Sub
輸出結果為(每次執行都可能得到不同的結果,這得由函數呼叫時滑鼠指標在螢幕中所處的位置而決定)︰
X= 240 and Y= 151
程式中,GetCursorPos函數用來取得滑鼠指標在螢幕上的位置。
以上例子中,你可以發現,以參數傳遞的MyPpint結構的內容在函數呼叫後發生了實質性變化。這是由於結構是按ByRef傳遞的原因。
API函數
來自社群: 百變紀念群
|