3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

Delphi 2007体验!

發布時間:2023/12/4 综合教程 35 生活家
生活随笔 收集整理的這篇文章主要介紹了 Delphi 2007体验! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Delphi 2007體驗!

baidu

內容摘要:CodeGear(From Borland) 公司公布了最新的Delphi 2007 For Win32版本號。作為一個 Delphi 的使用者,第一時間下載、安裝并進行了體驗,現將一些使用感受記錄例如以下

CodeGear(From Borland) 公司公布了最新的Delphi 2007 For Win32版本號。作為一個 Delphi 的使用者,第一時間下載、安裝并進行了體驗,現將一些使用感受記錄例如以下:

注:以下的對照主要是針對Delphi7與Delphi2007,以下列出的部分功能可能在Delphi8/2005/2006中已存在。

1、下載

可在CodeGear官方站點下載試用版,或在VeryCD上尋找ISO,整個安裝文件約1.2G多一些。

2、安裝

Delphi 2007放棄了InstallShield,採用了InstallAware安裝軟件,整個安裝操作非常友好。在安裝結束時,會提示是否在Delphi啟 動時自己主動檢查更新,建議不要選擇此項,由于安裝完畢后無法禁用。Delphi 2007在開始菜單中建立了自己主動檢查更新的快捷方式。

3、啟動

Delphi 2007的啟動速度沒有傳說中那么快,但和Delphi7相比也差不了多少,整體感覺不錯。

4、界面

Delphi 2007的界面和之前的BDS 8/2005/2006界面風格是一樣的,個人感覺不是太好,由于整個IDE顏色偏暗,Visual Studio 2005那種白亮色的界面應該更好一些。Splash和Welcome Page作的還是那么粗糙,CodeGear應該盡快找個好美工啊。

5、速度

IDE啟動速度還不錯,IDE的反映速度也非常好,尤其是程序的編譯速度,個人感覺比Delphi7還快。

6、返回傳統界面

習 慣了Delphi7及之前版本號的界面,對Delphi2007的這樣的一體式界面多少有些難以適應,尤其是窗口設計器。盡管能夠通過更改Desktop Layout為Classic Undocked讓其與Delphi7有些相像,但卻失去了Delphi7的那種自由設計的效果。

內容摘要:CodeGear(From Borland) 公司公布了最新的Delphi 2007 For Win32版本號。作為一個 Delphi 的使用者,第一時間下載、安裝并進行了體驗,現將一些使用感受記錄例如以下

在Delphi2007中,更改一個選項,可讓IDE的窗口設計器返回Delphi的傳統風格:Tools–Options–VCL Designer,取消選中Embedded Designer。

此選項僅僅有在IDE重新啟動后才會生效,生效后整個界面和Delphi7差點兒相同,但組件面板無法達到傳統界面的效果。但此設計似乎有BUG,當IDE最小化的時候,窗口設計器卻沒有一同最小化。

7、新增屬性:Application.MainFormOnTaskBar

用Delphi2007新建一project,然后查看project文件的源碼,發現多一行代碼:

Application.MainFormOnTaskBar := True;

Delphi2007默認已將MainForm顯示于任務欄,而不是之前版本號的Application。這個功能在曾經非常多Delphier都討論過,如今Delphi自身支持了。設計此屬性非常明顯,因該是為了兼容Windows Vista。

當然任務欄的右鍵菜單也發生了變化:

(Delphi 7 任務欄右鍵菜單)

(Delphi 2007 任務欄右鍵菜單)

8、新Project Option: Enable Runtime Themes

該project選項默認啟用,用Delphi 2007編寫的程序默認將啟用Themes,這是一個非常好的功能,曾經必須用組件:Win32-XPManifest。

Delphi 2007 IDE本身、窗口設計器已支持操作系統Themes。

9、TeeChart升級為了TeeChart Standard 7.10

TeeChart最終升級了新版本號。

10、報表組件

Delphi 2007似乎沒有附帶不論什么報表組件,QuickReport和Rave消失了。

內容摘要:CodeGear(From Borland) 公司公布了最新的Delphi 2007 For Win32版本號。作為一個 Delphi 的使用者,第一時間下載、安裝并進行了體驗,現將一些使用感受記錄例如以下

11、DBExpress

DBExpress重大升級至v4,架構已重寫,使用此技術的Delphier能夠試試,本人非常少使用。

12、模態窗口下的窗口閃動

在當前窗口用ShowModal顯示一個模態窗口后,再次點擊當前窗口,此時顯示出的模態窗口會閃動,Delphi 2007編譯的程序最終已能實現此效果,這也是Windows程序的標準效果。

13、Project Clean 功能

在Project Manager中右鍵點擊project名稱,選擇Clean,會自己主動清除project的全部暫時文件和dcu文件。

14、實用的快捷鍵

最終為Build Project和Run Without Dedugging功能設置了快捷鍵。

15、比Delphi7超強的編輯器

Delphi2007的編輯器功能強大,這也應該是放棄Delphi7的重要理由,如輸入Begin,自己主動生成End,代碼重構,語法實時檢查,顯示行號等。

只是Delphi2007的那個代碼幫助提示信息的窗口真是太丑了。

16、窗口設計器控件感應對齊

窗口設計器中的控件能夠感應對齊,相當好的功能啊。

17、中文變量名

如今的Delphi已經支持中文變量名了,你能夠試試。

18、新的組件

TTrayIcon、TFlowPanel、TGridPanel三個控件非常實用。Delphi2007新增Vista Dialogs組件,這些組件效果非常好,可是基于Vista API的,所以僅僅能在Windows Vista下使用。

19、TLabel控件可在內容超出范圍時顯示省略號

此功能非常實用,尤其是在Label中顯示一個文件路徑時,設置TLabel的EllipsisPosition屬性就可以。

20、新增了一些實用的屬性

Delphi2007對大多數常規組件增加了一些實用的屬性,如Margins、Padding、TForm.PopupMode等,細致查看一些控件的屬性列表,你就會發現非常多陌生的屬性,但他們確實都非常實用。

用了Delphi 2007一段時間,但也發現一些小問題:

1、在Project Manager中更改PAS文件的名稱后,不自己主動更新uses列表中的名稱,呵呵~,這個要求不知道過只是分。

2、TMainMenu組件在窗口設計器中不可預覽。

3、編輯器錯誤提示功能會誤報,如Application.MainFormOnTaskBar屬性,有時會提示不存在該屬性。

4、新安裝的組件,不但要在Tool–Options中設置Library Path,還必須設置Browseing Path,組件才干被正常使用。

用了N年的Delphi7了,體驗了Delphi2007后覺得真應該換換了,綜合來看,Delphi2007是一個非常好的版本號,IDE速度及功能性各方面都已經非常優秀,你準備使用Delphi2007嗎?

Delphi 2007 初步印象

baidu

內容摘要:經過苦苦的等待,最終等來了新一代 Delphi 2007 的下載鏈接。昨天從 emule 上下載時,發覺有非常多的人在下載,讓我非常感動。原來和我一樣,對 Delphi 關注的人還不少啊。速度還算快,到夜里就下完了。接著開始了2個小時的試用,盡管不怎么細致,但從 Delphi 1 一路用過來的我,對里面的變化還比較敏感的。

經過苦苦的等待,最終等來了新一代 Delphi 2007 的下載鏈接。昨天從 emule 上下載時,發覺有非常多的人在下載,讓我非常感動。原來和我一樣,對 Delphi 關注的人還不少啊。速度還算快,到夜里就下完了。接著開始了2個小時的試用,盡管不怎么細致,但從 Delphi 1 一路用過來的我,對里面的變化還比較敏感的。

相比 Delphi 2006 來說,我覺得這個版本號的 Delphi 2007 是個超強的優化版,功能方面,僅僅有少些改變。說她是優化版,那是由于她的啟動速度,編譯效率,IDE 速度是相當的快(PS:我的電腦是PM1.4G,512MB)。李維先生所說的比 Delphi 7 快一點也不為過。近期用 VS2005,打開一個 C++ 控制臺程序,要經過相當長的時間,編譯就更不用說了。Welcome Page 里的“Where developers matter”真是讓人感動啊。

至于其它方面的改進,對我來說,并不是非常有吸引力。Help 系統改用 MSDN 的那套最新的 help,支持了VISTA,可是我的電腦是不能跑了。對 Together 的集成,這個非常實用??丶嗔藥讉€。IntraWeb 變成了 CodeGear 的 VCL 部分了,但卻不支持調試,這點有些奇怪。數據庫接口統一了。特別的一點就是,原來 Borland 的標識,如今全變成了 CodeGear。

事實上,我并不了解多少開發,更不知道開發者究竟須要 Delphi 2007 增加些什么功能。我中心一直覺得 Delhpi 發展到今天,變化的可能已經越來越少。就像 VC 一樣,非常少變動。Win32 RAD 的開發王者,應該還是屬于 Delphi(C++上的RAD工具非常少,即便有,也是對語言進行了一些惡心擴展),速度,效率,誰能相比。如今唯一的希望是 Delphi 2007 的語言再加強一些,如 template。近期用 C++,一直在學習 template,所以也希望 Delphi 能跟上時代。

內容摘要:本文介紹delph i2007學習筆記

如今學的是delphi 的類,原D7的類我不就不記了,記下與D7不同的地方

a.class abstract 純虛類,不能實例化的類

type
TAbstractClass = class abstract
procedure SomeProcedure;
end;

曾經的做法是在 procedure 的后面與 abstract ,如今僅僅移類的說明上,僅僅是意思一樣,就是直觀點少打字 呵呵.

b.class sealed 這個我眼下不知是什么意思,可能是不能繼承的類

type
TAbstractClass = class sealed
procedure SomeProcedure;
end;

c.class const 類的常量,這個地方在D7內能夠定類的方法一樣能實現

type
TClassWithConstant = class
public
const SomeConst = 'This is a class constant';
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowMessage(TClassWithConstant.SomeConst); //引用時,僅僅寫類名就可能引用,不必實例化
end;
d.class type 類的類型, 在類的層次下能夠定record,class子類什么的,這個將數據的集中體現....
type
TClassWithClassType = class
private
type
TRecordWithinAClass = record
SomeField: string;
end;
public
class var
RecordWithinAClass: TRecordWithinAClass;
end;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
TClassWithClassType.RecordWithinAClass.SomeField := 'This is a field of a class type declaration';
ShowMessage(TClassWithClassType.RecordWithinAClass.SomeField);
end;
type
TOuterClass = class
strict private
MyField: Integer;
public
type
TInnerClass = class
public
MyInnerField: Integer;
procedure InnerProc;
end;
procedure OuterProc;
end;
procedure TOuterClass.TInnerClass.InnerProc;
begin
...
end;

讓人期待的Delphi 2007 for Win32

baidu

內容摘要:從李維的Blog和一些網友的反饋來看,D2007確實是比較讓人期待的一個版本號。作為Delphi的忠實支持者,我期待著Delphi的逐步回歸,希望她能重登昔日的王者地位!

自從D7之后,Delphi似乎開始走了下坡路,到D2005時,讓非常多人感到了失望,而D2006也是非常不easy才挽回了一點局面。大家都知道如今Delphi跟Borland分家,歸屬于CodeGear繼續發展了。只是分出去或許會是好事,這能讓一幫人更為專注地發展IDE的技術。Delphi 2007 for Win32的公布會(包括Delphi for PHP和InterBase 2007)前幾天正在密鑼緊鼓地進行著,從李維的Blog和一些網友的反饋來看,D2007確實是比較讓人期待的一個版本號。作為Delphi的忠實支持者,我期待著Delphi的逐步回歸,希望她能重登昔日的王者地位!(這次不要當VB殺手了,來當當C#和Java殺手吧.... XD)

Delphi2007 for Win32一些技術亮點:

1、IDE工具採用.Net2.0來編寫,安裝時候須要.Net FrameWork2.0,但編譯出來的Exe是純正的Win32程序,公布不須要.Net FrameWork2.0。新IDE採用.Net FrameWork2.0是為了更好的統一IDE平臺,利用.Net的反射、泛型等高級特性,節省編寫IDE時間。新的IDE確實比D7啟動還要快,大概是五六秒時間左右就啟動,跟VS.Net2005差點兒相同。演示中,新的IDE編譯新建project比D7還要快一點。

2、Delphi2007支持Vista界面,封裝了Vsita的新API函數。

3、Delphi2007有一個功能非常有意思,支持D2006的全部BPL組件。意思是,假設你用D2006編譯過的BPL,沒有源碼和DCU,也能夠直接安裝在Delphi2007。哈哈,這個是delphi版本號上的史無前例,即使第三方控件包來不及支持Delphi2007,也最好還是礙開發者從D2006升級到Delphi2007做項目開發。

4、支持MS Build。比方支持Debug、Release。也支持編譯前和編譯后事件,可調用BAT文件。比方,你編譯前郵件告訴老板,你要給我加薪50%,否則十分鐘后銷毀源碼。另外,也能夠改動其XML格式,僅僅編譯特定的程序代碼。

5、Delphi2007 for Win32數據集控件全面支持Unicode。比方,你的數據庫表字段可採用中文名稱作字段。Filter也支持Unicode。唉,為了這個filter支持Unicode,有些人在delphiBBS上苦苦守候了六七年。但delphi2007的UI(可視化界面)還不是全面支持Unicode。比方,你的DBGrid就不能支持中文、韓文(不說狗屁日文)、阿拉伯文等同一時候顯示。原有的Vcl框架大量採用string聲明而不是WideString,據李維講,CodeGear會將Vcl全面支持Unicode,但要考慮一種最佳的平衡方法。

6、非常棒的DBX4。dbExpress將要統一混亂的數據庫連接組件,底層全部改寫。無論在Win32還是Win64上,可兼容本機代碼和托管代碼。呵呵,舉個樣例,DBX4可支持ADO,也能夠支持ADO.Net。這一點,M$也做不到。DBX4新增加了Connection Pool高級組件。更令人心動的是,開放Driver Source,可自行擴展屬性和方法,哈哈,ColreLab公司這回可高價賣不出去dbExpress驅動了。DBX4也全面支持Uicode。

7、Delphi2007 for Win32支持Ajax技術—Intraweb9.0。Intraweb9.0組件封裝了Ajax,以事件方式來驅動程序。Intraweb9.0的Ajax技術支持斷點跟蹤調試,簡單到跟你調試其它delphi程序一樣。Intraweb9.0不愧是封裝javascript的上乘之作。事實上,話又說回來了,這一兩年流行的Ajax技術,事實上無非就是javascript封裝而已。而Intraweb已經在六七年將javascript技術做得爐火純青。更令人汗顏的是,很多所謂的web2.0新技術調試Ajax時候,仿佛又回到20多年前的C編程時代,不停地用Printf打印調試。Ajax技術也并不是是什么高級技術,僅僅是web編程一種無奈的選擇。不久未來,應該是屬于智能client平臺。

個人的幾點看法:

1、說實話這里我經歷過大喜->大悲->大喜的三次變化。最初聽說Delphi重回Win32,當然是大喜,個人比較不爽.Net,由于給別人敲代碼還要別人裝無用的東西才干運行(當然對象是企業的話,沒什么所謂);之后在CSDN聽到一些殘缺不全的消息說D2007又要裝.Net了?轉而失望;幸而從這里看,僅僅是裝.Net的開發時環境而已,運行庫并不須要,至此疑慮全消。并且聽說D2007還能針對2000、XP和Vista多種系統公布不同的程序?這個功能太棒了!曾經寫的程序,總是部分人能用部分人不能,想出多版本號的話,自己多裝個系統在那個系統下編譯吧,這不是一般的麻煩。希望此功能是真的。

2、啟動和編譯速度比D7還快,這個也非常吸引人。Delphi的編譯速度本來就非常有名了(用過C系列的就能對照出來)。之后聽說D2006又用一種新技術改進了內存管理,可惜D2006還沒裝,沒能體驗。這下D2007竟然比D7還快了,希望不是僅僅針對特殊項目弄出來的“演示效果”。

3、關于Unicode,強烈期待實現整個IDE環境的Unicode化,某次寫跟韓文有關的程序已經被整慘過了.... -_,- 這點Delphi須要向C#靠攏。

4、盡管IntraWeb自上次寫日志來,還暫時沒安排到學習時間。只是偶還是相信那位Delphi達人的話,相信它的前途。如今都出到9.0了,我覺得假設Delphi在B/S上面能搶回份額,微軟將會面臨非常大的挑戰。

Delphi 2007 下安裝 Shell 組件

baidu

內容摘要:本文介紹Delphi 2007 下安裝 Shell 組件

Delphi 2007 中沒有 Shell 組件,但在Delphi的lib文件夾存在相關的源文件,并且在C:WindowsSystem32文件夾下存在vclshlctrls100.bpl的運行時包,只有沒有設計時包。

但在Delphi的Demo中有一個ShellControls,安裝其文件夾下的 dclshlctrls.dpk 就可以安裝成功Shell組件。

注1:Shell組件安裝后還是在組件面板的Sample頁中;

注2:Delphi2007的Demo文件夾在:我的電腦-共享文檔-RAD Studio-Demos-DelphiWin32-VCLWin32;

在 使用Shell這樣的組件時,程序編譯后都會出現以下的警告:[DCC Warning] Unit1.pas(7): W1005 Unit ‘ShellCtrls’ is specific to a platform ,假設您不想讓他顯示的話,能夠在你project相關源文件的開頭加上以下代碼:

{$WARN UNIT_PLATFORM OFF}

Delphi歸來,重燃開發者的信心

baidu

內容摘要:江元麟表示:「眼下僅僅有Microsoft Visual C++支持64位,但我們累積了非常多Delphi的Library和組件,基于穩定度及開發時效的考慮,并不希望換開發環境,眼下做法是花非常多力氣和C++整合。……我今天就是來問Delphi什么時候支持64Bit?」

CodeGear臺灣區產品經理李維介紹完新產品,聽眾迫不及待的走向講臺,紛紛問起新產品的兼容問題,知網生物識別科技技術長江元麟也是當中一位,他特地來問一個問題,由于這將影響到公司未來產品的開發效率。

Windows Vista出現帶來64位新挑戰。知網生物識別科技去年面臨客戶要求在Vista的Content Menu技術上支持64位檔案指紋加密。江元麟表示:「眼下僅僅有Microsoft Visual C++支持64位,但我們累積了非常多Delphi的Library和組件,基于穩定度及開發時效的考慮,并不希望換開發環境,眼下做法是花非常多力氣和C++整合?!医裉炀褪莵韱朌elphi什么時候支持64Bit?」

從1995年發表1.0版后,12年來,Delphi歷經11個版本號更迭,從16位的1.0到.NET平臺的BDS 2006。開發部門獨立成立CodeGear 后,又回到原生Win32環境下的Delphi 2007 for Win32。江元麟24年程序開發經驗,一路見證了Delphi的變化。

從1987年開始接觸Borland,江元麟用過Turbo Pascal和Turbo C。1995年,由于工作須要開始使用Delphi。2000年,他投入生物識別產業,繼續使用Delphi 5開發,他指出:「Delphi有一個非常好的長處是能夠開發自己的組件,它的組件讓我們的產品開發加速非???。新進project師能立即就作一些簡單的開發,這是Delphi最優秀的地方?!?/p>

相較于當時其它開發工具,他覺得:「VB當時沒辦法全然用對象導向的方式去開發組件,比較不是給Engineer用,而是給Power User使用。而C++要客制化組件難度頗高,它的平臺沒有那么靈活?!?/p>

為何一直用Delphi?江元麟解釋說:「是由于它的平臺,非常多Source Code都有釋出,所以你能夠開發一些真的是自己會用到的基層組件。我們公司的組件已經累積5年到10年都有,一個組件能夠撐那么久,代表它非常穩定了,相對的我們公司的產品出來質量是非常好的,這也是Delphi的貢獻……這也是為什么,我們寧可在Delphi上花力氣結合C++來處理新挑戰?!?年前,知網的識別軟件能讓Pentium 4 處理器在1秒內辨識十萬枚指紋,是當時國外最高速度的3倍,他說:「這當中有一部份是由Delphi編譯出來的程序代碼效率相當好的貢獻?!?/p>

盡管當天江元麟的問題沒有立即的解決方式,但對于脫離Borland后的GodeGear,他表示:「蠻喜歡分割出來的CodeGear,曾經步調非常慢,如今步調非常快,我比較喜歡,聽李維傳遞的訊息,感覺比較有活力,但希望能維持曾經的速度和質量。兩年前看到Borland公司非常亂,覺得非常遺憾,周圍的人兩年前已經慢慢轉到C#去了?!?#xff0c;他接著說:「我們本來去年要考慮轉成C#,如今要又一次考慮了

編碼的藝術

baidu

我在本文中要談的不是編碼的技術實現,我所關注的是關于編碼的風格的問題。我在編寫了e速的編碼規范之后,產生了要寫一些關于程序編碼風格的念頭;因此,就有了以下的文章,這些僅僅是本人的想法,可能在文章中還有一些未盡如人意的地方,所以肯請大家能夠給與諒解。

非常多人在談到編碼的藝術時,總會說我的程序怎么怎么的厲害,功能多么的強大,好像什么事情都能完畢一樣;可是去運行他的程序,bug不斷;連他自己都不知道錯在了什么地方。打開他的程序一看,代碼寫的凌亂不堪;命名上不規范,為了偷懶和簡便有些命名干脆就用一個字母或者其它的簡單符號取代,甚至于有些代碼連他自己也搞不清是干什么了,更不要說怎樣讓別人去改動了….本人編碼也快4個年頭了,像上述的樣例遇見過不少,整個程序改動起來實在是頭疼。

的確,一件好的藝術品不在于其功能是多么的完好,而在于別人贊賞起來是否有它內在的美和是否非常easy就把它從雜貨堆里一眼就能辨認出來;畢竟它是藝術品而非日用品。我們敲代碼也是同樣,假設程序中的格式非常隨意,比如對數組做循環,一會兒採用下標變量從下到上的方式,一會兒又用從上到下的方式;對字符串一會兒用s t r c p y做復制,一會兒又用f o r循環做復制;等等。這些變化就會使人非常難看清實際上究竟是怎么回事了。

寫好一個程序,當然須要使它符合語法規則、修正當中的錯誤和使它運行得足夠快,可是實際應該做的遠比這多得多。程序不僅須要給計算機讀,也要給程序猿讀。一個寫得好的程序比那些寫得差的程序更easy讀、更easy改動。經過了怎樣寫好程序的訓練,生產的代碼更可能是正確的。

凝視:凝視是幫助程序讀者的一種手段??墒?#xff0c;假設在凝視中僅僅說明代碼本身已經講明的事情,或者與代碼矛盾,或是以精心編排的形式干擾讀者,那么它們就是幫了倒忙。最好的凝視是簡潔地點明程序的突出特征,或是提供一種概觀,幫助別人理解程序。在標注凝視的同一時候,應該注意以下的問題:

不要大談明顯的東西。凝視不要去說明明確白的事,比方i + +能夠將i值加1等等。凝視應該提供那些不能一下子從代碼中看到的東西,或者把那些散布在很多代碼里的信息收集到一起。當某些難以捉摸的事情出現時,凝視能夠幫助澄清情況。假設操作本身非常明了,反復談論它們就是畫蛇添足了;給函數和全局數據加凝視。凝視當然能夠有價值。對于函數、全局變量、常數定義、結構和類的域等,以及不論什么其它加上簡短說明就能夠幫助理解的內容,我們都應該為之提供凝視。全局變量常被分散使用在整個程序中的各個地方,寫一個凝視能夠幫人記住它的意義,也能夠作為參考。放在每個函數前面的凝視能夠成為幫人讀懂程序的臺階。假設函數代碼不太長,在這里寫一行凝視就足夠了。有些代碼原本非常復雜,可能是由于算法本身非常復雜,或者是由于數據結構非常復雜。在這些情況下,用一段凝視指明有關文獻對讀者也非常有幫助。此外,說明做出某種決定的理由也非常有價值。

職業程序猿也常被要求凝視他們的全部代碼。可是,應該看到,盲目遵守這些規則的結果卻可能是丟掉了凝視的真諦。凝視是一種工具,它的作用就是幫助讀者理解程序中的某些部分,而這些部分的意義不easy通過代碼本身直接看到。我們應該盡可能地把代碼寫得easy理解。在這方面你做得越好,須要寫的凝視就越少。好的代碼須要的凝視遠遠少于差的代碼。

編碼的風格:全局變量應該採用具有描寫敘述意義的名字,局部變量用短名字。函數採用動作性的名字。給神奇的數起個名字?,F實中存在很多命名約定或者本地習慣。常見的比方:指針採用以p結尾的變量名,比如n o d e p;全局變量用大寫開頭的變量名,比如G l o b a l;常量用全然由大寫字母拼寫的變量名,如C O N S T A N T S等。命名約定能使自己的代碼更easy理解,對別人寫的代碼也是一樣。這些約定也使人在寫代碼時更easy決定事物的命名。對于長的程序,選擇那些好的、具有說明性的、系統化的名字就更加重要。

保持一致性。要準確。以縮行形式顯示程序結構。使用表達式的自然形式。利用括號排除歧義。分解復雜的表達式。要清晰。當心副作用。使用一致的縮行和加括號風格。為了一致性,使用習慣使用方法。用else-if 處理多路選擇。避免使用函數宏。給宏的體和參數都加上括號。這些都是非?,嵥榈氖虑?#xff0c;但卻又是非常有價值的,就像保持書桌整潔能使你easy找到東西一樣。與你的書桌不同的是,你的程序代碼非常可能還會被別人使用。

用縮行顯示程序的結構。採用一種一致的縮行風格,是使程序呈現出結構清晰的最省力的方法。

用加括號的方式排除二義性。括號表示分組,即使有時并不必要,加了括號也可能把意圖表示得更清晰。在混合使用互相無關的運算符時,多寫幾個括號是個好主意。C語言以及與之相關的語言存在非常險惡的優先級問題,在這里非常easy犯錯誤。比如,由于邏輯運算符的約束力比賦值運算符強,在大部分混合使用它們的表達式中,括號都是必需的。

利用語言去計算對象的大小。不要大談明顯的東西。給函數和全局數據加凝視。不要凝視不好的代碼,應該重寫。不要與代碼矛盾。澄清情況,不要添亂。

界面的風格:隱蔽實現的細節。不要在用戶背后搞小動作。在各處都用同樣方式做同樣的事。釋放資源與分配資源應該在同一層次進行。在低層檢查錯誤,在高層處理。僅僅把異經常使用在異常的情況。

寫良好的代碼更easy閱讀和理解,差點兒能夠保證當中的錯誤更少。進一步說,它們通常比那些馬馬虎虎地堆起來的、沒有細致推敲過的代碼更短小。在這個拼命要把代碼送出門、去趕上最后期限的時代,人們非常easy把風格丟在一旁,讓將來去管它們吧??墒?#xff0c;這非常可能是一個代價非常昂貴的決定。上面的一些陳述性的言語充分的說明了,假設對好風格問題重視不夠,程序中哪些方面可能出毛病。草率的代碼是非常壞的代碼,它不僅難看、難讀,并且經常崩潰。好風格應該成為一種習慣。假設你在開始寫代碼時就關心風格問題,假設你花時間去審視和改進它,你將會逐漸養成一種好的編程習慣。一旦這樣的習慣變成自己主動的東西,你的潛意識就會幫你照料很多細節問題,甚至你在工作壓力下寫出的代碼也會更好

Delphi面向對象的編程方法

baidu

Delphi的編程語言是以Pascal為基礎的。Pascal語言具有可讀性好、編寫easy的特點,這使得它非常適合作為基礎的開發語言。同一時候,使用編譯器創建的應用程序僅僅生成單個可運行文件(.EXE),正是這樣的結合,使得Pascal成為Delphi這樣的先進開發環境的編程語言。

本章中,我們將討論Object Pascal的主要特點,并解說怎樣在事件處理過程和其它應用程序中,使用它來編制程序代碼。本章將解說Delphi應用程序中最經常使用的Object Pascal語法,而不是Pascal語言的一切細節。假設您全然不熟悉Pascal編程,請參閱一些基礎的Pascal教程。假設您具有編程經驗,并能熟練地使用其它流行程序語言,您將在本章的Object Pascal中發現一些同樣的概念。假設您已經熟悉了Borland Pascal,就能夠高速瀏覽或跳過本章。

2.1 編寫Object Pascal程序代碼

在前邊的章節中,我們通過例程,已經編寫了幾行簡單的代碼。在本章中,我們將從熟悉Pascal編程的角度,配合實例,解說Object Pascal編程的基本方法。

在編寫自己的Object Pascal程序時,要注意程序的可讀性。Pascal語言是英式結構語言,在程序中選擇合適的縮排、大寫和小寫風格,并在須要時將程序代碼分行,會使得程序代碼能夠非常easy地被自己和他人讀懂。一般的程序猿都有這樣的體驗:假設不給程序加上適當的注解,一段時間后,自己也難以理清程序的流程。給程序及時地加上凝視是良好的編程習慣。Delphi的凝視須要加注在{}之間,編輯器會把它們處理成為空白。Delphi保留了Borland Pascal編輯器的風格,keyword採用黑體字,被凝視的部分會變暗,這使得編程風格良好,易讀易寫。

2.1.1 編寫賦值語句

在事件處理過程中,最經常使用到的工作就是把一個新值賦給一個屬性或變量。在設計用戶界面時,能夠使用Object Inspector(Object Inspector)來改變其屬性;但有時須要在程序運行時改變屬性的值,并且有些屬性僅僅能在運行時改變,這些屬性在Delphi的在線幫助的“Proprety”主題中被標為運行期屬性。進行這樣的改變,就必須使用賦值語句。

下文的賦值語句表征一個OnClick事件。當button按動后,將編輯框部件Edit1的Color屬性置為clRed:

procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Color := clRed;
end;

當按動button后賦值語句被運行,編輯框變成紅色。

在語句中,部件的名稱在屬性前,中間用“.”表示屬性的所屬關系。這樣就準確地指定了要將clRed值賦給哪一部件的哪一屬性。賦值號為“:=”,不論給屬性還是給變量賦值,都是將右邊的值賦給左邊的屬性或變量。

當將一個屬性值、變量、常量或文本數據賦給屬性或變量時,所賦值的類型和接受此值的屬性或變量的類型應同樣或兼容。一個屬性或變量的類型定義了此屬性或變量的可能值集合,也定義了程序代碼能夠運行的運算。在前邊的例程中,編輯框部件的Color屬性和clRed的類型都是TColor。能夠在在線幫助中找到一個屬性的類型;第二種方法是在Object Inspector中選定該屬性值段,并按下F1鍵,則類型將在屬性說明的結尾處列出,比如Color屬性列出下邊的語句:

Property Color : TColor;

有些屬性是僅僅讀(Read Only)的,它們僅僅能被讀取,不能被改變。請查閱在線幫助,在Delphi中這些僅僅讀屬性都有注解。

2.1.2 標識符的說明與使用

標識符是Delphi應用程序中一些量的名稱,這些量包括變量(var)、常量(const)、類型(type)、過程(procedure)、方法(Method)及其它,Object Pascal 在應用標識符時,必須首先說明它們。Object Pascal是強類型語言,它的編譯器能夠檢查確保賦給變量或屬性的值是正確的類型,以便于您改正錯誤。由于Object Pascal是編譯語言,所以Delphi的運行速度要比使用解釋語言快得多。在使用標識符前說明它們,能夠降低程序錯誤并增加代碼的效率。

2.1.2.1 變量

變量是程序代碼中代表一個內存地址的標識符,而此地址的內存內容在程序代碼運行時能夠被改變。在使用變量前必須對它進行說明,即對它進行命名,并說明它的類型。在全部變量說明曾經加上保留字var。變量說明左邊是變量的名稱,右邊則是該變量的類型,中間用(:)隔開。

var
Value ,Sum : Integer;
Line : String;

在窗口中增加一個名稱為Edit1的編輯框,再增加一個名稱(屬性Name)為Add的button部件,并建立例如以下的事件處理過程:

procedure TForm1.addClick(Sender: TObject);
var
X , Y: Integer;
begin
X := 100;
Y := 20;
Edit1.Text := IntToStr(X + Y);
end;

在本例中,當按動ADDbutton時,編輯框中顯示值120。在Object Pascal中,必須確保變量或屬性被賦予類型同樣或兼容的值。您能夠嘗試將賦給X的值改為100.0,或去掉IntToStr函數,在編譯時會出現類型不匹配的錯誤,這也說明了Object Pascal強類型語言的特點。

2.1.2.2 提前定義類型

Object Pascal有多個提前定義的數據類型,您能夠說明不論什么這些類型的變量:

整形:Integer的范圍是-32768到32767,占2字節的內存;Shortint從-128到127,占1字節內存;Longint從-2147443648到2147483647 占4字節內存;Byte從0到255,占1字節;Word從0到65535,占2字節內存。它們都是沒有小數部分的數字。

實型:Single能夠包括7到8位有效小數部分,占用4字節的內存;Double類能夠包括15到16位有效小數部分,占用8字節的內存;Extended類型包括19到20位有效小數部分,占用10字節內存;Comp能夠包括19到20位有效小數部分,占用8字節內存。以上實數類型僅僅有在8087/80287選項[N+]打開才干夠使用。Real能夠包括11到12位有效小數部分,占用6字節內存。它僅僅有在和曾經Borland Pascal兼容的情況下才使用,否則應使用Double或Extended。

布爾型:Boolean,僅僅包括true或False兩個值,占用1字節內存。

字符型:Char,一個ASCII字符;字符串類型String一串最長可達255個ASCII字符。

指針型:Pointer,能夠指向不論什么特定類型。

字符串型:PChar,是一個指向以零結尾的字符串的指針。

除了提前定義類型外,Delphi還有自行定義的類型。上述例程的TColor就是這樣的類型。此外,用戶還能夠定義自己的數據類型,這部分內容將在下文中具體講述。

整型類別和實型類別都各有五種類型,同一類別中,全部的類型與其它同類別的都相容,您能夠將一種類型的值賦給同樣類別中不同類型的變量或屬性,而僅僅須要這個值的范圍在被賦值的變量或屬性的可能值范圍內。比如,對于一個Shortint型的變量,能夠接受在-128到127范圍內的隨意整數,比如Shortint類型的7;您不能將300賦給它,由于300已經超出了Shortint的范圍了。將范圍檢查功能打開(選用Options|Project,并在Compiler Options Page中選擇Range Checking),將會檢查出一個范圍錯誤;假設Range Checking沒有被打開,那么程序代碼將能夠運行,但被賦值的值將不是您期望的值。

在一些情況下,您能夠進行不同類型的變量或屬性的賦值。一般來說,能夠將一個較小范圍的值賦給一個較大范圍的值。比如,您能夠將整型值10賦給一個接受實型值的Double屬性而使得值成為10.0,但假設將一個Double類型的值賦給整形變量,則會出現類型錯誤。假設您不清晰類型的兼容性,能夠參閱Delphi的在線幫助中“Type Compatibility and Assignment Compatibility”主題。

2.1.2.3 常量

常量在說明時就被賦予了一個值,在程序運行過程中是不可改變的。以下的樣例說明了三個常量:

const
Pi = 3.14159;
Answer = 342;
ProductName = "Delphi";

象變量一樣,常量也有類型。不同的是,常量假設其類型就是常量說明中其所代表的值的類型。上文的三個常量的類型各自是real型、整形、字符串型。常量用“= " 表示兩邊的值是相等的。

2.1.3 過程與函數

過程與函數是程序中運行特定工作的模塊化部分。Delphi的運行庫包括很多過程與函數以供您的應用程序調用。您不必了解過程與函數的邏輯,但要知道過程與函數的用途。在對象中說明的過程和函數稱為方法(Method)。全部的事件處理過程都是過程,以保留字procedure開頭。每個事件處理過程僅僅包括了當這一事件發生時須要運行的程序代碼。在事件處理過程中使用Delphi已經存在的過程與函數,僅僅需在程序代碼中調用它們就可以。

2.1.3.1 一個調用Delphi方法的簡單例程

下文將通過對一個Memo部件的文本進行剪切、拷貝、粘貼、清除等編輯的應用程序編制,介紹使用Delphi過程和函數的調用方法。

Memo(備注)部件有一個CutToClipboard方法,實現將用戶在memo中選擇的文本移到剪貼板上去。由于這個功能已經被建立在此方法中了,所以您僅僅需知道這種方法做什么以及怎樣使用它就可以。

以下的語句表明怎樣調用一個名為Memo1的memo部件的CutToClipboard方法:

Memo1.CutToClipboard;

通過指定Memo1的名稱,說明調用哪一個部件的CutToClipboard方法。假設不指明對象名稱,Delphi會顯示Unknown identifier錯誤。當該事件處理過程被觸發,程序會運行CutToclipboard中的語句,將Memo1中的文本剪貼到剪貼板上去。

下文的例程展示了怎樣調用Delphi的方法,實現將備注部件的文本信息剪切、復制到剪貼板上;將剪貼板上的標記文本粘貼到備注中,清除備注部件中的全部文本等四個功能。

打開一個新的空窗口,增加一個memo部件和四個button,并排列整齊。改變button部件的Name屬性,分別命名為Cut,Copy,Paste,Clear。您會發現,當Name屬性發生改變時,Caption屬性將發生對應的變化。在Caption屬性前加標“&”號設立加速鍵

將memo部件的ScrollBars屬性設為ScVertical,以便加上滾行條。將WordWrap屬性設置為True,這樣當用戶輸入文本到達Memo部件的右邊緣時會自己主動回行。將Line屬性第一行的Memo1文本刪除,使得memo部件在初始顯示時為空的。

為每個button建立例如以下的事件處理過程:

procedure TForm1.CutClick(Sender: TObject);
begin
Memo1.CutToClipboard;
end;
procedure TForm1.CopyClick(Sender: TObject);
begin
Memo1.CopyToClipboard;
end;
procedure TForm1.PasteClick(Sender: TObject);
begin
Memo1.PasteFromClipboard;
end;
procedure TForm1.ClearClick(Sender: TObject);
begin
Memo1.clear;
end;

運行此程序。您能夠在備注部件中輸入文本,在進行了文本的標記后,能夠隨意地進行剪切、拷貝、粘貼和清除。當button被按動時,就調用對應的過程進行處理。用戶能夠通過查閱在線幫助進行Memo部件的Topic Search,在Memo Component項中查閱Method,會得到以上過程的具體說明。

2.1.3.2 調用Delphi的含參過程

有些過程要求用戶指明參數。被調用的過程會在運行時使用傳入的參數值,這些值在過程中被覺得是已經被說明的變量。比如,LoadFromFile方法在TString對象中被說明為:

Procedure LoadFromFile(const FileName: String);

在調用這一過程時,應指明FileName參數是要裝入的文件名。以下的程序將先打開Open對話框,當您選擇了一個文件后,Delphi將把該文件讀入一個Memo部件:

begin
OpenDialog.Execute;
Memo1.lines.LoadFromFile(OpenDialog.FileName);
end;

2.1.3.3 使用Delphi函數

與過程一樣,函數的程序代碼也運行特定的工作。它和過程的差別為:函數運行時會返回一個值,而過程則沒有返回值。函數能夠用來賦給一個屬性或變量;也能夠使用返回值來決定程序的流程。

前文中我們實際上已經接觸過了函數。在講述變量時,曾用到過以下的程序段: Edit1.Text := IntToStr(X + Y);當中,IntToStr(Value)把一個LongInt類型的數值轉化為字符串的值,Value是IntToStr唯一的參數,它能夠是一個整形的值、變量、屬性或產生整形值的表達式。調用函數,必須把返回值賦給和此返回值類型兼容的變量或屬性。

有些函數返回一個True或False的布爾量,用戶的程序能夠依據返回值來決定跳轉。下文的例程講述了函數返回值為Boolean的推斷使用方法:

在窗口中增加一個ColorDialog對象和一個Name屬性為ChangeColor的button。為button的OnClick事件建立事件處理步驟例如以下:

procedure TForm1.ChangeColorClick(Sender: TObject);
begin
if ColorDialog1.Execute then
Form1.Color := ColorDialog1.Color
else
Form1.Color := clRed;
end;

此事件處理過程使用一個返回Boolean值的Execute方法。按動button,并在顏色對話框中選擇一個顏色。假設按動OKbutton,ColorDialog.Execute方法將返回True,則Form1.Color將被賦值為ColorDialog1.Color,窗口顯現您選用的顏色;假設按動顏色對話框的Cancelbutton,方法將返回False值,窗口將變為紅色。

2.1.4 跳轉語句

Object Pascal的跳轉語句有if和case兩個。

2.1.4.1 if語句

if語句會計算一個表達式,并依據計算結果決定程序流程。在上文的例程中,依據ColorDialog.Execute的返回值,決定窗口的背景顏色。if保留字后尾隨一個生成Boolean值True或False的表達式。一般用“=”作為關系運算符,比較產生一個布爾型值。當表達式為True時,運行then后的語句。否則運行else后的代碼,if語句也能夠不含else部分,表達式為False時自己主動跳到下一行程序。

if語句能夠嵌套,當使用復合語句表達時,復合語句前后需加上begin…end。else保留字前不能加“;”,并且,編譯器會將else語句視為屬于最靠近的if語句。必要時,須使用begin…end保留字來強迫else部分屬于某一級的if語句。

2.1.4.2 case語句

case語句適用于被推斷的變量或屬性是整形、字符型、枚舉型或子界型時(LongInt除外)。用case語句進行邏輯跳轉比編寫復雜的if語句easy閱讀,并且程序代碼整形較快。

以下的例程顯示一個使用case語句的窗口:

建立例如以下的事件處理過程:

procedure TForm1.Button1Click(Sender: TObject);
var
Number : Integer;
begin
Number := StrToInt(Edit1.Text);
case Number of
1,3,5,7,9: Label2.Caption := '奇數';
0,2,4,6,8: Label2.Caption := '偶數';
10..100:
begin
Label2.Caption := '在10到100之間';
Form1.Color := clBlue;
end;
else
Label2.Caption := '大于100或為負數';
end;
end;

運行程序,當Edit1部件接受到一個值,并按動“OK”button觸發程序后,Number便被賦值為用戶輸入的數值。case語句依據Number的值推斷該運行哪一條語句。象if語句一樣。case語句也有可選擇的else部分。case語句以end結尾。

2.1.5 循環語句

Object Pascal的循環語句有三種:repeat、while和for語句。

2.1.5.1 repeat語句

repeat語句會反復運行一行或一段語句直到某一狀態為真。語句以repeat開始,以until結束,其后尾隨被推斷的布爾表達式。參閱以下的例程:

i := 0;
repeat
i := i+1;
Writen(i);
until i=10;

當此語句被運行時,窗口的下方會出現1到10的數字。布爾表達式 i=10 (注意,與其它語言不同的是,“=”是關系運算符,而不能進行賦值操作)直到repeat..until程序段的結尾才會被計算,這意味著repeat語句至少會被運行一次。

2.1.5.2 while語句

while語句和repeat語句的不同之處是,它的布爾表達式在循環的開頭進行推斷。while保留字后面必須跟一個布爾表達式。假設該表達式的結果為真,循環被運行,否則會退出循環,運行while語句后面的程序。

以下的例程達到和上面的repeat例程達到同樣的效果:

i := 0;
while i<10 do
begin
i := i+1;
writeln(i);
end;

2.1.5.3 for語句

for語句的程序代碼會運行一定的次數。它須要一個循環變量來控制循環次數。您須要說明一個變量,它的類型能夠是整形、布爾型、字符型、枚舉型或子界型。

以下的程序段會顯示1到5的數字,i為控制變量:

var
i : integer;
for i := 1 to 5 do
writeln(i);

以上介紹了三種循環語句。假設您知道循環要運行多少次的話,能夠使用for語句。for循環運行速度快,效率比較高。假設您不知道循環要運行多少次,但至少會運行一次的話,選用repeat..until語句比較合適;當您覺得程序可能一次都不運行的話,最好選用while..do語句。

2.1.6 程序模塊

程序模塊在Object Pascal中是非常重要的概念。它們提供了應用程序的結構,決定了變量、屬性值的范圍及程序運行的過程。它由兩個部分組成:可選擇的說明部分和語句部分。假設有說明部分,則必在語句部分之前。說明部分包括變量說明、常量說明、類型說明、標號說明、程序,函數,方法的說明等。語句部分敘述了可運行的邏輯行動。

在Delphi中,最常見的程序模塊便是事件處理過程中的程序模塊。以下的事件處理過程是含有變量說明部分的程序模塊:

procedure TForm.Button1Click(Sender Tobject);
var {程序模塊的說明部分}
Name : string;
begin {程序模塊的語句部分}
Name := Edit1.Text;
Edit2.Text := 'Welcome to Delphi'+Name;
end; {程序模塊結束}

庫單元也是程序模塊。庫單元的interface部分含有庫函數、類型、私有,公有域的說明,也能夠含有常量、變量的說明。這一部分能夠作為程序模塊的說明部分。在庫單元的implementation部分中通常含有各種事件處理過程,它們能夠視為模塊的語句部分,是事件處理模塊。庫單元模塊結束于庫單元結束的end.處。

程序模塊中能夠包括其它的程序模塊。上文庫單元模塊中含有事件處理模塊。而庫單元模塊實際是在project程序模塊中。

全部的Delphi應用程序都有同樣的基本結構。當程序逐漸復雜時,在程序中增加模塊就可以。比如在庫單元模塊中增加事件處理模塊,向project中增加庫單元模塊等。模塊化編程使得程序結構良好,并且對數據具有保護作用。

2.1.7 關于作用范圍

2.1.7.1 標識符的作用范圍

一個變量、常量、方法、類型或其它標識符的范圍定義了這個標識符的活動區域。對于說明這個標識符的最小程序模塊而言,此標識符是局部的。當您的應用程序在說明一個標識符的程序模塊外運行時,該標識符就不在此范圍內。這意味著此時運行的程序無法訪問這個標識符,僅僅有當程序再度進入說明這個標識符的程序模塊時,才干夠訪問它。

以下的示意圖表示一個含有兩個庫單元的project,每個庫單元中又各有三個過程或事件處理過程。

2.1.7.2 訪問其它程序模塊中的說明

您能夠在當前的程序模塊中訪問其它程序模塊中的說明。比如您在庫單元中編寫一個事件處理過程來計算利率,則其它的庫單元能夠訪問這個事件處理過程。要訪問不在當前庫單元中的說明,應在這個說明之前加上其它應用程序的名稱和一個點號(.)。比如,在庫單元Unit1中有事件處理過程CalculateInterest過程,如今您想在庫單元Unit2中調用這一過程,則能夠在Unit2的uses子句中增加Unit1,并使用以下的說明:

Unit1.CalculateInterest(PrincipalInterestRate : Double);

應用程序的代碼不能在一個模塊外訪問它說明的變量。事實上,當程序運行跳出一個模塊后,這些變量就不存在于內存中了。這一點對于不論什么標識符都是一樣的,無論事件處理過程、過程、函數還是方法,都具有這一性質。這樣的標識符稱為局部變量。

2.1.7.3 依照作用范圍說明標識符

您能夠在應用程序的不同地方說明一個標識符,而僅僅需保證它們的有效范圍不同就可以。編譯器會自己主動訪問最靠近當前范圍的標識符。

庫單元的全局變量一般能夠說明在保留字implementation后面。比如,以下的例程實現將兩個編輯框中的整數相加,顯示在第三個編輯框中。用到了一個整形的全局變量Count:

…implememntation
var
Count : Integer;
procedure TForm1.AddClick(Sender:TObject);
var
FirstNumber,SecondNumber:Integer;
begin
Count := Count + 1;
Counter.Text := IntToStr(Count);
FirstNumber := StrToInt(Edit1.Text);
SecondNumber := StrToInt(Edit2.Text);
Edit3.Text := IntToStr(FirstNumber+SecondNumber);
end;

為了實現每按動一次buttonCount增加一次,必須對全程變量Count進行初始化處理。在程序庫單元的結尾處,最后一個end.保留字之前,增加保留字initialization和初始化Count的代碼:


initialization
Count := 0;

這樣當事件處理過程AddClick被觸發時,Count就會被增加一次,以表征計算次數。假設用面向對象編程,則Count能夠說明成窗口的一個域,這在下一節中將有講述。

2.1.8 編寫一個過程或函數

在您開發Delphi應用程序時,所需的大部分代碼都編寫在事件處理過程中,但有時仍然須要編寫不是事件處理過程的函數或過程。比如,您能夠把在多個事件處理過程中用得到語句編寫成過程,然后不論什么事件處理過程、過程、函數都能夠象調用已經存在的過程或函數一樣直接調用它。長處是您僅僅需編寫一次代碼,并且程序代碼會比較清晰。

2.1.8.1 一個自行編寫的函數例程

在上文兩個數相加的程序中,假設編輯框中無值,則會使得程序出錯中斷。為避免這樣的情況,編寫以下的函數,檢查編輯框中是否有值,如無值,則提醒用戶輸入:

function NoValue(AnEditBox:TEdit):Boolean;
begin
if AnEditBox.Text='' then
begin
AnEditBox.Color := clRed;
AnEditBox.Text := '請輸入整數值';
Result := True;
end
else
begin
AnEditBox.Color := clWindow;
Result := False;
end;
end;

NoValue函數會檢查編輯框是否為空,假設是,編輯框顏色變紅,并提醒用戶輸入一個整數,然后函數返回真值;Result保留字在Delphi中用來專指函數返回值。在上文的例程中增加NoValue函數:

procedure TForm1.AddClick(Sender: TObject);
var
FirstNumber,SecondNumber : Integer;
begin
if NoValue(Edit1)or NoValue(Edit2) then
exit;
Count := Count + 1;
Counter.Text := IntToStr(Count);
FirstNumber := StrToInt(Edit1.Text);
SecondNumber := StrToInt(Edit2.Text);
Edit3.Text := IntToStr(FirstNumber+SecondNumber);
end;

假設當中的不論什么一個返回真值,則表示有編輯框空,會運行exit過程,使得當前的程序模塊停止運行,并使得編輯框出現輸值提示。當新值被輸入后,再運行程序時,紅色提示被隱去,恢復正常的計算狀態。

2.1.8.2 過程和函數的標題

每個過程或函數都以標題開始,當中包括過程或函數的名稱和它使用的參數。過程以保留字procedure開始,函數以保留字function開始。參數位于括號中面,每個參數以分號分隔。比如:

procedure validateDate(Day : Integer; month : Integer; Year : Integer);

您也能夠將同樣類型的參數組合在一起,則上述過程頭寫作:

procedure ValidateDate(Day, Month, Year : Integer);

函數在標題中還多了一項:返回值的類型。以下是一個返回值為Double型的函數標題:

function CalculateInterest(principal,InterestRate:Double):Double;

2.1.8.3 函數和過程中的類型說明

一個過程或函數程序模塊也含有說明部分和語句部分。說明部分能夠包括類型說明、變量說明、常量說明等。除了Object Pascal語言中已經定義的類型之外,Delphi的應用程序還能夠建立新的數據類型。類型說明部分有保留字type開始。以下是一些類型的說明:

type
Tcount = Integer;
TPrimaryColor = (Red,Yellow,Blue);
TTestIndex = 1..100;
TTextValue = -99..99;
TTestList = array [TTestIndex] of TTestValue;
TCharVal = Ord('A')..Ord('Z') ;
Today = (Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,
Sunday) ;

在類型標識符后面,用“=”號定義了新的類型。類型界定了變量的取值范圍,比如,TCount類型的變量必須是整形值;一個TPrimaryColor類型的變量僅僅能是red、yellow或blue等等。每個類型的名稱都是由字母T開始,這并不是必須的,但它是Delphi的慣例,在差別類型名和標識符時非常實用。類型說明能夠是局部的,也能夠是全局的。假設您把它放在implementation后面,則表明對于庫單元來講,它是全局的,全部的事件處理過程和其它的過程、函數都能夠調用它。假設類型是在過程中被說明的,則是局部的,離開這一過程,該類型將失效。

一般來講,在過程和函數中,不論什么類型說明都在變量說明之前,而不論什么變量說明都在常量之前??墒?#xff0c;僅僅要遵從說明必須在過程與函數的標題之后,并且在程序代碼之前,即是有效的。

2.1.8.4 過程和函數的語句部分

過程或函數的語句部分由begin開始,end結束。函數須要一個返回值。能夠將返回值賦給函數名稱,也能夠將返回值賦給Result變量。以下的例程將返回值賦給函數名稱:

function CalculateInterest(Principal,InterestRate: Double):Double;
begin
CalculateInterest := Principal * InterestRate;
end;

將返回值賦給Result變量也是能夠的,則上面的程序改為:

Result := Principal*InterestRate;

以下是這個函數的調用方法:

InterestEarned :=CalculateInterest(2000,0.012);

在Implementation后面的過程和函數,能夠且僅僅能被此庫單元的事件處理過程使用。要讓過程和函數能夠被其它的程序庫單元使用,則須要將過程或函數的標題部分放在庫單元中的interface部分,而把含標題的整個過程或函數放在庫單元的inplementation部分,并在要訪問這個過程或函數的庫單元的uses子句中增加說明這個過程或函數的庫單元名稱。

2.1.8.5 函數的遞歸調用

在Object Pascal中,過程或函數必須先說明再調用。上文的NoValue函數必須在使用它的事件處理過程之前說明和運行,否則程序會報告一個未知標識符的錯誤。

以上規則在遞歸調用時是例外情況。所謂遞歸調用,是指函數A調用函數B,而函數B又調用函數A的情況。在遞歸調用中,函數要進行前置,即在函數或過程的標題部分最后加上保留字forword。下文的例程是一個遞歸調用的典型樣例:


implementation
var
alpha:Integer;
procedure Test2(var A:Integer):forword;
{Test2被說明為前置過程}
procedure Test1(var A:Integer);
begin
A :=A-1;
if A>0 then
test2(A); {經前置說明,調用未運行的過程Test2}
writeln(A);
end;
procedure Test2(var A:Integer);{經前置說明的Test2的運行部分}
begin
A :=A div 2;
if A>0 rhen
test1(A); {在Test2中調用已運行的過程Test1}
end;
procedure TForm1.Button1Click(Sender:TObject);
begin
Alpha := 15; {給Alpha賦初值}
Test1(Alpha); { 第一次調用Test1,遞歸開始}
end;

button的OnClick事件處理過程給Alpha賦初值,并實現先減1再除2的循環遞歸調用,直到Alpha小于0為止。

2.1.8.6 過程和函數的參數

當您的程序代碼在調用一個過程或函數時,通經常使用參數傳遞數據到被調用的過程或函數中。最經常使用的參數有數值參數、變量參數和常量參數三種。

由被調用過程或函數定義的參數為形參,而由調用過程或函數指明的參數叫實參。在NoValue函數中,說明函數體中的AnEditBox是形參,而調用時在if NoValue(Edit1)…中,Edit1是實參。

數值參數在運行過程中僅僅改變其形參的值,不改變事實上參的值,即參數的值不能傳遞到過程的外面。試看以下的例程:

procedure Calculate(CalNo:Integer);
begin
CalNo := CalNo*10;
end;

用以下例程調用Calculate函數:


Number := StrToInt(Edit1.Text);
Calculate(Number);
Edit2.Text := IntToStr(Number);

Number接受由編輯框1輸入的數值,經Calculate過程運算。它是一個數值型實參。在進入Calculate函數后,會把Number實參拷貝給形參CalNo,在過程中CalNo增大十倍,但并未傳遞出來,因此Number值并未改變,在編輯框2中顯示仍然是編輯框1中的輸入值。形參和實參占用不同的內存地址,在過程或函數被調用時,將實參的值復制到形參占用的內存中。因此出了過程或函數后,形參和實參的數值是不同的,但實參的值并不發生變化。

假設您想改變傳入的參數值,就須要使用變量參數,即在被調用程序的參數表中的形參前加上保留字var。比如:

procedure Calculate(var CalNo : Integer);

則CalNo并不在內存中占領一個位置,而是指向實參Number。當一個變參被傳遞時,不論什么對形參所作的改變會反映到實參中。這是由于兩個參數指向同一個地址。將上一個例程中過程頭的形參CalNo前面加上var,再以同樣的程序調用它,則在第二個編輯框中會顯示計算的結果,把第一個編輯框中的數值放大十倍。這時形參CalNo和實參Number的值都是Nnmber初始值的10倍。

假設當過程或函數運行是要求不改變形參的值,最保險的辦法是使用常量參數。在參數表的參數名稱前加上保留字const能夠使一個形參成為常量參數。使用常量參數取代數值參數能夠保護您的參數,使您在不想改變參數值時不會意外地將新的值賦給這個參數。

2.1.9 定義新的數據類型

Object Pascal有一些系統提前定義的數據類型,在2.1.2中已經對它們作了介紹。您能夠利用這些數據類型以建立新的數據類型來滿足程序的特定須要。以下簡單地敘述了您能建立的主要數據類型,如枚舉型、子界型、數組型、集合型、記錄型、對象型等。

2.1.9.1 枚舉類型

一個枚舉型的說明列出了全部這樣的類型能夠包括的值:

type
Tdays=( Sunday ,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);

能夠定義上述枚舉類型的變量:

var
DayOfWeek:TDays;

在枚舉型中,括號中的每個值都有一個由說明它的位置決定的整形值。比如Sunday有整形值0,Monday有整形值1等。您能夠把DayOfWeek說明為一個整形變量,并將一星期的每一天賦一個整形值以達到同樣的效果,但用枚舉型會使得程序可讀性好,編寫easy。當您在枚舉型中列出值時,您同一時候說明了這個值是一個標識符。比如您的程序中假設已經含有TDays類型且說明了DayOfWeeks變量,則程序中便不能使用Monday變量,由于它已經被說明為標識符了。

2.1.9.2 子界類型

子界型是下列這些類型中某范圍內的值:整形、布爾量、字符型或枚舉型。在您想限制一個變量的取值范圍時,子界型是非常實用的。

type
Thours = 0..23;
TValidLetter = 'A' .. 'F';
TDays = ( Sunday ,Monday,Tuesday,Wednesday,Thursday,
Friday,Saturday); {枚舉型}
TWorkDay = Monday..Friday; {一個TDays型的子界}

子界型限定了變量的可能取值范圍。當范圍檢查打開時,(在庫單元的Implementation后面有{$R*.DFM}字樣表示范圍檢查打開,否則您能夠在Options|Project|Complier Options中選擇Range Cheking來打開范圍檢查),假設變量取到子界以外的值,會出現一個范圍檢查錯誤。

2.1.9.3 數組類型

數組是某種數據類型的有序組合,當中每個元素的值由其相對位置來指定,您能夠在數組的某個位置上放置數據,并在須要時使用這些數據。以下的類型說明了一個Double型的數組變量:

var
Check : array [1..10] of Double;

它表示Check指向一個含有10個Double型元素的數據串列,代表每個元素的是1到10之間的數字,稱為索引。數組的每一項由數組名稱加上[]中的索引來表示。Check包括10個變量,Check[1]表示第一個變量。您也能夠把數組定義成類型:

type
TCheck = array[1..10] of Double;

則變量說明改為:

var
Check :TCheck;

您能夠通過給數組賦值等方法來使用數組。以下的語句將0.0賦給Check數組中的全部元素:

for J := 1 to 10 do
Check[J] := 0.0;

數組也能夠是多維的,以下的類型定義了一個20行、20列的數組。

type
Ttable = array[1..20,1..20] of Double;
var
table1:TTable;

想將這一表格的全部數據初始化為0.0,您能夠使用for循環:

var
Col,Row:Integer;

for Col :=1 to 20 do
for Row := 1 to 20 do
Table1[Col,Row] := 0.0;

2.1.9.4 字符串類型

字符串類型事實上是一個一維的字符數組。當您說明一個字符串型的變量時,您應當指明這個字符串的大小,以下是說明字符串類型的樣例:

type
MyString: string[15];
var
MyName: MyString;

則變量MyName被說明成為最多能夠包括15個字符。假設您沒有說明字符串的大小,Delphi會覺得字符串包括最大值255個字符。給字符串賦值能夠直接使用單引號括起的字串賦值:

MyName := 'Frank.Smith';

或MyName := '張明';

由于MyName是一個能夠包括15個字符的MyString型變量,上文的兩個的變量都是有效的,一個漢字能夠視作兩個字符。當您給字符串型變量賦的值多于定義數值時,比如將MyName賦為‘FrankSmith.Franklin’,則Delphi僅僅會接受前15個字符‘FrankSmith.Fran’。在內存中,字符串通常占用比所說明的大小多一個字節的空間,由于第一個位置是一個包括這個數組大小的字節。您能夠使用索引值來訪問字符串的字符,MyName[1]能夠得到MyName的第一個字符'F'。

您能夠使用Delphi豐富的運算符、過程和函數來處理字符串型的變量和屬性。以下介紹幾個經常使用的運算符和Delphi過程或函數:

Concat和(+)功能同樣,都能夠將多個字符串組合在一起,建立一個較大的字符串;Copy會返回一個字符串中的子字符串;Delete在一個字符串中從一個指定位置起刪除一定數目的字符;Insert在一個字符串中插入一個字符串;Length返回字符串的長度;Pos返回一個子字符串在一個字符串中的位置,即索引值。

2.1.9.5 集合類型

集合類型是一群同樣類型元素的組合,這些類型必須是有限類型如整形、布爾型、字符型、枚舉型和子界型。在檢查一個值是否屬于一個特定集合時,集合類型非常實用。以下的例程能夠說明集合類型的使用方法:

在窗口上增加一個編輯框和一個button,清除編輯框中的文字,在其上加上Caption為“輸入元音”的標簽Label,并在編輯框的下方增加一個空的標簽,將button的Default屬性改為True,建立button的事件處理步驟例如以下:

procedure TForm1.Button1Click(Sender:TObject);
type
Tvowels=set of Char;
var
Vowels:TVowels;
begin
Vowels := ['a','e','i','o','u'];
if Edit1.Text[1] in Vowels then
Lable2.Caption := '是元音';
else
Lable2.Caption := '請再試';
end;

運行這個程序,在編輯框中輸入字母,表達式Edit1.Text[1] in Vowels的結果是布爾型的,in是運算符,用來推斷字母是否存在于集合中。輸入的判別結果會顯示在編輯框的下方。以上就用到了集合類型TVowels。

2.1.9.6 記錄類型

記錄是您的程序能夠成組訪問的一群數據的集合。以下的例程說明了一個記錄類型的使用方法:

type
TEmployee=record
Name : string[20];
YearHired:1990..2000;
Salsry: Double;
Position: string[20];
end;

記錄包括能夠保存數據的域,每個域有一個數據類型。上文的記錄TEmployee類型就含有四個域。您能夠用以下的方式說明記錄型的變量:

var
NewEmployee,PromotedEmployee:TEmployee;

用例如以下的方法能夠訪問記錄的單域:

NewEmployee.Salary := 1000;

編寫例如以下的語句能夠給整個記錄賦值:

with PromotedEmployee do
begin
Name :='';
YearHired := 1993;
Salary := 2000.00
Position := 'editor';
end;

您的程序能夠將記錄當成單一實體來操作:

PromptEmployee := NewEmployee;

以上介紹了用戶經常使用的自己定義類型。在Delphi的編程中,對象是非常重要的用戶自己定義數據類型。象記錄一樣,對象是結構化的數據類型,它包括數據的域(Field),也包括作為方法的過程和函數。在Delphi中,當您向窗口中增加一個部件,也就是向窗口對象中增加了一個域;每個部件也是對象,每當您建立一個事件處理過程使得部件能夠響應一個事件時,您即自己主動地在窗口中增加了一個方法。在本章第2節中,將具體講述Delphi面向對象編程的方法和技巧。

2.1.10 Object Pascal的庫單元Unit

Units是常量、變量、數據類型、過程和函數的集合,并且能夠被多個應用程序所共享。Delphi已經擁有很多提前定義的程序庫單元可供您建立您的程序庫單元使用。Delphi的Visual Component Library由多個程序庫單元組成,它們說明了對象、部件以供您的應用程序用來設計用戶界面。比如,當您在窗口中增加一個Check Box時,Delphi自己主動在您的程序庫單元中增加了Stdctrls庫單元,由于TCheckBox部件是在StdCtrls庫單元中說明的。

當您設計您的窗口時,Delphi自己主動建立一個和您的窗口有關的庫單元。您的庫單元不必都和窗口有關,也能夠使用提前定義的僅僅包括數學運算函數的庫單元,或是自行編寫數學函數庫單元。在一個庫單元中全部的說明都相互有關系,比如,CDialogs程序庫單元包括了在您的應用程序中使用的普通對話框的全部說明。

2.1.10.1 Object Pascal程序庫單元的結構

無論一個庫單元是否和一個窗口有關,庫單元的結構都是同樣的。其結構例如以下:

unit
interface
uses
{公有說明}
implementation
uses
{私有說明}
{過程和函數的運行部分}
initialization {選擇性的}
{選擇性的初始化程序}
end.

2.1.10.2 程序庫單元的接口部分

interface是庫單元的接口部分,它決定了本庫單元對其它不論什么庫單元或程序的可見(可訪問)部分。您能夠在接口部分說明變量、常量、數據類型、過程和函數等等。Delphi在您設計窗口的庫單元中,將窗口數據類型、窗口變量和事件處理過程都說明在這一部分。

interface標志庫單元接口部分的開始。在interface中的說明對要使用這些說明的其它庫單元或應用程序是可見的。一個庫單元能夠使用其它Unit的說明,僅僅須要在uses子句中指明那些庫單元就可以。比如,您在庫單元A中編敲代碼代碼,且您想調用UnitB于interface部分說明的程序。您能夠把庫單元B的名稱增加到A的interface部分的uses子句中,則不論什么A中的程序都能夠調用B中說明的程序。并且,假設B中interface部分的uses子句中出現C庫單元,盡管A中未曾出現C,A同樣能夠調用B、C庫單元在interface中說明的程序。但假設B出如今A的interface部分的uses子句中,那么庫單元A便不能出如今B的interface的uses子句中。由于這樣會產生對庫單元的循環訪問。當試圖編譯時,會產生出現錯誤信息。

2.1.10.3 程序庫單元的實現部分

實現部分implementation中包括interface中說明的過程、函數、事件處理過程的具體實現程序代碼。這一部分能夠有自己的額外說明,但這些說明是私有的,外部程序不能調用這些說明。在interface中說明的函數實體必須在implementation部分出現,能夠使用標題簡寫:僅僅輸入procedure或function保留字,后面跟過程或函數的名稱就可以,其后則是程序的實現部分了。假設您在implementation部分說明不論什么常式,其標題并未出如今interface部分,則必須寫全其標題部分。

在implementation部分的uses子句中指定的庫單元,僅僅供給本庫單元的程序使用其interface中說明的程序。其它使用本庫單元的庫單元,不能訪問這些在implementation的udes子句中庫單元的說明,由于在implementation后進行的庫單元包括是私有的。所以上例中,假設C出如今B的implementation部分,則A不能使用C的公有部分,除非C出如今A的uses子句中。在implementation中出現的循環訪問是Delphi所同意的,假設A的implemetation的uses子句中出現B,則B的implementation部分也能夠出現A。

2.1.10.4 程序庫單元的初始化部分

初始化當前庫單元所使用的數據,或是通過interface部分將數據提供給其它應用程序、庫單元使用時,您能夠在庫單元中增加一個initialization部分,在庫單元的end前加上您的初始化語句。當一個應用程序使用一個庫單元時,在庫單元中的initialization部分會先于其它的代碼運行。假設一個應用程序使用了多個庫單元,則每個庫單元的初始化部分都會在全部的程序代碼前運行。

2.1.10.5 使用Delphi的可視化部件及其庫單元

當您在窗口中增加可視化部件時,假設該部件在可視化部件庫中,Delphi會在您的庫單元的interface部分的uses子句中自己主動加上須要使用的庫單元名稱。但有些對象在Delphi的環境中并沒有可視化部件存在,比如,您想在庫單元中增加一個提前定義的信息框,則您必須把MsgDlg庫單元增加您的uses子句中。假設您要使用TPrinter對象的話,必須將Printer庫單元增加uses子句中。在在線幫助中能夠查到對象所屬的提前定義庫單元。

要使用在其它庫單元中說明的函數,應在函數的前面加上這一庫單元的名稱,并用‘.’號隔開。比如,要在Unit2中使用Unit1中說明的Calculate函數,應使用以下的方法:

Number := Unit1.Calculate(10);

您能夠在不論什么標識符如屬性、常量、變量、數據類型、函數等之前加上庫單元的名稱。您能夠在自由地在不論什么Delphi庫單元中增加程序代碼,但不要改變由Delphi生成的程序。

2.1.10.6 建立與窗口無關的新庫單元

假設您想在project中建立一個和不論什么窗口無關的新庫單元,能夠現選用File|New Unit。這時一個新的庫單元增加了project,新庫單元的代碼例如以下:

unit Unit2;
interface
implementation
end.

Delphi將依據您的project中的文件數目為您的庫單元選擇名稱,您能夠在程序骨架間增加您的程序代碼。

當編譯您的project時,這個新增加的庫單元會被編譯為一個具有.DCU后綴的文件。這個新生成的文件是鏈接到project的可運行文件上的機器代碼。

2.1.10.7 將庫單元增加project

將庫單元增加project是比較簡單的。無論是您自己建立的庫單元還是Delphi建立的與窗口有關的庫單元,假設已經完畢,則先打開您想增加庫單元的project(能夠用Open Project打開project);再選用File|Open File,然后選擇您想增加的源程序(.PAS文件),并選擇OK就可以。則庫單元被增加到應用程序中。

2.2 用Delphi的對象進行編程

Delphi是基于面向對象編程的先進開發環境。面向對象的程序設計(OOP)是結構化語言的自然延伸。OOP的先進編程方法,會產生一個清晰而又easy擴展及維護的程序。一旦您為您的程序建立了一個對象,您和其它的程序猿能夠在其它的程序中使用這個對象,全然不必又一次編制繁復的代碼。對象的反復使用能夠大大地節省開發時間,切實地提高您和其它人的工作效率。

2.2.1 什么是對象

一個對象是一個數據類型。對象就象記錄一樣,是一種數據結構。按最簡單的理解,我們能夠將對象理解成一個記錄。但實際上,對象是一種定義不確切的術語,它經常使用來定義抽象的事務,是構成應用程序的項目,其內涵遠比記錄要豐富。在本書中,對象可被理解為可視化部件如button、標簽、表等。

了解對象,最關鍵的是掌握對象的特性。一個對象,其最突出的特征有三個:封裝性、繼承性、多態性。

2.2.1.1 對象的封裝性

對對象最基本的理解是把數據和代碼組合在同一個結構中,這就是對象的封裝特性。將對象的數據域封閉在對象的內部,使得外部程序必需并且僅僅能使用正確的方法才干對要讀寫的數據域進行訪問。封裝性意味著數據和代碼一起出如今同一結構中,假設須要的話,能夠在數據周圍砌上“圍墻”,僅僅實用對象類的方法才干在“圍墻”上打開缺口。

2.2.1.2 對象的繼承性

繼承性的含義直接并且顯然。它是指把一個新的對象定義成為已存在對象的后代;新對象繼承了舊類的一切東西。在往新對象中增加不論什么新內容曾經,父類的每個字段和方法都已存在于子類中,父類是創建子類的基石。

2.2.1.3 對象的多態性

多態性是在對象體系中把設想和實現分開的手段。假設說繼承性是系統的布局手段,多態性就是其功能實現的方法。多態性意味著某種概括的動作能夠由特定的方式來實現,這取決于運行該動作的對象。多態性同意以相似的方式處理類體系中相似的對象。依據特定的任務,一個應用程序被分解成很多對象,多態性把高級設計處理的設想如新對象的創建、對象在屏幕上的重顯、程序運行的其它抽象描寫敘述等,留給知道該怎樣完美的處理它們的對象去實現。

2.2.1.4 通過Delphi實例了解對象

讓我們結合Delphi的實例討論對象的概念:

當您要建立一個新project時,Delphi 將顯示一個窗口作為設計的基礎。在程序編輯器中,Delphi將這個窗口說明為一個新的對象類型,并同一時候在與窗口相關聯的庫單元中生成了創建這個新窗口對象的程序代碼。

unit Unit1;
interface
uses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs;
type
TForm1 = class(TForm) {窗口的類型說明開始}
private
{ Private declarations }
public
{ Public declarations }
end; {窗口的類型說明結束}
var
Form1: TForm1; {說明一個窗口變量}
implementation
{$R *.DFM}
end.

新的窗口類型是TForm1,它是從TForm繼承下來的一個對象。它具有對象的特征:含有域或方法。由于您未給窗口增加不論什么部件,所以它僅僅有從TForm類中繼承的域和方法,在窗口對象的類型說明中,您是看不到不論什么域、方法的說明的。Form1稱為TForm1類型的實例(instance)。您能夠說明多個對象類型的實例,比如在多文檔界面(MDI)中管理多個子窗口時就要進行這樣的說明。每個實例都有自己的說明,但全部的實例卻共用同樣的代碼。

假設您向窗口中增加了一個button部件,并對這個button建立了一個OnClick事件處理過程。再查看Unit1的源程序,會發現TForm1的類型說明部分例如以下:

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

如今TForm1對象有了一個名為Button1的域:它是您在窗口中增加的button。TButton是一個對象類型,Button1是Tbutton的一個實例。它被TForm1對象所包括,作為它的數據域。每當您在窗口中增加一個部件時,部件的名稱就會作為TFom1的域增加到類型說明中來。在Delphi中,您所編寫的事件處理過程都是窗口對象的方法。每當您建立一個事件處理過程,就會在窗口的對象類型中說明一個方法。

當您使用Object Inspector來改變對象(部件)的名稱時,這個名稱的改變會反映到程序中。比如,在Object Inspector中將Form1的Name屬性命名為ColorBox,您會發如今類型說明部分,會將前文的TForm1改為:

TColorBox=class(TForm);

并且在變量說明部分,會說明ColorBox為TColorBox類型的變量,由Delphi自己主動產生的事件處理過程名稱會自己主動改為TColorBox.Button1Click;但您自行編寫的實現部分的代碼卻不會被自己主動改動。因此,假設您在改變Name屬性前編寫了程序,則您必須將事件處理過程中的對象名稱進行改變。所以,原先的Form1.Color要改為ColorBox.Color。

2.2.2 從一個對象中繼承數據和方法

前面的TForm1類型是非常簡單的,由于它僅僅含有域Button1和方法Button1Click。可是在這個窗口上,您能夠改變窗口的大小、增加或刪除窗口的最大最小化button,或設置這個窗口為MDI界面。對于一個僅僅包括一個域和方法的對象來講,您并沒有看到顯式的支持程序。在窗口上單擊鼠標或用Object Inspector的上端的Object Selector選中Form1對象,按動F1查閱它的在線幫助,您會在Properties和Method中找到它的繼承到的全部屬性和方法。這些是在TForm類型中說明的,TForm1是TForm的子類,直接繼承了它全部的域、方法、屬性和事件。比如窗口的顏色屬性Color就是在TForm中說明的。當您在project中增加一個新窗口時,就等于增加了一個基本模型。通過不斷地在窗口中增加部件,您就自行定義了一個新的窗口。要自己定義不論什么對象,您都將從已經存在的對象中繼承域和方法,建立一個該種對象的子類。比如對象TForm1就被說明為對象TForm的子類,擁有一個窗口部件的基本屬性或方法。僅僅有當您在窗口中增加了部件或編寫了事件處理過程時,Form1才成為您自己的類型。

一個比較特殊的對象是從一個范圍較廣或較一般的對象中繼承下來的,它是這個特別對象的祖先,這個對象則稱為祖先的后代。一個對象僅僅能有一個直接的祖先,可是它能夠有很多后代。TForm是TForm1類型的祖先,全部的窗口對象都是TForm的后代。

用F1查閱窗口的在線幫助時,您會發現TForm被稱為component(部件)。這是由于全部的部件都是對象。

在這個結構中全部的部件都是對象。部件類型TComponent從TObject類型中繼承數據和程序代碼,并具有額外的能夠用作特殊用途的屬性、方法、事件,所以部件能夠直接和用戶打交道,記錄它的狀態并存貯到文件里等等??刂祁愋蚑Control從TComponent中繼承而來,又增加了新的功能,如它能夠顯示一個對象。在上圖中,盡管TCheckBox不是直接由TObject繼承來的,可是它仍然有不論什么對象所擁有的屬性,由于在VCL結構中,TCheckBox終究還是從TObject 中繼承了全部功能的特殊對象,但它還有些自行定義的獨到的功能,如能夠選擇記錄狀態等。

2.2.3 對象的范圍

2.2.3.1 關于對象的范圍

一個對象的范圍決定了它的數據域、屬性值、方法的活動范圍和訪問范圍。在一個對象的說明部分說明的數據域、屬性值、方法都僅僅是在這個對象的范圍中,并且僅僅有這個對象和它的后代才干擁有它們。盡管這些方法的實際程序代碼可能是在這個對象之外的程序庫單元中,但這些方法仍然在這個對象的范圍內,由于它們是在這個對象的說明部分中說明的。

當您在一個對象的事件處理過程中編敲代碼代碼來訪問這個對象的屬性值、方法或域時,您不須要在這些標識符之前加上這個對象變量的名稱。比如,假設您在一個新窗口上增加一個button和一個編輯框,并為這個button編寫OnClick事件處理過程:

procedure TForm1.Button1Click(Sender:Tobject);
begin
Color :=clFuchsia;
Edit1.Color :=clLime;
end;

當中的第一行語句是為整個窗口Form1著色。您也能夠編寫例如以下:

Form1.Color :=clFuchsia;

但您能夠不必加上Form1.,由于Button1Click方法是在TForm1對象的范圍里。當您在一個對象的范圍中時,您能夠省略全部這個對象中的屬性值、方法、域之前的對象標識符??墒钱斈帉懙诙€語句改變編輯框的底色時,由于此時您想訪問的是TEdit1對象的Color屬性,而不是TForm1類型的,所以您須要通過在屬性前面加上編輯框的名稱來指明Color屬性值的范圍。假設不指明,Delphi會象第一個語句一樣,將窗口的顏色變成綠色。由于Edit1部件是在窗口中的,它是窗口的一個數據域,所以您同樣不必指明其從屬關系。

假設Edit1是在其它窗口中,那么您須要在編輯框之前加上這個船體對象的名稱了。比如,假設Edit1是在Form2之中,那它是Form2說明的一個數據域,并位于Form2的范圍中,那么您須要將第二句改為:

Form2.Edit1.Color := clLime;

并且須要把Unit2增加Unit1的uses子句中。

一個對象的范圍擴展到這個對象的全部后代。TForm的全部屬性值、方法和事件都在TForm1的范圍中,由于TForm1是TForm的后代。您的應用程序不能說明和祖先的數據域重名的類型、變量等。假設Delphi顯示了一個標識符被反復定義的信息,就有可能是一個數據域和其祖先對象(比如TForm)的一個數據域有了同樣的名稱。能夠嘗試改變這個標識符的名稱。

2.2.3.2 重載一個方法

您能夠重載(Override)一個方法。通過在后代對象中說明一個與祖先對象重名的方法,就能夠重載一個方法。假設想使這種方法在后代對象中作和祖先對象中一樣的工作可是使用不同的方式時,您就能夠重載這種方法。Delphi不推薦您經常重載方法,除非您想建立一個新的部件。重載一個方法,Delphi編譯器不會給出錯誤或警告提示信息。

2.2.4 對象公有域和私有域的說明

當使用Delphi的環境來建立應用程序時,您能夠在一個TForm的后代對象中增加數據域和方法,也能夠通過直接改動對象類型說明的方法來為一個對象加上域和方法,而不是把一個部件增加窗口或事件處理過程中。

您能夠在對象的Public或Private部分增加新的數據域和方法。Public和Private是Object Pascal的保留字。當您在project中增加新的窗口時,Delphi開始建立這個新窗口對象。每個新的對象都包括public和private指示,以便您在代碼中增加數據域和方法。在public部分中說明其它庫單元中對象的方法也能夠訪問的數據域或方法。在private部分的說明有訪問的限制。假設您在private中說明域和方法,那么它在說明這個對象的庫單元外是不透明的,并且不能被訪問。private中能夠說明僅僅能被本庫單元方法訪問的數據域和本庫單元對象訪問的方法。過程或函數的程序代碼能夠放在庫單元的implementation部分。

2.2.5 訪問對象的域和方法

當您想要改變一個窗口對象的一個域的某個屬性,或是調用它的一個方法時,您必須在這個屬性名稱或調用方法之前加上這個對象的名稱。比如,假設您的窗口上有一個編輯框部件,而您須要在運行中改變它的Text屬性,須要編寫下列的代碼:

Edit1.Text := 'Welcome to Delphi';

同樣,清除編輯框部件中選中的文本,能夠調用TEdit部件的對應方法:

Edit1.ClearSelection;

假設您想改變一個窗口對象中一個對象域的多個屬性或調用多個方法時,使用with語句能夠簡化您的程序。with語句在對象中能夠和在記錄中一樣方便地使用。以下的事件處理過程在響應OnClick事件時,會對一個列表框作多個調整:

procedure TForm1.Button1Click(Sender:TObject);
begin
ListBox1.Clear;
ListBox1.MultiSelect :=True;
ListBox1.Item.Add('One');
ListBox1.Item.Add('Two');
ListBox1.Item.Add('Three');
ListBox1.Sorted :=Ture;
ListBox1.FontStyle :=[fsBold];
ListBox1.Font.Color :=clPurple;
ListBox1.Font.Name :='Times New Roman';
ListBox1.ScaleBy(125,100);
end;

假設使用了With語句,則程序例如以下:

procedure TForm1.Button1Click(Sender:TObject);
begin
with (ListBox1) do
begin
Clear;
MultiSelect :=True;
Item.Add('One');
Item.Add('Two');
Item.Add('Three');
Sorted :=Ture;
FontStyle :=[fsBold];
Font.Color :=clPurple;
Font.Name :='Times New Roman';
ScaleBy(125,100);
end;
end;

使用with語句,您不必在每個屬性或方法前加上ListBox1標識符,在With語句之內,全部的屬性或調用方法對于ListBox這個對象而言都是在它的范圍內的。

2.2.6 對象變量的賦值

假設兩個變量類型同樣或兼容,您能夠把當中一個對象變量賦給還有一個對象變量。比如,對象TForm1和TForm2都是從TForm繼承下來的類型,并且Form1和Form2已被說明過,那么您能夠把Form1賦給Form2:

Form2 :=Form1;

僅僅要賦值的對象變量是被賦值的對象變量的祖先類型,您就能夠將一個對象變量賦給還有一個對象變量。比如,以下是一個TDataForm的類型說明,在變量說明部分一共說明了兩個變量:AForm和DataForm。

type
TDataForm = class(TForm)
Button1:TButton;
Edit1:TEdit;
DataGrid1:TDataGrid;
Database1:TDatabase;
TableSet1:TTableSet;
VisibleSession1:TVisibleSession;
private
{私有域說明}
public
{公有域說明}
end;
var
AForm:TForm;
DataForm:TDataForm;

由于TDataForm是TForm類型的后代,所以Dataform是AForm的后代,因此以下的賦值語句是合法的:

AForm :=DataForm;

這一點在Delphi中是極為重要的。讓我們來看一下應用程序調用事件處理過程的過程,以下是一個button部件的OnClick事件處理過程:

procedure TForm1.Button1Click(Sender:TObject);
begin
end;

您能夠看到TObject類在Delphi的Visual Component Library的頂部,這就意味著全部的Delphi對象都是TObject的后代。由于Sender是TObject類型,所以不論什么對象都能夠賦值給它。盡管您沒有看見賦值的程序代碼,但事實上發生事件的部件或控制部件已經賦給Sender了,這就是說Sender的值是響應發生事件的部件或控制部件的。

您能夠使用保留字is來測試Sender以便找到調用這個事件處理過程的部件或控制部件的類型。Delphi中的一個顯示drag-and-drop的DRAGDROP.DPRproject。載入它,能夠查閱到DROPFONT.PAS庫單元的代碼,在Memo1DragOver方法中檢查了一個對象變量的類型。在這樣的情形下,參數是Source而不是Sender。

procrdure TForm1.Memo1DragOver(SenderSource:TObject;X,Y:integer;
State:TDragState;var Accept:Boolean);
begin
Accept :=Source is TLabel;
end;

Source參數也是TObject類型,Source被賦值為那個被拖曳的對象。用Memo1DragOver方法的目的是確保僅僅有標簽能夠被拖曳。Accept是布爾型參數,假設Accept為True,那么用戶選擇的部件能夠被拖曳;反之當Accept的值為False時,用戶就不能夠拖曳選擇控制部件。is保留字檢查Source是否TLabel的類型,所以Accept僅僅有在用戶拖曳一個標簽時才為真,并作為變參輸出到函數之外。

以下的drag-and-drop展示的Memo1DragDrop事件處理過程中也使用了Source參數。這種方法是為了把Memo部件的字型改變成和放入這個備注控制部件的標簽一樣的字型:

procedure TForm1.Memo1DragDrop(SenderSource:TObject;
X,Y:Integer);
begin
Memo1.Font := (Source as TLabel).Font;
end;

當您在這個事件處理過程中編寫賦值語句時,開發者并不知道用戶會放入哪一個標簽,僅僅有通過參考這個標簽的名稱(Source as TLabel)用戶才干知道,并把標簽類型賦給Memo1.TFont。Source包括了用戶拖放控制部件的名稱,僅僅有當Source是一個標簽時,這個事件處理過程才同意這個賦值發生。

2.2.7 建立非可視化對象

您在Delphi中使用的大部分對象都是您在設計和運行期間能夠看見的部件,比如編輯框、button等;一些部件,如通用對話框(Common dialog box)等,在設計時看不見,而在運行時能夠看見;另外有些部件,比如計時器(Timer)、數據源(Data Source)部件等,在程序的運行期間沒有不論什么可視化的顯示,但您卻能夠在您的應用程序中使用它們。

2.2.7.1說明一個非可視化對象

以下,通過一個簡單的樣例講述怎樣建立自己的非可視化對象:

您能夠用例如以下的方法,建立一個自己的TEmployee非可視化對象:

type
Temployee = class(TObject);
Name := String[25];
Title := String[25];
HourlyPayRate : Double;
function CalculatePayAmount:Double;
end;

在這樣的情況下,TEmployee從TObject繼承下來,且包括三個域和一個方法。把您建立的類型說明放在庫單元中的說明部分,并和窗口說明放在一起。在這個程序庫單元的變量說明部分,說明一個新類型的變量:

var
Employee : TEmployee;

2.2.7.2用Create方法建立對象實例

TEmployee僅僅是一個對象類型。除非通過一個構造函數的調用從而被實例取代或創建,否則一個對象并不存儲在內存中。構造函數是一個方法,它為新對象配置內存并且指向這個新的對象。這個新的對象也被稱為這個對象類型的一個實例。

建立一個對象的實例,須要調用Create方法,然后構造函數把這個實例賦給一個變量。假設您想說明一個TEmployee類型的實例,在您訪問這個對象的不論什么域之前,您的程序代碼必須調用Create。

Employee := TEmployee.Create;

Create方法并沒有在TEmployee類型中說明,它繼承自TObject類型。由于TEmployee是TObject的子類,所以它能夠調用Create方法而創建一個TEmployee實例。然后把它賦給Employee變量。在創建了一個這樣的對象后,您就能夠象使用其它的Delphi對象一樣訪問Employee對象了。

2.2.7.3 撤銷對象

當您使用完對象后,您應該及時撤銷它,以便把這個對象占用的內存釋放出來。您能夠通過調用一個注銷方法來撤銷您的對象,它會釋放分配給這個對象的內存。

Delphi的注銷方法有兩個:Destroy和Free。Delphi建議使用Free,由于它比Destroy更為安全,同一時候調用Free會生成效率更高的代碼。

您能夠用下列的語句釋放用完的Employee對象:

Employee.Free;

和Create方法一樣,Free方法也是TEmployee從TObject中繼承過來的。把您的注銷放在try…finally程序模塊的finally部分,而把對象的程序代碼放在try部分是編程的好習慣。這樣,即使您的程序代碼在使用對象時發生了異常事件,也會確保您為這個對象分配的內存會被釋放。關于異常處理和try…finally程序模塊的信息以及建立非可視化對象的樣例,在后文中還將細致講述。

hook實現dll注入具體解釋

baidu

須要一個用來注入的dll(inject.dll)及一個調用程序(caller.exe)

流程:

caller.exe
procedure TestHook;
var pwnd,hChild, hwndInject :hwnd;
msg:tmsg;
begin
//通過窗口標題用FindWindow找到要注入的程序的主窗口句柄pwnd
pwnd := findwindow('Progman',nil);
//用FindwindowEx(hMain,0,nil,nil)找到要處理的子窗口句柄hChild
hChild := findWindowEx(pwnd,0,nil,nil);
//用getwindowThreadProcessid(hChild,nil)找到要注入的線程
dwThreadID := getwindowThreadProcessid(hChild,nil);
//調用 inject.dll的SetInjectHook方法
SetInjectHook(dwThreadID);
//等待消息返回
getmessage(msg,0,0,0);
//找到注入的窗口
hwndInject:= findwindow(nil,'InjectForm');
//發送控制消息,將目標窗口的句柄作為wparam,控制參數以lparam傳入
sendMessage( hwndInject, wm_app,hChild,integer(true));
//關閉注入的窗口
sendMessage( hwndInject,wm_close,0,0);
//等待窗口關閉
sleep(500);
//檢查是否成功關閉
assert(not iswindow( hwndInject));
//去掉掛鉤
setDipsHook(0);
end;
//以下說明 Inject.dll的SetInjectHook的具體操作
在全局定義以下變量
var
g_hhook :Hhook=0;
g_dwThreadidInject :dword=0;
g_hInjectfrm:hwnd;
function SetInjectHook(dwThreadid:DWORD):boolean;
begin
result := false;
//假設線程標志為0則用于去掉鉤子,否則進行動態庫注入
if dwThreadid<>0 then
begin
assert(g_hhook=0);
//保存當前線程的ID到 g_dwThreadidInject
g_dwThreadidInject := getCurrentThreadid;
//下一個GetMessage的鉤子到目標線程
//GetMsgProc是在以下定義的一個函數,在第一次調用時將自己定義的form在目標線程中創建出來
//這樣就能通過這個自己定義的form對目標線程進行進程內控制了
g_hhook := setWindowsHookEx(wh_getMessage,GetMsgProc,hInstance,dwThreadid);
result := g_hhook <> null;
if result then
//發一個空的信息以便于立即創建這個自己定義form
result := postThreadMessage(dwThreadid, wm_Null,0,0);
//等待半秒鐘,以保證調用者能夠找到這個剛創建的form
sleep(500);
end else
begin
assert(g_hhook<>0);
//去掉鉤子
result := unHookWindowsHookEx(g_hhook);
g_Hhook := 0;
end;
end;
//定義一個全局的是否第一個消息的標志
var
fFirstTime:boolean = true;
//這個函數用于在收到第一個消息時創建自己定義窗口,以便于遠程控制
function GetMsgProc(code: Integer; wparam: WPARAM; lparam: LPARAM): LRESULT; stdcall;
begin
//假設是第一次
if fFirstTime then
begin
fFirstTime := false;
//創建窗口
InjectFrm := TinjectFrm.create(nil);
//保存窗口句柄
g_hInjectfrm := InjectFrm.handle;
end;
//調用默認處理,這一句可不能忘記
result := callNexthookEx(g_hhook,code,wparam,lparam);
end;

COM 原理與應用》學習筆記 - 第一部分 COM原理

baidu

⊙ 第一章 概述

===================================================

COM 是什么

---------------------------------------------------

COM 是由 Microsoft 提出的組件標準,它不僅定義了組件程序之間進行交互的標準,并且也提供了組件程序運行所需的環境。在 COM 標準中,一個組件程序也被稱為一個模塊,它能夠是一個動態鏈接庫,被稱為進程內組件(in-process component);也能夠是一個可運行程序(即 EXE 程序),被稱作進程外組件(out-of-process component)。一個組件程序能夠包括一個或多個組件對象,由于 COM 是以對象為基本單元的模型,所以在程序與程序之間進行通信時,通信的兩方應該是組件對象,也叫做 COM 對象,而組件程序(或稱作 COM 程序)是提供 COM 對象的代碼載體。

COM 對象不同于一般面向對象語言(如 C++ 語言)中的對象概念,COM 對象是建立在二進制可運行代碼級的基礎上,而 C++ 等語言中的對象是建立在源碼級基礎上的,因此 COM 對象是語言無關的。這一特性使用不同編程語言開發的組件對象進行交互成為可能。

---------------------------------------------------

COM 對象與接口

---------------------------------------------------

相似于 C++ 中對象的概念,對象是某個類(class)的一個實例;而類則是一組相關的數據和功能組合在一起的一個定義。使用對象的應用(或還有一個對象)稱為客戶,有時也稱為對象的用戶。

接口是一組邏輯上相關的函數集合,其函數也被稱為接口成員函數。依照習慣,接口名常是以“I”為前綴。對象通過接口成員函數為客戶提供各種形式的服務。

在 COM 模型中,對象本身對于客戶來說是不可見的,客戶請求服務時,僅僅能通過接口進行。每個接口都由一個 128 位的全局唯一標識符(GUID,Global Unique Identifier)來標識。客戶通過 GUID 來獲得接口的指針,再通過接口指針,客戶就能夠調用其對應的成員函數。

COM 原理與應用》學習筆記 - 第一部分 COM原理

baidu

與接口相似,每個組件也用一個 128 位 GUID 來標識,稱為 CLSID(class identifer,類標識符或類 ID),用 CLSID 標識對象能夠保證(概率意義上)在全球范圍內的唯一性。實際上,客戶成功地創建對象后,它得到的是一個指向對象某個接口的指針,由于 COM 對象至少實現一個接口(沒有接口的 COM 對象是沒有意義的),所以客戶就能夠調用該接口提供的全部服務。依據 COM 規范,一個 COM 對象假設實現了多個接口,則能夠從某個接口得到該對象的隨意其它接口。從這個過程我們也能夠看出,客戶與 COM 對象僅僅通過接口打交道,對象對于客戶來說僅僅是一組接口。

---------------------------------------------------

COM 進程模型

---------------------------------------------------

COM 所提供的服務組件對象在實現時有兩種進程模型:進程內對象和進程外對象。假設是進程內對象,則它在客戶進程空間中運行;假設是進程外對象,則它運行在同機器上的還有一個進程空間或者在遠程機器的空間。

進程內服務程序:

服務程序被載入到客戶的進程空間,在 Windows 環境下,通常服務程序的代碼以動態連接庫(DLL)的形式實現。

本地服務程序:

服務程序與客戶程序運行在同一臺機器上,服務程序是一個獨立的應用程序,通常它是一個 EXE 文件。

遠程服務程序:

服務程序運行在與客戶不同的機器上,它既能夠是一個 DLL 模塊,也能夠是一個 EXE 文件。假設遠程服務程序是以 DLL 形式實現的話,則遠程機器會創建一個代理進程。

盡管 COM 對象有不同的進程模型,但這樣的差別對于客戶程序來說是透明的,因此客戶程序在使用組件對象時能夠無論這樣的差別的存在,僅僅要遵照 COM 規范就可以。然而,在實現 COM 對象時,還是應該謹慎選擇進程模型。進程內模型的長處是效率高,但組件不穩定會引起客戶進程崩潰,因此組件可能會危及客戶;(savetime 注:這里有點問題,假設組件不穩定,進程外模型也同樣會出問題,可能是由于進程內組件和客戶同處一個地址空間,出現沖突的可能性比較大?)進程外模型的長處是穩定性好,組件進程不會危及客戶程序,一個組件進程能夠為多個客戶進程提供服務,但進程外組件開銷大,并且調用效率相對低一點。

COM 原理與應用》學習筆記 - 第一部分 COM原理

baidu

---------------------------------------------------

COM 可重用性

---------------------------------------------------

由于 COM 標準是建立在二進制代碼級的,因此 COM 對象的可重用性與一般的面向對象語言如 C++ 中對象的重用過程不同。對于 COM 對象的客戶程序來說,它僅僅是通過接口使用對象提供的服務,它并不知道對象內部的實現過程,因此,組件對象的重用性可建立在組件對象的行為方式上,而不是具體實現上,這是建立重用的關鍵。COM 用兩種機制實現對象的重用。我們假定有兩個 COM 對象,對象1 希望能重用對象2 的功能,我們把對象1 稱為外部對象,對象2 稱為內部對象。

(1)包容方式。

對象1 包括了對象2,當對象1 須要用到對象2 的功能時,它能夠簡單地把實現交給對象2 來完畢,盡管對象1 和對象2 支持同樣的接口,但對象1 在實現接口時實際上調用了對象2 的實現。

(2)聚合方式。

對象1 僅僅需簡單地把對象2 的接口遞交給客戶就可以,對象1 并沒有實現對象2 的接口,但它把對象2 的接口也暴露給客戶程序,而客戶程序并不知道內部對象2 的存在。

===================================================

⊙ 第二章 COM 對象模型

===================================================

全局唯一標識符 GUID

---------------------------------------------------

COM 規范採用了 128 位全局唯一標識符 GUID 來標識對象和接口,這是一個隨機數,并不須要專門機構進行分配和管理。由于 GUID 是個隨機數,所以并不絕對保證唯一性,但發生標識符相重的可能性非常小。從理論上講,假設一臺機器每秒產生 10000000 個 GUID,則能夠保證(概率意義上)的 3240 年不反復)。

GUID 在 C/C++ 中能夠用這樣的結構來描寫敘述:

typedef struct _GUID
{
DWORD Data1;
WORD Data2;
WORD Data3;
BYTE Data4[8];
} GUID;

例:{64BF4372-1007-B0AA-444553540000} 能夠例如以下定義一個 GUID:

extern "C" const GUID CLSID_MYSPELLCHECKER =
{ 0x54BF0093, 0x1048, 0x399D,
{ 0xB0, 0xA3, 0x45, 0x33, 0x43, 0x90, 0x47, 0x47} };

Visual C++ 提供了兩個程序生成 GUID: UUIDGen.exe(命令行) 和 GUIDGen.exe(對話框)。COM 庫提供了以下 API 函數能夠產生 GUID:

HRESULT CoCreateGuid(GUID *pguid);

假設創建 GUID 成功,則函數返回 S_OK,并且 pguid 將指向所得的 GUID 值。

---------------------------------------------------

COM 對象

---------------------------------------------------

在 COM 規范中,并沒有對 COM 對象進行嚴格的定義,但 COM 提供的是面向對象的組件模型,COM 組件提供給客戶的是以對象形式封裝起來的實體。客戶程序與 COM 程序進行交互的實體是 COM 對象,它并不關心組件模型的名稱和位置(即位置透明性),但它必須知道自己在與哪個 COM 對象進行交互。

---------------------------------------------------

COM 接口

---------------------------------------------------

從技術上講,接口是包括了一組函數的數據結構,通過這組數據結構,客戶代碼能夠調用組件對象的功能。接口定義了一組成員函數,這組成員函數是組件對象暴露出來的全部信息,客戶程序利用這些函數獲得組件對象的服務。

通常我們把接口函數表稱為虛函數表(vtable),指向 vtable 的指針為 pVtable。對于一個接口來說,它的虛函數表是確定的,因此接口的成員函數個數是不變的,并且成員函數的先后先后順序也是不變的;對于每個成員函數來說,其參數和返回值也是確定的。在一個接口的定義中,全部這些信息都必須在二進制一級確定,無論什么語言,僅僅要能支持這樣的內存結構描寫敘述,就能夠使用接口。

接口指針 ----> pVtable ----> 指針函數1 -> |----------|
m_Data1 指針函數2 -> | 對象實現 |
m_Data2 指針函數3 -> |----------|

每個接口成員函數的第一個參數為指向對象實例的指針(=this),這是由于接口本身并不獨立使用,它必須存在于某個 COM 對象上,因此該指針能夠提供對象實例的屬性信息,在被調用時,接口能夠知道是對哪個 COM 對象在進行操作。

在接口成員函數中,字符串變量必須用 Unicode 字符指針,COM 規范要求使用 Unicode 字符,并且 COM 庫中提供的 COM API 函數也使用 Unicode 字符。所以假設在組件程序內部使用到了 ANSI 字符的話,則應該進行兩種字符表達的轉換。當然,在即建立組件程序又建立客戶程序的情況下,能夠使用自己定義的參數類型,僅僅要它們與 COM 所能識別的參數類型兼容。

Visual C++ 提供兩種字符串的轉換:

namespace _com_util {
BSTR ConvertStringToBSTR(const char *pSrc) throw(_com_error);
BSTR ConvertBSTRToString(BSTR pSrc) throw(_com_error);
}

BSTR 是雙字節寬度字符串,它是最經常使用的自己主動化數據類型。

---------------------------------------------------

接口描寫敘述語言 IDL

---------------------------------------------------

COM 規范在採用 OSF 的 DCE 規范描寫敘述遠程調用接口 IDL (interface description language,接口描寫敘述語言)的基礎上,進行擴展形成了 COM 接口的描寫敘述語言。接口描寫敘述語言提供了一種不依賴于不論什么語言的接口的描寫敘述方法,因此,它能夠成為組件程序和客戶程序之間的共同語言。

COM 規范使用的 IDL 接口描寫敘述語言不僅可用于定義 COM 接口,同一時候還定義了一些經常使用的數據類型,也能夠描寫敘述自己定義的數據結構,對于接口成員函數,我們能夠定義每個參數的類型、輸入輸出特性,甚至支持可變長度的數組的描寫敘述。IDL 支持指針類型,與 C/C++ 非常相似。比如:

interface IDictionary
{
HRESULT Initialize()
HRESULT LoadLibrary([in] string);
HRESULT InsertWord([in] string, [in] string);
HRESULT DeleteWord([in] string);
HRESULT LookupWord([in] string, [out] string *);
HRESULT RestoreLibrary([in] string);
HRESULT FreeLibrary();
}

Microsoft Visual C++ 提供了 MIDL 工具,能夠把 IDL 接口描寫敘述文件編譯成 C/C++ 兼容的接口描寫敘述頭文件(.h)。

---------------------------------------------------

IUnknown 接口

---------------------------------------------------

IUnknown 的 IDL 定義:

interface IUnknown
{
HRESULT QueryInterface([in] REFIID iid, [out] void **ppv);
ULONG AddRef(void);
ULONG Release(void);
}

IUnkown 的 C++ 定義:

class IUnknown
{
virutal HRESULT _stdcall QueryInterface(const IID& iid, void **ppv) = 0;
virtual ULONG _stdcall AddRef() = 0;
virutal ULONG _stdcall Release() = 0;
}

---------------------------------------------------

COM 對象的接口原則

---------------------------------------------------

COM 規范對 QueryInterface 函數設置了以下規則:

1. 對于同一個對象的不同接口指針,查詢得到的 IUnknown 接口必須全然同樣。也就是說,每個對象的 IUnknown 接口指針是唯一的。因此,對兩個接口指針,我們能夠通過推斷其查詢到的 IUnknown 接口是否相等來推斷它們是否指向同一個對象。

2. 接口自反性。對一個接口查詢其自身總應該成功,比方:

pIDictionary->QueryInterface(IID_Dictionary, ...) 應該返回 S_OK。

3. 接口對稱性。假設從一個接口指針查詢到還有一個接口指針,則從第二個接口指針再回到第一個接口指針必然成功,比方:

pIDictionary->QueryInterface(IID_SpellCheck, (void **)&pISpellCheck);

假設查找成功的話,則再從 pISpellCheck 查回 IID_Dictionary 接口肯定成功。

4. 接口傳遞性。假設從第一個接口指針查詢到第二個接口指針,從第二個接口指針能夠查詢到第三個接口指針,則從第三個接口指針一定能夠查詢到第一個接口指針。

5. 接口查詢時間無關性。假設在某一個時刻能夠查詢到某一個接口指針,則以后不論什么時間再查詢同樣的接口指針,一定能夠查詢成功。

總之,無論我們從哪個接口出發,我們總能夠到達不論什么一個接口,并且我們也總能夠回到最初的那個接口。

===================================================

⊙ 第三章 COM 的實現

===================================================

COM 組件注冊信息

---------------------------------------------------

當前機器上全部組件的信息 HKEY_CLASS_ROOT/CLSID

進程內組件 HKEY_CLASS_ROOT/CLSID/guid/InprocServer32

進程外組件 HKEY_CLASS_ROOT/CLSID/guid/LocalServer32

組件所屬類別(CATID) HKEY_CLASS_ROOT/CLSID/guid/Implemented Categories

COM 接口的配置信息 HKEY_CLASS_ROOT/Interface

代理 DLL/存根 DLL HKEY_CLASS_ROOT/CLSID/guid/ProxyStubClsid

HKEY_CLASS_ROOT/CLSID/guid/ProxyStubClsid32

類型庫的信息 HKEY_CLASS_ROOT/TypeLib

字符串命名 ProgID HKEY_CLASS_ROOT/ (比如 "COMCTL.TreeCtrl")

組件 GUID HKEY_CLASS_ROOT/COMTRL.TreeControl/CLSID

缺省版本號號 HKEY_CLASS_ROOT/COMTRL.TreeControl/CurVer

(比如 CurVer = "COMTRL.TreeCtrl.1", 那么

HKEY_CLASS_ROOT/COMTRL.TreeControl.1 也存在)

當前機器全部組件類別 HKEY_CLASS_ROOT/Component Categories

COM 提供兩個 API 函數 CLSIDFromProgID 和 ProgIDFromCLSID 轉換 ProgID 和 CLSID。

假設 COM 組件支持同樣一組接口,則能夠把它們分到同一類中,一個組件能夠被分到多個類中。比方全部的自己主動化對象都支持 IDispatch 接口,則能夠把它們歸成一類“Automation Objects”。類別信息也用一個 GUID 來描寫敘述,稱為 CATID。組件類別最基本的用處在于客戶能夠高速發現機器上的特定類型的組件對象,否則的話,就必須檢查全部的組件對象,并把組件對象裝入到內存中實例化,然后依次詢問是否實現了必要的接口,如今使用了組件類別,就能夠節省查詢過程。

---------------------------------------------------

注冊 COM 組件

---------------------------------------------------

RegSrv32.exe 用于注冊一個進程內組件,它調用 DLL 的 DllRegisterServer 和 DllUnregisterServer 函數完畢組件程序的注冊和注銷操作。假設操作成功返回 TRUE,否則返回 FALSE。

對于進程外組件程序,情形稍有不同,由于它自身是個可運行程序,并且它也不能提供入口函數供其它程序使用。因此,COM 規范中規定,支持自注冊的進程外組件必須支持兩個命令行參數 /RegServer 和 /UnregServer,以便完畢注冊和注銷操作。命令行參數大寫和小寫無關,并且 “/” 能夠用 “-” 替代。假設操作成功,程序返回 0,否則,返回非 0 表示失敗。

---------------------------------------------------

類廠和 DllGetObjectClass 函數

---------------------------------------------------

類廠(class factory)是 COM 對象的生產基地,COM 庫通過類廠創建 COM 對象;對應每個 COM 類,有一個類廠專門用于該 COM 類的對象創建操作。類廠本身也是一個 COM 對象,它支持一個特殊的接口 IClassFactory:

class IClassFactory : public IUnknown
{
virtual HRESULT _stdcall CreateInstance(IUnknown *pUnknownOuter,
const IID& iid, void **ppv) = 0;
virtual HRESULT _stdcall LockServer(BOOL bLock) = 0;
}

CreateInstance 成員函數用于創建對應的 COM 對象。第一個參數 pUnknownOuter 用于對象類被聚合的情形,一般設置為 NULL;第二個參數 iid 是對象創建完畢后客戶應該得到的初始接口 IID;第三個參數 ppv 存放返回的接口指針。

LockServer 成員函數用于控制組件的生存周期。

類廠對象是由 DLL 引出函數 DllGetClassObject 創建的:

HRESULT DllGetClassObject(const CLSID& clsid, const IID& iid, (void **)ppv);

DllGetClassObject 函數的第一個參數為待創建對象的 CLSID。由于一個組件可能實現了多個 COM 對象類,所以在 DllGetClassObject 函數的參數中有必要指定 CLSID,以便創建正確的 class factory。另兩個參數 iid 和 ppv 分別指于指定接口 IID 和存放類廠接口指針。

COM 庫在接到對象創建的指令后,它要調用進程內組件的 DllGetClassObject 函數,由該函數創建類廠對象,并返回類廠對象的接口指針。COM 庫或客戶一旦擁有類廠的接口指針,它們就能夠通過 IClassFactory 的成員函數 CreateInstance 創建對應的 COM 對象。

---------------------------------------------------

CoGetClassObject 函數

---------------------------------------------------

在 COM 庫中,有三個 API 可用于對象的創建,它們各自是 CoGetClassObject、CoCreateInstnace 和 CoCreateInstanceEx。通常情況下,客戶程序調用當中之中的一個完畢對象的創建,并返回對象的初始接口指針。COM 庫與類廠也通過這三個函數進行交互。

HRESULT CoGetClassObject(const CLSID& clsid, DWORD dwClsContext,
COSERVERINFO *pServerInfo, const IID& iid, (void **)ppv);

CoGetClassObject 函數先找到由 clsid 指定的 COM 類的類廠,然后連接到類廠對象,假設須要的話,CoGetClassObject 函數裝入組件代碼。假設是進程內組件對象,則 CoGetClassObject 調用 DLL 模塊的 DllGetClassObject 引出函數,把參數 clsid、iid 和 ppv 傳給 DllGetClassObject 函數,并返回類廠對象的接口指針。通常情況下 iid 為 IClassFactory 的標識符 IID_IClassFactory。假設類廠對象還支持其它可用于創建操作的接口,也能夠使用其它的接口標識符。比如,可請求 IClassFactory2 接口,以便在創建時,驗證用戶的許可證情況。IClassFactory2 接口是對 IClassFactory 的擴展,它加強了組件創建的安全性。

參數 dwClsContext 指定組件類別,能夠指定為進程內組件、進程外組件或者進程內控制對象(相似于進程外組件的代理對象,主要用于 OLE 技術)。參數 iid 和 ppv 分別對應于 DllGetClassObject 的參數,用于指定接口 IID 和存放類對象的接口指針。參數 pServerInfo 用于創建遠程對象時指定server信息,在創建進程內組件對象或者本地進程外組件時,設置 NULL。

假設 CoGetClassObject 函數創建的類廠對象位于進程外組件,則情形要復雜得多。首先 CoGetClassObject 函數啟動組件進程,然后一直等待,直到組件進程把它支持的 COM 類對象的類廠注冊到 COM 中。于是 CoGetClassObject 函數把 COM 中對應的類廠信息返回。因此,組件外進程被 COM 庫啟動時(帶命令行參數“/Embedding”),它必須把所支持的 COM 類的類廠對象通過 CoRegisterClassObject 函數注冊到 COM 中,以便 COM 庫創建 COM 對象使用。當進程退出時,必須調用 CoRevokeClassObject 函數以便通知 COM 它所注冊的類廠對象不再有效。組件程序調用 CoRegisterClassObject 函數和 CoRevokeClassObject 函數必須配對,以保證 COM 信息的一致性。

---------------------------------------------------
CoCreateInstance / CoCreateInstanceEx 函數
---------------------------------------------------
HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter,
DWORD dwClsContext, const IID& iid, (void **)ppv);

CoCreateInstance 是一個被包裝過的輔助函數,在它的內部實際上也調用了 CoGetClassObject 函數。CoCreateInstance 的參數 clsid 和 dwClsContext 的含義與 CoGetClassObject 對應的參數一致,(CoCreateInstance 的 iid 和 ppv 參數與 CoGetClassObject 不同,一個是表示對象的接口信息,一個是表示類廠的接口信息)。參數 pUnknownOuter 與類廠接口的 CreateInstance 中對應的參數一致,主要用于對象被聚合的情況。CoCreateInstance 函數把通過類廠創建對象的過程封裝起來,客戶程序僅僅要指定對象類的 CLSID 和待輸出的接口指針及接口 ID,客戶程序能夠不與類廠打交道。CoCreateInstance 能夠用以下的代碼實現:

(savetime 注:以下代碼中 ppv 指針的應用,好像應該是 void **)

HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter,
DWORD dwClsContext, const IID& iid, void *ppv)
{
IClassFactory *pCF;
HRESULT hr;
hr = CoGetClassObject(clsid, dwClsContext, NULL, IID_IClassFactory,
(void *) pCF);
if (FAILED(hr)) return hr;
hr = pCF->CreateInstance(pUnknownOuter, iid, (void *)ppv);
pFC->Release();
return hr;
}

從這段代碼我們能夠看出,CoCreateInstance 函數首先利用 CoGetClassObject 函數創建類廠對象,然后用得到的類廠對象的接口指針創建真正的 COM 對象,最后把類廠對象釋放掉并返回,這樣就把類廠屏蔽起來。

可是,用 CoCreateInstance 并不能創建遠程機器上的對象,由于在調用 CoGetClassObject 時,把第三個用于指定server信息的參數設置為 NULL。假設要創建遠程對象,能夠使用 CoCreateInstance 的擴展函數 CoCreateInstanceEx:

HRESULT CoCreateInstanceEx(const CLSID& clsid, IUnknown *pUnknownOuter,

DWORD dwClsContext, COSERVERINFO *pServerInfo, DWORD dwCount,

MULTI_QI *rgMultiQI);

前三個參數與 CoCreateInstance 一樣,pServerInfo 與 CoGetClassOjbect 的參數一樣,用于指定server信息,最后兩個參數 dwCount 和 rgMultiQI 指定了一個結構數組,能夠用于保存多個對象接口指針,其目的在于一次獲得多個接口指針,以便降低客戶程序與組件程序之間的頻繁交互,這對于網絡環境下的遠程對象是非常有意義的。

---------------------------------------------------

COM 庫的初始化

---------------------------------------------------

調用 COM 庫的函數之前,為了使函數有效,必須調用 COM 庫的初始化函數:

HRESULT CoInitialize(IMalloc *pMalloc);

pMalloc 用于指定一個內存分配器,可由應用程序指定內存分配原則。普通情況下,我們直接把參數設為 NULL,則 COM 庫將使用缺省提供的內存分配器。

返回值:S_OK 表示初始化成功

S_FALSE 表示初始化成功,但這次調用不是本進程中首次調用初始化函數

S_UNEXPECTED 表示初始化過程中發生了錯誤,應用程序不能使用 COM 庫

通常,一個進程對 COM 庫僅僅進行一次初始化,并且,在同一個模塊單元中對 COM 庫進行多次初始化并沒有意義。唯一不須要初始化 COM 庫的函數是獲取 COM 庫版本號的函數:

DWORD CoBuildVersion();

返回值:高 16 位 主版本號號

低 16 位 次版本號號

COM 程序在用完 COM 庫服務之后,一般是在程序退出之前,一定要調用終止 COM 庫服務函數,以便釋放 COM 庫所維護的資源:

void CoUninitialize(void);

注意:凡是調用 CoInitialize 函數返回 S_OK 的進程或程序模塊一定要有對應的 CoUninitialize 函數調用,以保證 COM 庫有效地利用資源。

(? 假設在一個模塊中調用 CoInitialize 返回 S_OK,那么它調用 CoUnitialize 函數后,其它也在使用 COM 庫的模塊是否會出錯誤?還是 COM 庫會自己主動檢查有哪些模塊在使用?)

---------------------------------------------------

COM 庫的內存管理

---------------------------------------------------

由于 COM 組件程序和客戶程序是通過二進制級標準建立連接的,所以在 COM 應用程序中凡是涉及客戶、COM 庫和組件三者之間內存交互(分配和釋放不在同一個模塊中)的操作必須使用一致的內存管理器。COM 提供的內存管理標準,實際上是一個 IMalloc 接口:

// IID_IMalloc: {00000002-0000-0000-C000-000000000046}
class IMalloc: public IUnknown
{
void * Alloc(ULONG cb) = 0;
void * Realloc(void *pv, ULONG cb) = 0;
void Free(void *pv) = 0;
ULONG GetSize(void *pv) = 0; // 返回分配的內存大小
int DidAlloc(void *pv) = 0; // 確定內存指針是否由該內存管理器分配
void HeapMinimize() = 0; // 使堆內存盡可能降低,把沒用到的內存還給
// 操作系統,用于性能優化
}

獲得 IMalloc 接口指針:

HRESULT CoGetMalloc(DWORD dwMemContext, IMalloc **ppMalloc);

CoGetMalloc 函數的第一個參數 dwMemContext 用于指定內存管理器的類型。COM 庫中包括兩種內存管理器,一種就是在初始化時指定的內存管理器或者其內部缺省的管理器,也稱為作業管理器(task allocator),這樣的管理器在本進程內有效,要獲取該管理器,在 dwMemContext 參數中指定為 MEMCTX_TASK;還有一種是跨進程的共享分配器,由 OLE 系統提供,要獲取這樣的管理器,dwMemContext 參數中指定為 MEMCTX_SHARED,使用共享管理器的便利是,能夠在一個進程內分配內存并傳給第二個進程,在第二個進程內使用此內存甚至釋放掉此內存。

僅僅要函數的返回值為 S_OK,則 ppMalloc 就指向了 COM 庫的內存管理器接口指針,能夠使用它進行內存操作,使用完畢后,應該調用 Release 成員函數釋放控制權。

COM 庫封裝了三個 API 函數,可用于內存分配和釋放:

void * CoTaskMemAlloc(ULONG cb);
void CoTaskFree(void *pv);
void CoTaskMemRealloc(void *pv, ULONG cb);

這三個函數分配對應于 IMalloc 的三個成員函數:Alloc、Realloc 和 Free。

例:COM 程序怎樣從 CLSID 值找到對應的 ProgID 值:

WCHAR *pwProgID;
char pszProgID[128];
hResult = ::ProgIDFromCLSID(CLSID_Dictionary, &pwProgID);
if (hResult != S_OK) {
...
}
wcstombs(pszProgID, pwProgID, 128);
CoTaskMemFree(pwProgID); // 注意:必須釋放內存

在調用 COM 函數 ProgIDFromCLSID 返回之后,由于 COM 庫為輸出變量 pwProgID 分配了內存空間,所以應用程序在用完 pwProgID 變量之后,一定要調用 CoTaskMemFree 函數釋放內存。該樣例說明了在 COM 庫中分配內存,而在調用程序中釋放內存的一種情況。COM 庫中其它一些函數也有相似的特性,尤其是一些包括不定長度輸出參數的函數。

---------------------------------------------------

組件程序的裝載和卸載

---------------------------------------------------

進程內組件的裝載:

客戶程序調用COM 庫的 CoCreateInstance 或 CoGetClassObject 函數創建 COM 對象,在 CoGetClassObject 函數中,COM 庫依據系統注冊表中的信息,找到類標識符 CLSID 對應的組件程序(DLL 文件)的全路徑,然后調用 LoadLibrary(實際上是 CoLoadLibrary)函數,并調用組件程序的 DllGetClassObject 引出函數。DllGetClassObject 函數創建對應的類廠對象,并返回類廠對象的 IClassFactory 接口。至此 CoGetClassObject 函數的任務完畢,然后客戶程序或者 CoCreateInstance 函數繼續調用類廠對象的 CreateInstance 成員函數,由它負責 COM 對象的創建工作。

CoCreateInstance
|-CoGetClassObject
|-Get CLSID -> DLLfile path
|-CoLoadLibrary
|-DLLfile.DllGetClassObject
|-return IClassFactory
|-IClassFactory.CreateInstnace

進程外組件的裝載:

在 COM 庫的 CoGetClassObject 函數中,當它發現組件程序是 EXE 文件(由注冊表組件對象信息中的 LocalServer 或 LocalServer32 值指定)時,COM 庫創建一個進程啟動組件程序,并帶上“/Embedding”命令行參數,然后等待組件程序;而組件程序在啟動后,當它檢查到“/Embedding”命令行參數后,就會創建類廠對象,然后調用 CoRegisterClassObject 函數把類廠對象注冊到 COM 中。當 COM 庫檢查到組件對象的類廠之后,CoGetClassObject 函數就把類廠對象返回。由于類廠與客戶程序運行在不同的進程中,所以客戶程序得到的是類廠的代理對象。一旦客戶程序或 COM 庫得到了類廠對象,它就能夠完畢組件對象的創建工作。

進程內對象和進程外對象的不同創建過程僅僅影響了 CoGetClassObject 函數的實現過程,對于客戶程序來說是全然透明的。

CoGetClassObject
|-LocalServer/LocalServer32
|-Execute EXE /Embedding
|-Create class factory
|-CoRegisterClassObject ( class factory )
|-return class factory (proxy)

進程內組件的卸載:

僅僅有當組件程序滿足了兩個條件時,它才干被卸載,這兩個條件是:組件中對象數為 0,類廠的鎖計數為 0。滿足這兩個條件時,DllCanUnloadNow 引出函數返回 TRUE。COM 提供了一個函數 CoFreeUnusedLibraries,它會檢測當前進程中的全部組件程序,當發現某個組件程序的 DllCanUnloadNow 函數返回 TRUE 時,就調用 FreeLibrary 函數(實際上是 CoFreeLibrary 函數)把該組件從程序從內存中卸出。

該由誰來調用 CoFreeUnusedLibraries 函數呢?由于在組件程序運行過程中,它不可能把自己從內存中卸出,所以這個任務應該由客戶來完畢??蛻舫绦螂S時都能夠調用 CoFreeUnusedLibraries 函數完畢卸出工作,但通常的做法是,在程序的空暇處理過程中調用 CoFreeUnusedLibraries 函數,這樣做既能夠避免程序中處處考慮對 CoFreeUnusedLibraries 函數的調用,又能夠使不再使用的組件程序得到及時清除,提高資源的利用率,COM 規范也推薦這樣的做法。

進程外組件的卸載:

進程外組件的卸載比較簡單,由于組件程序運行在單獨的進程中,一旦其退出的條件滿足,它僅僅要從進程的主控函數返回就可以。在 Windows 系統中,進程的主控函數為 WinMain。

前面曾經說過,在組件程序啟動運行時,它調用 CoRegisterClassObject 函數,把類廠對象注冊到 COM 中,注冊之后,類廠對象的引用計數始終大于 0,因此單憑類廠對象的引用計數無法控制進程的生存期,這也是引入類廠對象的加鎖和減鎖操作的原因。進程外組件的載條件與 DllCanUnloadNow 中的推斷相似,也須要推斷 COM 對象是否還存在、以及推斷是否鎖計數器為 0,僅僅有當條件滿足了,進程的主函數才干夠退出。

從原則上講,進程外組件程序的卸載就是這么簡單,但實際上情況可能復雜一些,由于有些組件程序在運行過程中能夠創建自己的對象,或者包括用戶界面的程序在運行過程中,用戶手工關閉了進程,那么進程對這些動作的處理要復雜一些。比如,組件程序在運行過程中,用戶又打開了一個文件并進行操作,那么即使原先創建的對象被釋放了,并且鎖計數器也為 0,進程也不能退出,它必須繼續為用戶服務,就像是用戶打開的進程一樣。對這樣的程序,能夠增加一個“用戶控制”標記 flag,假設 flag 為 FALSE,則能夠按簡單的方法直接退出程序就可以;假設 flag 為 TRUE,則表明用戶參與了控制,組件進程不能立即退出,但應該調用 CoRevokeClassObject 函數以便與 CoRegisterClassObject 調用相響呼應,把進程留給用戶繼續進行。

假設組件程序在運行過程中,用戶要關閉進程,而此時并不滿足進程退出條件,那么進程能夠採取兩種辦法:第一種方法,把應用隱藏起來,并把 flag 標記設置為 FALSE,然后組件程序繼續運行直到卸載條件滿足為止;還有一種辦法是,調用 CoDisconnectObject 函數,強迫脫離對象與客戶之間的關系,并強行終止進程,這樣的方法比較粗暴,不提倡採用,但不得已時能夠也使用,以保證系統完畢一些高優先級的操作。

---------------------------------------------------

COM 庫經常使用函數

---------------------------------------------------

初始化函數 CoBuildVersion 獲得 COM 庫的版本號號

CoInitialize COM 庫初始化

CoUninitialize COM 庫功能服務終止

CoFreeUnusedLibraries 釋放進程中全部不再使用的組件程序

GUID 相關函數 IsEqualGUID 推斷兩個 GUID 是否相等

IsEqualIID 推斷兩個 IID 是否相等

IsEqualCLSID 推斷兩個 CLSID 是否相等 (*為什么要3個函數)

CLSIDFromProgID 字符串組件標識轉換為 CLSID 形式

StringFromCLSID CLSID 形式標識轉化為字符串形式

IIDFromString 字符串轉換為 IID 形式

StringFromIID IID 形式轉換為字符串

StringFromGUID2 GUID 形式轉換為字符串(*為什么有 2)

對象創建函數 CoGetClassObject 獲取類廠對象

CoCreateInstance 創建 COM 對象

CoCreateInstanceEx 創建 COM 對象,可指定多個接口或遠程對象

CoRegisterClassObject 登記一個對象,使其它應用程序能夠連接到它

CoRevokeClassObject 取消對象的登記

CoDisconnectObject 斷開其它應用與對象的連接

內存管理函數 CoTaskMemAlloc 內存分配函數

CoTaskMemRealloc 內存又一次分配函數

CoTaskMemFree 內存釋放函數

CoGetMalloc 獲取 COM 庫內存管理器接口

---------------------------------------------------

HRESULT 類型

---------------------------------------------------

大多數 COM 函數以及一些接口成員函數的返回值類型均為 HRESULT 類型。HRESULT 類型的返回值反映了函數中的一些情況,其類型定義規范例如以下:

31 30 29 28 16 15 0
|-----|--|------------------------|-----------------------------------|

類別碼 (30-31) 反映函數調用結果:

00 調用成功

01 包括一些信息

10 警告

11 錯誤

自己定義標記(29) 反映結果是否為自己定義標識,1 為是,0 則不是;

操作碼 (16-28) 標識結果操作來源,在 Windows 平臺上,其定義例如以下:

#define FACILITY_WINDOWS 8
#define FACILITY_STORAGE 3
#define FACILITY_RPC 1
#define FACILITY_SSPI 9
#define FACILITY_WIN32 7
#define FACILITY_CONTROL 10
#define FACILITY_NULL 0
#define FACILITY_INTERNET 12
#define FACILITY_ITF 4
#define FACILITY_DISPATCH 2
#define FACILITY_CERT 11

操作結果碼(0-15) 反映操作的狀態,WinError.h 定義了 Win32 函數全部可能返回結果。

以下是一些經經常使用到的返回值和宏定義:

S_OK 函數運行成功,其值為 0 (注意,其值與 TRUE 相反)

S_FALSE 函數運行成功,其值為 1

S_FAIL 函數運行失敗,失敗原因不確定

E_OUTOFMEMORY 函數運行失敗,失敗原由于內存分配不成功

E_NOTIMPL 函數運行失敗,成員函數沒有被實現

E_NOTINTERFACE 函數運行失敗,組件沒有實現指定的接口

不能簡單地把返回值與 S_OK 和 S_FALSE 比較,而要用 SECCEEDED 和 FAILED 宏進行推斷。

===================================================

⊙ 第四章 COM 特性

===================================================

可重用性:包容和聚合

---------------------------------------------------

包容模型:

組件對象在接口的實現代碼中運行自身創建的還有一個組件對象的接口函數(客戶/server模型)。這個對象同一時候實現了兩個(或很多其它)接口的代碼。

聚合模型:

組件對象在接口的查詢代碼中把接口傳遞給自已創建的還有一個對象的接口查詢函數,而不實現該接口的代碼。還有一個對象必須實現聚合模型(也就是說,它知道自己正在被還有一個組件對象聚合),以便 QueryInterface 函數能夠正常運作。

在組件對象被聚合的情況下,當客戶請求它所不支持的接口或者請求 IUnknown 接口時,它必須把控制交給外部對象,由外部對象決定客戶程序的請求結果。

聚合模型體現了組件軟件真正意義上的重用。

聚合模型實現的關鍵在 CoCreateInstance 函數和 IClassFactory 接口:

HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter,
DWORD dwClsContext, const IID& iid, (void **)ppv);
// class IClassFactory : public IUnknown
virtual HRESULT _stdcall CreateInstance(IUnknown *pUnknownOuter,
const IID& iid, void **ppv) = 0;

當中 pUnknownOuter 參數用于指定組件對象是否被聚合。假設 pUnknownOuter 參數為 NULL,說明組件對象正常使用,否則說明被聚合使用,pUnknownOuter 是外部組件對象的接口指針。

聚合模型下的被聚合對象的引用計數成員函數也要進行特別處理。在未被聚合的情況下,能夠使用一般的引用計數方法。在被聚合時,由客戶調用 AddRef/Release 函數時,必須轉向外部組件對象的 AddRef/Release 方法。這時,外部組件對象要控制被聚合的對象必須採用其它的引用計數接口。

---------------------------------------------------

進程透明性 (待學)

安全性(待學)

多線程特性(待學)

---------------------------------------------------

===================================================

⊙ 第五章 用 Visual C++ 開發 COM 應用

===================================================

Win32 SDK 提供的一些頭文件的說明

---------------------------------------------------

Unknwn.h 標準接口 IUnknown 和 IClassFacatory 的 IID 及接口成員函數的定義

Wtypes.h 包括 COM 使用的數據結構的說明

Objidl.h 全部標準接口的定義,就可以用于 C 語言風格的定義,也可用于 C++ 語言 Comdef.h 全部標準接口以及 COM 和 OLE 內部對象的 CLSID ObjBase.h 全部的 COM API 函數的說明 Ole2.h 全部經過封裝的 OLE 輔助函數

---------------------------------------------------

與 COM 接口有關的一些宏

---------------------------------------------------

DECLARE_INTERFACE(iface)

聲明接口 iface,它不從其它的接口派生

DECLARE_INTERFACE_(iface, baseiface)

聲明接口 iface,它從接口 baseiface 派生

STDMETHOD(method)

聲明接口成員函數 method,函數返回類型為 HRESULT

STDMETHOD_(type, method) 聲明接口成員函數 method,函數返回類型為 type

===================================================

⊙ 結 束

===================================================

轉載于:https://www.cnblogs.com/bhlsheji/p/4065439.html

總結

以上是生活随笔為你收集整理的Delphi 2007体验!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 亚洲国产欧美日韩精品一区二区三区 | 国产在线精品一区二区高清不卡 | 国产午夜亚洲精品不卡下载 | 高潮喷水的毛片 | 国产在线无码精品电影网 | 国产无遮挡又黄又爽又色 | 午夜时刻免费入口 | 99久久亚洲精品无码毛片 | 国产精品鲁鲁鲁 | 亚洲国产高清在线观看视频 | 日本乱偷人妻中文字幕 | 高潮毛片无遮挡高清免费 | 久久99精品久久久久久动态图 | 亚洲熟女一区二区三区 | 奇米影视7777久久精品人人爽 | 强奷人妻日本中文字幕 | 国产欧美精品一区二区三区 | 亚洲人成影院在线无码按摩店 | 国产精品香蕉在线观看 | 亚洲中文字幕无码一久久区 | 岛国片人妻三上悠亚 | 丰满肥臀大屁股熟妇激情视频 | 国产成人无码区免费内射一片色欲 | 99久久久无码国产aaa精品 | 亚洲最大成人网站 | 国产精品无码一区二区桃花视频 | 亚洲色在线无码国产精品不卡 | 精品一区二区不卡无码av | 亚洲色在线无码国产精品不卡 | 狂野欧美性猛交免费视频 | 国产无遮挡又黄又爽又色 | 婷婷六月久久综合丁香 | 男人的天堂2018无码 | 久久久中文字幕日本无吗 | 无码人妻出轨黑人中文字幕 | 亚洲成a人一区二区三区 | 国产精品亚洲一区二区三区喷水 | 无码人妻精品一区二区三区不卡 | 国产成人午夜福利在线播放 | 中文无码精品a∨在线观看不卡 | 性欧美大战久久久久久久 | 国产精品a成v人在线播放 | 中文无码精品a∨在线观看不卡 | 波多野结衣乳巨码无在线观看 | 国产精品久免费的黄网站 | 午夜熟女插插xx免费视频 | 国产莉萝无码av在线播放 | 日韩av无码一区二区三区不卡 | 日本熟妇浓毛 | 国产精品无码一区二区桃花视频 | 国产成人无码av一区二区 | 亚洲欧美综合区丁香五月小说 | 18禁黄网站男男禁片免费观看 | 樱花草在线播放免费中文 | 亚洲人交乣女bbw | 天堂无码人妻精品一区二区三区 | 久久久精品人妻久久影视 | 男女爱爱好爽视频免费看 | 青青草原综合久久大伊人精品 | 少妇无套内谢久久久久 | 欧美肥老太牲交大战 | av无码不卡在线观看免费 | 成熟妇人a片免费看网站 | 色五月丁香五月综合五月 | 扒开双腿疯狂进出爽爽爽视频 | 欧美日韩视频无码一区二区三 | 成人aaa片一区国产精品 | 婷婷综合久久中文字幕蜜桃三电影 | 日韩视频 中文字幕 视频一区 | 国产亚洲视频中文字幕97精品 | 欧美自拍另类欧美综合图片区 | 夜夜影院未满十八勿进 | 丝袜足控一区二区三区 | 超碰97人人做人人爱少妇 | 免费人成在线视频无码 | 国产熟妇高潮叫床视频播放 | 日本大乳高潮视频在线观看 | 国产一区二区三区日韩精品 | 精品国产国产综合精品 | 国产内射爽爽大片视频社区在线 | 成人免费视频视频在线观看 免费 | 精品国产aⅴ无码一区二区 | 亚洲爆乳无码专区 | 国产黑色丝袜在线播放 | 国产成人精品无码播放 | 国产精品99爱免费视频 | 免费无码的av片在线观看 | 精品 日韩 国产 欧美 视频 | 亚洲精品一区二区三区婷婷月 | 成人欧美一区二区三区黑人 | 野外少妇愉情中文字幕 | 欧美兽交xxxx×视频 | 精品久久久中文字幕人妻 | 亚洲日本va午夜在线电影 | 少妇人妻偷人精品无码视频 | 亚洲 日韩 欧美 成人 在线观看 | 亚洲国产精品一区二区第一页 | 免费国产黄网站在线观看 | 88国产精品欧美一区二区三区 | 99久久婷婷国产综合精品青草免费 | 性史性农村dvd毛片 | 天天拍夜夜添久久精品 | 少妇高潮喷潮久久久影院 | 极品嫩模高潮叫床 | 国产在线一区二区三区四区五区 | 欧美人妻一区二区三区 | 国产午夜福利亚洲第一 | 狠狠色噜噜狠狠狠狠7777米奇 | 欧美成人高清在线播放 | 天堂一区人妻无码 | 久久国产自偷自偷免费一区调 | 日韩人妻系列无码专区 | 久久国产精品二国产精品 | 国产av一区二区三区最新精品 | 亚洲一区二区三区播放 | 少妇无码av无码专区在线观看 | 草草网站影院白丝内射 | 久久国产36精品色熟妇 | 日韩精品无码免费一区二区三区 | 亚洲精品国产精品乱码不卡 | 午夜精品一区二区三区在线观看 | 国产激情无码一区二区app | 亚洲七七久久桃花影院 | 亚洲а∨天堂久久精品2021 | 亚洲成熟女人毛毛耸耸多 | 久久亚洲国产成人精品性色 | 乌克兰少妇性做爰 | 99久久精品午夜一区二区 | 亚洲熟女一区二区三区 | 麻豆果冻传媒2021精品传媒一区下载 | 国产色xx群视频射精 | 婷婷色婷婷开心五月四房播播 | 亚洲天堂2017无码 | 精品厕所偷拍各类美女tp嘘嘘 | 亚洲欧美日韩国产精品一区二区 | 狠狠噜狠狠狠狠丁香五月 | 国精品人妻无码一区二区三区蜜柚 | 国产精品久久久午夜夜伦鲁鲁 | 中文无码精品a∨在线观看不卡 | 男女下面进入的视频免费午夜 | 日韩人妻无码一区二区三区久久99 | 露脸叫床粗话东北少妇 | 色综合天天综合狠狠爱 | 天堂а√在线地址中文在线 | 精品成人av一区二区三区 | 久久久久久久人妻无码中文字幕爆 | 偷窥村妇洗澡毛毛多 | 亚洲精品一区三区三区在线观看 | 国产午夜亚洲精品不卡 | 国产精品多人p群无码 | 亚洲中文字幕在线观看 | 亚洲区欧美区综合区自拍区 | 国产农村妇女高潮大叫 | 日韩视频 中文字幕 视频一区 | 性开放的女人aaa片 | 久久久精品欧美一区二区免费 | 日本精品人妻无码免费大全 | 国产av剧情md精品麻豆 | 久久久久成人片免费观看蜜芽 | 国产激情精品一区二区三区 | 国产亚洲日韩欧美另类第八页 | 欧美日韩在线亚洲综合国产人 | 永久黄网站色视频免费直播 | 无码福利日韩神码福利片 | 国产精品高潮呻吟av久久4虎 | 成人免费视频一区二区 | 福利一区二区三区视频在线观看 | 精品一区二区三区波多野结衣 | 蜜桃av抽搐高潮一区二区 | 亚洲国产日韩a在线播放 | 日日麻批免费40分钟无码 | 奇米影视7777久久精品人人爽 | 亚洲一区二区三区在线观看网站 | 中文字幕av伊人av无码av | 色综合久久88色综合天天 | 麻豆国产97在线 | 欧洲 | 亚洲自偷精品视频自拍 | 中国女人内谢69xxxxxa片 | 大地资源网第二页免费观看 | 男人和女人高潮免费网站 | 夜夜影院未满十八勿进 | 国产午夜福利亚洲第一 | 国产美女精品一区二区三区 | 久久久久亚洲精品男人的天堂 | 国产午夜无码精品免费看 | 欧美成人免费全部网站 | 欧美性猛交内射兽交老熟妇 | 丰满岳乱妇在线观看中字无码 | 国产97在线 | 亚洲 | 日韩精品无码一本二本三本色 | 日日麻批免费40分钟无码 | 3d动漫精品啪啪一区二区中 | 女人被男人爽到呻吟的视频 | 最近中文2019字幕第二页 | 欧美亚洲日韩国产人成在线播放 | 强开小婷嫩苞又嫩又紧视频 | 亚洲人交乣女bbw | 精品人妻人人做人人爽 | 老子影院午夜精品无码 | 色婷婷欧美在线播放内射 | 亚洲国产成人a精品不卡在线 | 成熟人妻av无码专区 | 国产乱人伦偷精品视频 | 精品人人妻人人澡人人爽人人 | 精品成在人线av无码免费看 | 少妇激情av一区二区 | 男女下面进入的视频免费午夜 | 久久久久久av无码免费看大片 | 日本高清一区免费中文视频 | 精品国产av色一区二区深夜久久 | 狂野欧美激情性xxxx | 狠狠亚洲超碰狼人久久 | 久久这里只有精品视频9 | 强开小婷嫩苞又嫩又紧视频 | 乱人伦中文视频在线观看 | 亚洲国产欧美日韩精品一区二区三区 | 在线观看国产午夜福利片 | 日韩亚洲欧美精品综合 | 国产亲子乱弄免费视频 | 爆乳一区二区三区无码 | 国产亚洲精品精品国产亚洲综合 | 精品久久久久久亚洲精品 | 欧美国产日韩亚洲中文 | 亚洲爆乳大丰满无码专区 | 亚洲色大成网站www国产 | 亚洲精品一区国产 | 欧美人与禽猛交狂配 | 亚洲人成网站在线播放942 | 无码av中文字幕免费放 | 亚洲人成网站在线播放942 | 国产亚洲人成在线播放 | 国产一区二区三区影院 | 国产熟妇另类久久久久 | 丰满人妻被黑人猛烈进入 | 亚洲色在线无码国产精品不卡 | 2019nv天堂香蕉在线观看 | 亚洲人成网站在线播放942 | 免费播放一区二区三区 | 又大又硬又爽免费视频 | 国产精品-区区久久久狼 | 初尝人妻少妇中文字幕 | 婷婷色婷婷开心五月四房播播 | 国产办公室秘书无码精品99 | 大色综合色综合网站 | a在线亚洲男人的天堂 | 欧美xxxxx精品 | 成人精品视频一区二区三区尤物 | 丰满少妇女裸体bbw | 色一情一乱一伦一区二区三欧美 | 国产后入清纯学生妹 | 中文字幕色婷婷在线视频 | 扒开双腿疯狂进出爽爽爽视频 | 人妻少妇被猛烈进入中文字幕 | 亚洲乱码国产乱码精品精 | 日本护士毛茸茸高潮 | 在教室伦流澡到高潮hnp视频 | 曰本女人与公拘交酡免费视频 | av无码久久久久不卡免费网站 | 波多野结衣 黑人 | 成 人 网 站国产免费观看 | 亚洲综合无码一区二区三区 | 小鲜肉自慰网站xnxx | 少妇太爽了在线观看 | 国产精品美女久久久网av | 黑人粗大猛烈进出高潮视频 | 国产综合在线观看 | 国产绳艺sm调教室论坛 | 人人妻人人澡人人爽精品欧美 | 大地资源中文第3页 | 国产av一区二区三区最新精品 | 国产97色在线 | 免 | 久久亚洲国产成人精品性色 | 精品一区二区三区波多野结衣 | 大地资源中文第3页 | 国产亚洲美女精品久久久2020 | 18精品久久久无码午夜福利 | 欧美高清在线精品一区 | 欧美野外疯狂做受xxxx高潮 | 巨爆乳无码视频在线观看 | 中文字幕无码人妻少妇免费 | 亚洲成色在线综合网站 | 色综合久久久久综合一本到桃花网 | 内射老妇bbwx0c0ck | 人妻少妇精品无码专区动漫 | 国产欧美精品一区二区三区 | 精品无码国产一区二区三区av | 国产精品内射视频免费 | 国产乱人伦偷精品视频 | 日本肉体xxxx裸交 | 欧美日韩视频无码一区二区三 | 中文字幕乱码人妻二区三区 | 国产av无码专区亚洲a∨毛片 | 性做久久久久久久免费看 | 奇米影视7777久久精品 | 国产av一区二区三区最新精品 | а天堂中文在线官网 | 99在线 | 亚洲 | 毛片内射-百度 | 日产精品99久久久久久 | 妺妺窝人体色www在线小说 | 久久久精品成人免费观看 | 日韩av无码中文无码电影 | 免费无码肉片在线观看 | 色 综合 欧美 亚洲 国产 | 午夜男女很黄的视频 | 国产深夜福利视频在线 | 日韩少妇白浆无码系列 | 国产高清av在线播放 | 久久午夜无码鲁丝片午夜精品 | 成 人 免费观看网站 | 日本爽爽爽爽爽爽在线观看免 | 亚洲国产精品久久人人爱 | 日本熟妇浓毛 | 青草青草久热国产精品 | 中文字幕日产无线码一区 | 欧美freesex黑人又粗又大 | 国产97人人超碰caoprom | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 在线a亚洲视频播放在线观看 | 99久久精品午夜一区二区 | 亚洲国产精品一区二区美利坚 | 无码人妻黑人中文字幕 | 夜夜夜高潮夜夜爽夜夜爰爰 | 无码一区二区三区在线 | 中文字幕乱码中文乱码51精品 | 人人爽人人澡人人高潮 | 在线播放无码字幕亚洲 | 性开放的女人aaa片 | 国产乱码精品一品二品 | 正在播放老肥熟妇露脸 | 影音先锋中文字幕无码 | 欧洲美熟女乱又伦 | 中文字幕乱码亚洲无线三区 | 久久久婷婷五月亚洲97号色 | 欧美性生交xxxxx久久久 | 日本www一道久久久免费榴莲 | 熟妇人妻中文av无码 | 久久久国产精品无码免费专区 | 精品国产麻豆免费人成网站 | 欧美精品无码一区二区三区 | 欧美丰满老熟妇xxxxx性 | 无码人妻丰满熟妇区毛片18 | 国产在线精品一区二区三区直播 | 丰满人妻一区二区三区免费视频 | 午夜男女很黄的视频 | 特大黑人娇小亚洲女 | 亚洲 激情 小说 另类 欧美 | 又湿又紧又大又爽a视频国产 | 精品亚洲韩国一区二区三区 | 久久精品中文字幕大胸 | 又色又爽又黄的美女裸体网站 | 久精品国产欧美亚洲色aⅴ大片 | 亚洲欧美国产精品专区久久 | 国产成人综合在线女婷五月99播放 | 国产性生大片免费观看性 | 欧美性猛交xxxx富婆 | 玩弄人妻少妇500系列视频 | 学生妹亚洲一区二区 | 亚洲小说春色综合另类 | yw尤物av无码国产在线观看 | 久久综合香蕉国产蜜臀av | 国产国语老龄妇女a片 | 久久久久久a亚洲欧洲av冫 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 国产av久久久久精东av | 久久久久免费精品国产 | 精品成在人线av无码免费看 | 无码人妻久久一区二区三区不卡 | 三级4级全黄60分钟 | 狠狠色噜噜狠狠狠狠7777米奇 | 欧美日韩在线亚洲综合国产人 | 欧美人与禽zoz0性伦交 | 人人爽人人爽人人片av亚洲 | 六月丁香婷婷色狠狠久久 | 国产色视频一区二区三区 | 亚洲天堂2017无码 | 日本熟妇大屁股人妻 | 日本爽爽爽爽爽爽在线观看免 | 欧美野外疯狂做受xxxx高潮 | 蜜桃av抽搐高潮一区二区 | 亚洲精品国偷拍自产在线麻豆 | 永久免费精品精品永久-夜色 | 欧美一区二区三区 | 国产午夜手机精彩视频 | 国产精品高潮呻吟av久久 | 亚洲性无码av中文字幕 | 欧美 亚洲 国产 另类 | 亚洲爆乳精品无码一区二区三区 | 国产精品国产自线拍免费软件 | 亚洲色无码一区二区三区 | 色综合久久中文娱乐网 | 免费人成网站视频在线观看 | 98国产精品综合一区二区三区 | 日日橹狠狠爱欧美视频 | 一本久久伊人热热精品中文字幕 | 午夜熟女插插xx免费视频 | 熟妇人妻无乱码中文字幕 | 国产电影无码午夜在线播放 | 成人aaa片一区国产精品 | 日韩少妇白浆无码系列 | 免费观看的无遮挡av | 国产香蕉尹人综合在线观看 | 国产午夜亚洲精品不卡下载 | 中文字幕无码av激情不卡 | 免费观看黄网站 | 四虎国产精品免费久久 | 性做久久久久久久免费看 | 无码帝国www无码专区色综合 | 精品国产国产综合精品 | 亚洲成av人在线观看网址 | 国产va免费精品观看 | 无码av岛国片在线播放 | 中文字幕无码免费久久99 | 捆绑白丝粉色jk震动捧喷白浆 | 国产精品a成v人在线播放 | 亚洲呦女专区 | 国产亚洲精品久久久久久久久动漫 | 成人精品视频一区二区 | 亚欧洲精品在线视频免费观看 | 国产精品久久久av久久久 | 秋霞特色aa大片 | 亚洲精品成人福利网站 | 荫蒂添的好舒服视频囗交 | 免费国产黄网站在线观看 | 最新国产乱人伦偷精品免费网站 | 又大又硬又黄的免费视频 | 久久精品国产一区二区三区肥胖 | 无码免费一区二区三区 | 超碰97人人射妻 | 亚洲精品成人av在线 | 纯爱无遮挡h肉动漫在线播放 | 亚洲gv猛男gv无码男同 | 午夜无码人妻av大片色欲 | 中文字幕乱码亚洲无线三区 | 国产福利视频一区二区 | 老司机亚洲精品影院无码 | 国产乱人无码伦av在线a | 亚洲精品中文字幕 | 色婷婷综合激情综在线播放 | 三上悠亚人妻中文字幕在线 | 国内丰满熟女出轨videos | 亚洲国产精品无码久久久久高潮 | 中文字幕无码av波多野吉衣 | 一区二区传媒有限公司 | 婷婷丁香六月激情综合啪 | 少妇无码av无码专区在线观看 | 国产绳艺sm调教室论坛 | 国内精品人妻无码久久久影院蜜桃 | 国产乱码精品一品二品 | 美女扒开屁股让男人桶 | 少妇激情av一区二区 | 乱人伦人妻中文字幕无码 | 中文字幕+乱码+中文字幕一区 | 日韩在线不卡免费视频一区 | 久久99精品国产麻豆 | 67194成是人免费无码 | 又大又紧又粉嫩18p少妇 | 日本熟妇大屁股人妻 | 狠狠色色综合网站 | 午夜精品一区二区三区在线观看 | 亚洲а∨天堂久久精品2021 | 亚洲精品国偷拍自产在线麻豆 | 丰满妇女强制高潮18xxxx | 久久精品丝袜高跟鞋 | 国产精品99爱免费视频 | 狠狠躁日日躁夜夜躁2020 | 日韩少妇白浆无码系列 | 亚洲精品美女久久久久久久 | 日韩av无码一区二区三区不卡 | 欧美成人免费全部网站 | 大乳丰满人妻中文字幕日本 | 欧美亚洲国产一区二区三区 | 在线a亚洲视频播放在线观看 | 77777熟女视频在线观看 а天堂中文在线官网 | 中国女人内谢69xxxx | 少妇厨房愉情理9仑片视频 | 国产精品久久福利网站 | 国产精品久久精品三级 | 国产成人久久精品流白浆 | 免费无码一区二区三区蜜桃大 | 国产一精品一av一免费 | 久久久久国色av免费观看性色 | 久激情内射婷内射蜜桃人妖 | 300部国产真实乱 | 少妇高潮一区二区三区99 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 成人免费无码大片a毛片 | 中文字幕人妻无码一夲道 | 国产精品国产自线拍免费软件 | 亚洲男人av香蕉爽爽爽爽 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 精品国产一区二区三区四区 | 国产成人午夜福利在线播放 | 扒开双腿疯狂进出爽爽爽视频 | 少妇高潮一区二区三区99 | 97久久国产亚洲精品超碰热 | 国产成人精品优优av | 无码国产乱人伦偷精品视频 | 天天拍夜夜添久久精品 | 一本久道高清无码视频 | 国产在线精品一区二区高清不卡 | 人人爽人人澡人人高潮 | 窝窝午夜理论片影院 | 熟女少妇在线视频播放 | 狠狠色欧美亚洲狠狠色www | 特级做a爰片毛片免费69 | 成熟妇人a片免费看网站 | 国产精品美女久久久网av | 精品欧美一区二区三区久久久 | 永久免费观看美女裸体的网站 | 女人和拘做爰正片视频 | 人妻天天爽夜夜爽一区二区 | 国产精品国产自线拍免费软件 | 在教室伦流澡到高潮hnp视频 | 中文字幕无码视频专区 | 男女猛烈xx00免费视频试看 | 激情人妻另类人妻伦 | 男女作爱免费网站 | 狠狠色欧美亚洲狠狠色www | 精品国产一区二区三区四区 | aa片在线观看视频在线播放 | 亚洲国产精品毛片av不卡在线 | 国产精品久久久午夜夜伦鲁鲁 | 日韩视频 中文字幕 视频一区 | 欧美成人高清在线播放 | 成人一在线视频日韩国产 | 中文字幕人妻无码一夲道 | 国产亚洲日韩欧美另类第八页 | 亚洲国产精华液网站w | 久久久av男人的天堂 | 一本久道久久综合狠狠爱 | 六月丁香婷婷色狠狠久久 | 麻豆国产丝袜白领秘书在线观看 | 色 综合 欧美 亚洲 国产 | 无码免费一区二区三区 | 国产美女极度色诱视频www | 在线观看欧美一区二区三区 | 精品无码成人片一区二区98 | 欧美人与物videos另类 | 久久久久久久人妻无码中文字幕爆 | 狠狠亚洲超碰狼人久久 | 久久无码专区国产精品s | 精品国偷自产在线 | 1000部夫妻午夜免费 | 又粗又大又硬又长又爽 | a在线观看免费网站大全 | 国产高清不卡无码视频 | 日韩人妻无码中文字幕视频 | 久青草影院在线观看国产 | 亚洲人交乣女bbw | 欧美丰满熟妇xxxx性ppx人交 | 国产亚洲精品久久久ai换 | 中文字幕乱码亚洲无线三区 | 大肉大捧一进一出好爽视频 | а√资源新版在线天堂 | 亚洲日韩av一区二区三区四区 | 性色欲情网站iwww九文堂 | 蜜桃视频插满18在线观看 | 久久久久久久人妻无码中文字幕爆 | 久久精品人妻少妇一区二区三区 | 亚洲gv猛男gv无码男同 | 精品夜夜澡人妻无码av蜜桃 | 日韩精品a片一区二区三区妖精 | 精品一区二区三区波多野结衣 | 18禁黄网站男男禁片免费观看 | 亚洲国产精品成人久久蜜臀 | 成年女人永久免费看片 | 国产成人综合在线女婷五月99播放 | 亚洲爆乳大丰满无码专区 | 久久精品国产亚洲精品 | 精品无码一区二区三区的天堂 | 亲嘴扒胸摸屁股激烈网站 | 日韩精品一区二区av在线 | 欧美人与禽zoz0性伦交 | 三级4级全黄60分钟 | 国产97在线 | 亚洲 | 精品无码av一区二区三区 | 九九久久精品国产免费看小说 | 国产真实乱对白精彩久久 | 漂亮人妻洗澡被公强 日日躁 | 久久久久久久人妻无码中文字幕爆 | 国产色精品久久人妻 | 亚洲日韩一区二区 | 久久国产自偷自偷免费一区调 | 国产精品久久久久久无码 | 一二三四社区在线中文视频 | 东京一本一道一二三区 | 亚洲国产一区二区三区在线观看 | 免费无码一区二区三区蜜桃大 | 日本熟妇人妻xxxxx人hd | 理论片87福利理论电影 | 夜夜高潮次次欢爽av女 | 成人影院yy111111在线观看 | 人妻少妇被猛烈进入中文字幕 | 国产av一区二区精品久久凹凸 | 国产两女互慰高潮视频在线观看 | 蜜桃视频插满18在线观看 | 久久无码中文字幕免费影院蜜桃 | 国产高清不卡无码视频 | 野外少妇愉情中文字幕 | 成 人 网 站国产免费观看 | 成在人线av无码免观看麻豆 | 西西人体www44rt大胆高清 | 男女超爽视频免费播放 | 伊人久久大香线蕉av一区二区 | 国产精品永久免费视频 | 黑人玩弄人妻中文在线 | 欧美日韩视频无码一区二区三 | 亚洲欧洲中文日韩av乱码 | 国产激情无码一区二区app | 国产在线无码精品电影网 | 高清国产亚洲精品自在久久 | 无码人妻少妇伦在线电影 | 成人性做爰aaa片免费看不忠 | 97夜夜澡人人爽人人喊中国片 | 熟女体下毛毛黑森林 | 国产人妻久久精品二区三区老狼 | 午夜福利电影 | 牲欲强的熟妇农村老妇女视频 | 精品久久综合1区2区3区激情 | 国产特级毛片aaaaaa高潮流水 | 色综合久久久无码中文字幕 | 亚洲精品成人福利网站 | 亚洲国产日韩a在线播放 | 无码人妻精品一区二区三区下载 | 欧美自拍另类欧美综合图片区 | 野外少妇愉情中文字幕 | 人人妻人人澡人人爽欧美一区 | 白嫩日本少妇做爰 | 精品人妻人人做人人爽夜夜爽 | 高清国产亚洲精品自在久久 | 中文精品久久久久人妻不卡 | 欧美人妻一区二区三区 | 午夜免费福利小电影 | 国产精品久久久久久久影院 | 久久国语露脸国产精品电影 | 亚洲综合无码久久精品综合 | 国产亚洲精品久久久久久国模美 | 中文字幕无码视频专区 | 精品无码av一区二区三区 | 欧美激情一区二区三区成人 | 亚洲欧洲日本综合aⅴ在线 | 人人澡人摸人人添 | 国产精品无码一区二区桃花视频 | 狂野欧美性猛交免费视频 | 国产精品久久久久久无码 | 男女爱爱好爽视频免费看 | 久久午夜无码鲁丝片秋霞 | 国产女主播喷水视频在线观看 | 久青草影院在线观看国产 | 乱码av麻豆丝袜熟女系列 | 麻豆果冻传媒2021精品传媒一区下载 | 黑森林福利视频导航 | 国产精品igao视频网 | 国产精品久久久午夜夜伦鲁鲁 | 色综合久久88色综合天天 | 中文字幕中文有码在线 | 久久综合狠狠综合久久综合88 | 免费国产黄网站在线观看 | 中文字幕人成乱码熟女app | 亚洲中文字幕av在天堂 | 国产午夜无码视频在线观看 | 国产亚洲视频中文字幕97精品 | 又粗又大又硬毛片免费看 | 兔费看少妇性l交大片免费 | 人人妻人人澡人人爽欧美一区九九 | 在线播放免费人成毛片乱码 | 高清无码午夜福利视频 | 曰本女人与公拘交酡免费视频 | 日本爽爽爽爽爽爽在线观看免 | 好爽又高潮了毛片免费下载 | 精品久久8x国产免费观看 | 婷婷综合久久中文字幕蜜桃三电影 | 精品一区二区三区波多野结衣 | 亚洲精品一区二区三区大桥未久 | 国语精品一区二区三区 | 大肉大捧一进一出视频出来呀 | 久久久久成人精品免费播放动漫 | 在线亚洲高清揄拍自拍一品区 | 骚片av蜜桃精品一区 | 丰满肥臀大屁股熟妇激情视频 | 99久久久无码国产精品免费 | 国产精品无套呻吟在线 | 日本精品高清一区二区 | 色五月丁香五月综合五月 | 国产免费久久久久久无码 | 人妻互换免费中文字幕 | 日韩精品无码一区二区中文字幕 | 亚洲精品一区二区三区四区五区 | 成人欧美一区二区三区黑人 | 国产sm调教视频在线观看 | 中文字幕 亚洲精品 第1页 | 一二三四在线观看免费视频 | 亚洲精品无码国产 | 国产av一区二区三区最新精品 | 精品国产一区二区三区av 性色 | 性欧美牲交xxxxx视频 | 亚洲成在人网站无码天堂 | 日韩人妻少妇一区二区三区 | 99麻豆久久久国产精品免费 | 国产精品沙发午睡系列 | 亚洲精品无码人妻无码 | 亚洲成av人片在线观看无码不卡 | 一区二区三区高清视频一 | 国产偷抇久久精品a片69 | 鲁一鲁av2019在线 | 十八禁真人啪啪免费网站 | 国产亚洲精品久久久久久大师 | 国产麻豆精品一区二区三区v视界 | 亚洲成av人片在线观看无码不卡 | 一区二区三区乱码在线 | 欧洲 | 真人与拘做受免费视频一 | 国产av一区二区三区最新精品 | 九九久久精品国产免费看小说 | 超碰97人人做人人爱少妇 | 日本护士xxxxhd少妇 | 精品国偷自产在线 | 成人无码精品一区二区三区 | 国产福利视频一区二区 | 国产人妻精品午夜福利免费 | 一本久道久久综合狠狠爱 | 国产无av码在线观看 | 日本www一道久久久免费榴莲 | 中文字幕精品av一区二区五区 | 特大黑人娇小亚洲女 | 国产成人亚洲综合无码 | 人妻无码αv中文字幕久久琪琪布 | 欧美高清在线精品一区 | 狠狠色欧美亚洲狠狠色www | 久久久久久九九精品久 | 欧美黑人乱大交 | 一个人看的视频www在线 | 日韩人妻少妇一区二区三区 | 国产特级毛片aaaaaa高潮流水 | 一本色道久久综合亚洲精品不卡 | 风流少妇按摩来高潮 | 亚洲区欧美区综合区自拍区 | 无码人妻精品一区二区三区不卡 | 99久久无码一区人妻 | 亚洲国产精品久久人人爱 | 无码成人精品区在线观看 | 蜜桃av抽搐高潮一区二区 | 国产偷国产偷精品高清尤物 | 国产成人无码一二三区视频 | 亚洲国产综合无码一区 | 亚洲乱亚洲乱妇50p | 特级做a爰片毛片免费69 | 日本一本二本三区免费 | 免费播放一区二区三区 | 国产成人精品视频ⅴa片软件竹菊 | 性色欲情网站iwww九文堂 | 国产午夜亚洲精品不卡下载 | 国产免费久久精品国产传媒 | 人妻少妇被猛烈进入中文字幕 | 无码成人精品区在线观看 | 人人妻人人澡人人爽欧美一区九九 | 国产精品亚洲综合色区韩国 | 一本久久a久久精品亚洲 | 亚洲国产精品无码一区二区三区 | 六十路熟妇乱子伦 | 国产成人无码av在线影院 | 国产两女互慰高潮视频在线观看 | 国产精品美女久久久久av爽李琼 | 无遮挡国产高潮视频免费观看 | 性色欲网站人妻丰满中文久久不卡 | 蜜臀av无码人妻精品 | 无码精品人妻一区二区三区av | 午夜精品久久久内射近拍高清 | 永久免费观看国产裸体美女 | 亚洲日本在线电影 | 内射巨臀欧美在线视频 | 十八禁真人啪啪免费网站 | 亚洲欧美精品伊人久久 | 成人试看120秒体验区 | 国产精品亚洲综合色区韩国 | 麻豆精品国产精华精华液好用吗 | 白嫩日本少妇做爰 | 亚洲日韩av一区二区三区中文 | 久久久久亚洲精品中文字幕 | 亚洲欧美精品伊人久久 | 国产电影无码午夜在线播放 | 精品国产青草久久久久福利 | 亚洲娇小与黑人巨大交 | 99精品无人区乱码1区2区3区 | 四虎国产精品一区二区 | 国内揄拍国内精品少妇国语 | 亚洲精品国产第一综合99久久 | 中文字幕无码视频专区 | 亚洲无人区一区二区三区 | 国产后入清纯学生妹 | 亚洲s码欧洲m码国产av | 久久 国产 尿 小便 嘘嘘 | 爽爽影院免费观看 | 国产偷自视频区视频 | 精品国产一区二区三区四区 | 中文字幕精品av一区二区五区 | 亚欧洲精品在线视频免费观看 | 性生交大片免费看女人按摩摩 | 天天av天天av天天透 | 撕开奶罩揉吮奶头视频 | 欧美熟妇另类久久久久久不卡 | 亚洲va中文字幕无码久久不卡 | 人人妻人人藻人人爽欧美一区 | 亚洲成熟女人毛毛耸耸多 | 日韩av无码中文无码电影 | 青青草原综合久久大伊人精品 | 日本免费一区二区三区最新 | 精品无码一区二区三区爱欲 | 亚洲乱码日产精品bd | 人妻天天爽夜夜爽一区二区 | 国产在线精品一区二区高清不卡 | 人人妻人人澡人人爽欧美一区 | 亚洲精品综合一区二区三区在线 | 亚洲无人区午夜福利码高清完整版 | 精品国产麻豆免费人成网站 | 性色欲网站人妻丰满中文久久不卡 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 日本熟妇人妻xxxxx人hd | 久久99精品国产麻豆 | 伊人久久大香线蕉亚洲 | 牲欲强的熟妇农村老妇女视频 | 国产成人无码av在线影院 | 成在人线av无码免费 | 国产性生大片免费观看性 | 无码人中文字幕 | 国产高清av在线播放 | 国产精品办公室沙发 | 久久亚洲日韩精品一区二区三区 | 国产精品99爱免费视频 | 久久久久久国产精品无码下载 | 国产性生交xxxxx无码 | 日本熟妇人妻xxxxx人hd | 中文字幕日产无线码一区 | 精品久久久无码人妻字幂 | 狠狠cao日日穞夜夜穞av | 国模大胆一区二区三区 | 撕开奶罩揉吮奶头视频 | 亚洲成av人影院在线观看 | 亚洲成a人一区二区三区 | 色狠狠av一区二区三区 | 免费人成网站视频在线观看 | 国产精品爱久久久久久久 | 色综合久久网 | 国产亚洲日韩欧美另类第八页 | 亚洲成av人片天堂网无码】 | 国产成人综合色在线观看网站 | 性欧美熟妇videofreesex | 色老头在线一区二区三区 | 无码人妻久久一区二区三区不卡 | 国产精品久久久久久无码 | 国产成人无码区免费内射一片色欲 | 中文字幕无码乱人伦 | 国产人成高清在线视频99最全资源 | 久久久久se色偷偷亚洲精品av | 18禁黄网站男男禁片免费观看 | 久久精品一区二区三区四区 | 久久人人爽人人爽人人片ⅴ | 久久伊人色av天堂九九小黄鸭 | 久青草影院在线观看国产 | 亚洲国产精品成人久久蜜臀 | 国产精品久久久久久久影院 | 久久综合香蕉国产蜜臀av | 亚洲经典千人经典日产 | 日本一本二本三区免费 | 在线成人www免费观看视频 | 国产精品亚洲专区无码不卡 | 久久久久久国产精品无码下载 | 国产无遮挡吃胸膜奶免费看 | www国产亚洲精品久久久日本 | 亚洲呦女专区 | 天干天干啦夜天干天2017 | 永久免费观看美女裸体的网站 | 在线成人www免费观看视频 | 97无码免费人妻超级碰碰夜夜 | 丰满人妻一区二区三区免费视频 | 好屌草这里只有精品 | 日本欧美一区二区三区乱码 | 精品一区二区三区波多野结衣 | 中文无码精品a∨在线观看不卡 | 精品人妻中文字幕有码在线 | 久久无码中文字幕免费影院蜜桃 | 麻豆人妻少妇精品无码专区 | 午夜福利一区二区三区在线观看 | 亚洲国产av精品一区二区蜜芽 | 欧美黑人巨大xxxxx | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 亚洲爆乳大丰满无码专区 | 亚洲爆乳无码专区 | 日产精品高潮呻吟av久久 | 国产性生交xxxxx无码 | 精品偷自拍另类在线观看 | 午夜熟女插插xx免费视频 | 久久久精品国产sm最大网站 | 欧美国产亚洲日韩在线二区 | 国产成人无码av片在线观看不卡 | 性啪啪chinese东北女人 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 久久国语露脸国产精品电影 | 永久黄网站色视频免费直播 | 亚洲日韩av一区二区三区中文 | 性做久久久久久久久 | 亚洲一区二区三区播放 | 亚洲精品国偷拍自产在线观看蜜桃 | 色综合久久中文娱乐网 | 国产超级va在线观看视频 | 国产精品爱久久久久久久 | 无码播放一区二区三区 | 亚洲国产精品无码久久久久高潮 | 一个人看的视频www在线 | 亚洲精品成a人在线观看 | 熟妇女人妻丰满少妇中文字幕 | 色综合久久久久综合一本到桃花网 | 亚洲综合久久一区二区 | 亚洲国产av美女网站 | 国产人妻久久精品二区三区老狼 | 日本护士毛茸茸高潮 | 久久这里只有精品视频9 | 领导边摸边吃奶边做爽在线观看 | 红桃av一区二区三区在线无码av | 无套内谢的新婚少妇国语播放 | 人妻人人添人妻人人爱 | 在线精品亚洲一区二区 | 免费无码一区二区三区蜜桃大 | 亚洲小说图区综合在线 | 国产9 9在线 | 中文 | 内射老妇bbwx0c0ck | 国产午夜福利亚洲第一 | 国产亚洲视频中文字幕97精品 | 国产免费无码一区二区视频 | 日本熟妇人妻xxxxx人hd | 最近的中文字幕在线看视频 | 人人爽人人澡人人高潮 | 国产亚洲精品久久久久久 | 东京热无码av男人的天堂 | 天堂亚洲2017在线观看 | 51国偷自产一区二区三区 | 欧美 丝袜 自拍 制服 另类 | 性啪啪chinese东北女人 | 欧美亚洲国产一区二区三区 | 欧美亚洲日韩国产人成在线播放 | 亚洲国产欧美国产综合一区 | 国产亚洲欧美日韩亚洲中文色 | 国产精品高潮呻吟av久久 | 人人澡人人妻人人爽人人蜜桃 | 男女猛烈xx00免费视频试看 | 日韩精品无码免费一区二区三区 | 国产极品视觉盛宴 | 中文无码精品a∨在线观看不卡 | 日本护士毛茸茸高潮 | 九月婷婷人人澡人人添人人爽 | 无码成人精品区在线观看 | 少妇性荡欲午夜性开放视频剧场 | 大色综合色综合网站 | 久久久久国色av免费观看性色 | 免费观看激色视频网站 | 国产人妻精品一区二区三区 | 曰本女人与公拘交酡免费视频 | 奇米影视7777久久精品人人爽 | 少妇人妻偷人精品无码视频 | 亚洲精品久久久久中文第一幕 | 麻豆蜜桃av蜜臀av色欲av | 人妻有码中文字幕在线 | 亚无码乱人伦一区二区 | 久久久国产精品无码免费专区 | 网友自拍区视频精品 | 日本精品人妻无码免费大全 | 亚洲日韩中文字幕在线播放 | 国产精品国产三级国产专播 | 99久久久无码国产精品免费 | 色综合久久久无码中文字幕 | www国产亚洲精品久久网站 | 日本饥渴人妻欲求不满 | 精品国产av色一区二区深夜久久 | 国产电影无码午夜在线播放 | 无码午夜成人1000部免费视频 | 天干天干啦夜天干天2017 | 欧美人与动性行为视频 | 麻豆av传媒蜜桃天美传媒 | 国产成人人人97超碰超爽8 | 无套内谢的新婚少妇国语播放 | 一本久久伊人热热精品中文字幕 | 欧美人与禽zoz0性伦交 | 欧美 丝袜 自拍 制服 另类 | 樱花草在线社区www | 国产手机在线αⅴ片无码观看 | 亚洲娇小与黑人巨大交 | 国产精品18久久久久久麻辣 | 日韩视频 中文字幕 视频一区 | 人妻有码中文字幕在线 | 一二三四在线观看免费视频 | 无遮挡国产高潮视频免费观看 | 日韩av无码一区二区三区 | 亚洲成av人影院在线观看 | 天下第一社区视频www日本 | 97夜夜澡人人双人人人喊 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 国产精品va在线播放 | 美女张开腿让人桶 | 永久黄网站色视频免费直播 | 国内丰满熟女出轨videos | 丰腴饱满的极品熟妇 | 人妻少妇精品无码专区二区 | 香港三级日本三级妇三级 | 97夜夜澡人人爽人人喊中国片 | 亚洲阿v天堂在线 | 荡女精品导航 | 国产黑色丝袜在线播放 | 欧美熟妇另类久久久久久不卡 | 日韩人妻少妇一区二区三区 | 兔费看少妇性l交大片免费 | 国产日产欧产精品精品app | 动漫av一区二区在线观看 | 永久免费精品精品永久-夜色 | 一个人看的视频www在线 | 欧美阿v高清资源不卡在线播放 | 亚洲娇小与黑人巨大交 | 亚洲一区二区三区无码久久 | 久久99精品国产麻豆蜜芽 | 精品少妇爆乳无码av无码专区 | 久久精品国产一区二区三区肥胖 | 亚洲中文无码av永久不收费 | 精品熟女少妇av免费观看 | 亚洲精品综合五月久久小说 | 牲交欧美兽交欧美 | 精品无码一区二区三区的天堂 | 大胆欧美熟妇xx | 中文字幕无码免费久久99 | 在线播放亚洲第一字幕 | 麻豆国产97在线 | 欧洲 | 丰满妇女强制高潮18xxxx | 亚洲日韩av一区二区三区四区 | 波多野42部无码喷潮在线 | 日本成熟视频免费视频 | 亚洲日本va午夜在线电影 | 久久久国产精品无码免费专区 | 无遮挡啪啪摇乳动态图 | 国产区女主播在线观看 | 狠狠色噜噜狠狠狠7777奇米 | 99久久婷婷国产综合精品青草免费 | 人妻少妇精品无码专区动漫 | 欧美日韩一区二区免费视频 | 乱码午夜-极国产极内射 | 国产亚洲精品久久久久久久久动漫 | 日韩成人一区二区三区在线观看 | 99久久精品国产一区二区蜜芽 | 人人爽人人爽人人片av亚洲 | 欧美日韩久久久精品a片 | 天天摸天天透天天添 | 国产亚洲精品久久久久久久久动漫 | 亚洲天堂2017无码中文 | 亚洲成熟女人毛毛耸耸多 | 99视频精品全部免费免费观看 | 麻花豆传媒剧国产免费mv在线 | 亚洲自偷自偷在线制服 | 丰腴饱满的极品熟妇 | 亚洲精品久久久久久久久久久 | 无码一区二区三区在线 | 亚洲成a人一区二区三区 | 久久精品丝袜高跟鞋 | 国产色xx群视频射精 | 国产午夜无码精品免费看 | 日本一区二区三区免费高清 | 久久无码专区国产精品s | 亚洲国产日韩a在线播放 | 国产69精品久久久久app下载 | 久久无码专区国产精品s | 蜜臀aⅴ国产精品久久久国产老师 | 未满成年国产在线观看 | 亚洲日韩中文字幕在线播放 | 鲁大师影院在线观看 | а√资源新版在线天堂 | 精品日本一区二区三区在线观看 | 玩弄人妻少妇500系列视频 | 网友自拍区视频精品 | 乱中年女人伦av三区 | 亚洲日韩av一区二区三区四区 | 国产激情艳情在线看视频 | 日本精品人妻无码77777 天堂一区人妻无码 | 国语精品一区二区三区 | 国产办公室秘书无码精品99 | 欧美精品在线观看 | 国产精品无套呻吟在线 | 亚洲乱亚洲乱妇50p | 成人三级无码视频在线观看 | 国产一区二区三区精品视频 | 黄网在线观看免费网站 | 亚欧洲精品在线视频免费观看 | 亚洲日韩一区二区 | 一个人看的www免费视频在线观看 | 99er热精品视频 | 国产精品欧美成人 | 欧美性黑人极品hd | 午夜男女很黄的视频 | 成人欧美一区二区三区 | av无码久久久久不卡免费网站 | 色综合视频一区二区三区 | 亚洲精品一区二区三区四区五区 | 麻豆国产丝袜白领秘书在线观看 | 久久久久99精品成人片 | 精品一区二区不卡无码av | 激情亚洲一区国产精品 | 亚洲а∨天堂久久精品2021 | 日韩欧美中文字幕公布 | 免费国产成人高清在线观看网站 | 国产色精品久久人妻 | 久久久国产一区二区三区 | 欧洲熟妇精品视频 | 麻豆果冻传媒2021精品传媒一区下载 | 97无码免费人妻超级碰碰夜夜 | 蜜臀av无码人妻精品 | 国产乱人无码伦av在线a | 国产成人综合色在线观看网站 | 六十路熟妇乱子伦 | 亚洲最大成人网站 | 欧美大屁股xxxxhd黑色 | 国产乱人伦av在线无码 | 曰韩无码二三区中文字幕 | 无码国内精品人妻少妇 | 免费观看的无遮挡av | 久久人人爽人人人人片 | 最近免费中文字幕中文高清百度 | 国产成人人人97超碰超爽8 | 国产人妻精品一区二区三区 | 精品无码av一区二区三区 | 精品无人区无码乱码毛片国产 | 国产极品美女高潮无套在线观看 | 国产手机在线αⅴ片无码观看 | 精品国产一区av天美传媒 | 日本www一道久久久免费榴莲 | aa片在线观看视频在线播放 | 免费无码的av片在线观看 | 正在播放老肥熟妇露脸 | 国产特级毛片aaaaaa高潮流水 | 精品国产青草久久久久福利 | 无码av最新清无码专区吞精 | 成年女人永久免费看片 | 精品久久久无码人妻字幂 | 国产精品嫩草久久久久 | 日韩无套无码精品 | 大屁股大乳丰满人妻 | 激情亚洲一区国产精品 | 免费国产成人高清在线观看网站 | 狠狠色噜噜狠狠狠7777奇米 | 国产极品视觉盛宴 | 人妻少妇被猛烈进入中文字幕 | 成人欧美一区二区三区黑人 | 久久99久久99精品中文字幕 | 免费乱码人妻系列无码专区 | 精品一区二区三区波多野结衣 | 国产午夜福利亚洲第一 | 国产精品香蕉在线观看 | 国产精品久久久 | 夜夜影院未满十八勿进 | а√天堂www在线天堂小说 | 人人爽人人澡人人人妻 | 亚洲一区二区三区含羞草 | 午夜理论片yy44880影院 | 午夜无码人妻av大片色欲 | 久久久av男人的天堂 | 国产午夜无码视频在线观看 | 欧美放荡的少妇 | 在线观看国产午夜福利片 | 日本精品久久久久中文字幕 | 国产精品久免费的黄网站 | 精品久久久久久人妻无码中文字幕 | 99久久99久久免费精品蜜桃 | 人妻夜夜爽天天爽三区 | 99久久久国产精品无码免费 | 亚洲aⅴ无码成人网站国产app | 欧美日本免费一区二区三区 | 99久久99久久免费精品蜜桃 | 午夜精品一区二区三区在线观看 | 成人性做爰aaa片免费看不忠 | 日韩欧美成人免费观看 | 牲欲强的熟妇农村老妇女视频 | 亚洲国产精品一区二区美利坚 | 国色天香社区在线视频 | 婷婷五月综合激情中文字幕 | 国产精品国产三级国产专播 | 玩弄人妻少妇500系列视频 | 亚洲人成影院在线无码按摩店 | 色五月五月丁香亚洲综合网 | 99精品国产综合久久久久五月天 | 亲嘴扒胸摸屁股激烈网站 | 国产成人一区二区三区在线观看 | 国产午夜精品一区二区三区嫩草 | 日韩av无码中文无码电影 | 日本大乳高潮视频在线观看 | 国产97人人超碰caoprom | 亚洲欧洲中文日韩av乱码 | 99久久人妻精品免费一区 | 日欧一片内射va在线影院 | 欧美丰满老熟妇xxxxx性 | 国产精品资源一区二区 | 久久亚洲精品中文字幕无男同 | 亚洲精品综合五月久久小说 | 亚洲区小说区激情区图片区 | 色窝窝无码一区二区三区色欲 | 人妻体内射精一区二区三四 | 乱人伦人妻中文字幕无码久久网 | 大地资源网第二页免费观看 | 国产一区二区三区精品视频 | 人妻与老人中文字幕 | 精品一区二区三区无码免费视频 | 亚洲精品中文字幕 | 日韩精品成人一区二区三区 | 久久精品99久久香蕉国产色戒 | 黑人巨大精品欧美黑寡妇 | 黄网在线观看免费网站 | 亚洲欧美综合区丁香五月小说 | 国产精品亚洲专区无码不卡 | 久久久久久av无码免费看大片 | 国产人成高清在线视频99最全资源 | 丰满岳乱妇在线观看中字无码 | 国产精品二区一区二区aⅴ污介绍 | 中文字幕无码日韩专区 | 国产精品自产拍在线观看 | 色综合久久88色综合天天 | 人妻体内射精一区二区三四 | 波多野42部无码喷潮在线 | 国产亚洲精品久久久久久久 | 永久黄网站色视频免费直播 | 国产精品免费大片 | 国产亚洲精品久久久久久大师 | 午夜精品久久久久久久 | 色欲人妻aaaaaaa无码 | 中文字幕无码av激情不卡 | av人摸人人人澡人人超碰下载 | 18精品久久久无码午夜福利 | 丰腴饱满的极品熟妇 | 亚欧洲精品在线视频免费观看 | 无码午夜成人1000部免费视频 | 欧美午夜特黄aaaaaa片 | 国内丰满熟女出轨videos | 人人澡人人妻人人爽人人蜜桃 | 少妇愉情理伦片bd | 久久精品国产日本波多野结衣 | 国产亚洲美女精品久久久2020 | 欧美国产亚洲日韩在线二区 | 成年美女黄网站色大免费视频 | 欧美一区二区三区视频在线观看 | 国产精品内射视频免费 | 日韩精品无码一本二本三本色 | 67194成是人免费无码 | 国产精品igao视频网 | 久久久久亚洲精品中文字幕 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 国产精品va在线播放 | 1000部啪啪未满十八勿入下载 | 久久 国产 尿 小便 嘘嘘 | 国产激情艳情在线看视频 | 国产办公室秘书无码精品99 | 久在线观看福利视频 | 国产亚洲精品久久久久久久久动漫 | 中文字幕乱妇无码av在线 | 漂亮人妻洗澡被公强 日日躁 | 精品夜夜澡人妻无码av蜜桃 | 六十路熟妇乱子伦 | 国产午夜手机精彩视频 | 自拍偷自拍亚洲精品被多人伦好爽 | 国产偷国产偷精品高清尤物 | 亚洲人成无码网www | 欧美怡红院免费全部视频 | 日韩精品乱码av一区二区 | 西西人体www44rt大胆高清 | 久久精品国产日本波多野结衣 | 中文字幕乱码人妻二区三区 | 人妻少妇精品无码专区动漫 | 国产精品人人爽人人做我的可爱 | 欧美性生交xxxxx久久久 | 曰本女人与公拘交酡免费视频 | 大地资源中文第3页 | 中文精品久久久久人妻不卡 | 99麻豆久久久国产精品免费 | 福利一区二区三区视频在线观看 | 免费看男女做好爽好硬视频 | 宝宝好涨水快流出来免费视频 | 国产成人久久精品流白浆 | 无码av免费一区二区三区试看 | 久久午夜无码鲁丝片 | 亚洲精品一区二区三区大桥未久 | 国产成人久久精品流白浆 | 人妻少妇精品久久 | 欧美精品在线观看 | 亚洲国产高清在线观看视频 | 日日碰狠狠躁久久躁蜜桃 | 国产精品无码mv在线观看 | 亚洲精品欧美二区三区中文字幕 | 大地资源中文第3页 | 亚洲成av人片天堂网无码】 | 日韩精品久久久肉伦网站 | 国产成人精品视频ⅴa片软件竹菊 | 精品无码国产自产拍在线观看蜜 | 51国偷自产一区二区三区 | 日产精品99久久久久久 | 日韩av无码一区二区三区 | 国产午夜无码精品免费看 | 一个人看的www免费视频在线观看 | 粗大的内捧猛烈进出视频 | 亚洲第一无码av无码专区 | 久久综合网欧美色妞网 | 国色天香社区在线视频 | 亚洲精品鲁一鲁一区二区三区 | 国产三级精品三级男人的天堂 | 亚洲一区二区三区含羞草 | 乌克兰少妇性做爰 | 久久久久免费看成人影片 | 国产亚洲欧美日韩亚洲中文色 | 18黄暴禁片在线观看 | 国产成人精品无码播放 | 国产成人午夜福利在线播放 | 亚洲国产成人av在线观看 | 亚洲一区二区三区香蕉 | 成人综合网亚洲伊人 | 亚洲乱亚洲乱妇50p | 久久99精品国产.久久久久 | 我要看www免费看插插视频 | 国产精品欧美成人 | 未满成年国产在线观看 | 久久精品国产精品国产精品污 | 无码人妻少妇伦在线电影 | 亚洲最大成人网站 | 波多野结衣高清一区二区三区 | 成人三级无码视频在线观看 | 美女扒开屁股让男人桶 | 久久99精品久久久久久 | 中文字幕无码免费久久99 | 综合激情五月综合激情五月激情1 | 亚欧洲精品在线视频免费观看 | 夜先锋av资源网站 | 图片区 小说区 区 亚洲五月 | 装睡被陌生人摸出水好爽 | 国产亚洲精品久久久久久久 | 国产特级毛片aaaaaa高潮流水 | 乌克兰少妇性做爰 | 久久97精品久久久久久久不卡 | 精品无码av一区二区三区 | 无码中文字幕色专区 | 亚洲 激情 小说 另类 欧美 | 久久婷婷五月综合色国产香蕉 | 亚洲精品美女久久久久久久 | 少妇性l交大片 | 国产精品理论片在线观看 | 久久天天躁夜夜躁狠狠 | 日韩亚洲欧美中文高清在线 | 亚洲国产精品美女久久久久 | 欧美老妇与禽交 | 波多野结衣 黑人 | 大色综合色综合网站 | 国产精品无码久久av | 日韩少妇白浆无码系列 | 少妇性荡欲午夜性开放视频剧场 | 久久久精品欧美一区二区免费 | 人人妻人人澡人人爽精品欧美 | 国产成人精品无码播放 | 国产亚洲日韩欧美另类第八页 | 精品国产av色一区二区深夜久久 | 国产午夜精品一区二区三区嫩草 | 国产人成高清在线视频99最全资源 | 国产97在线 | 亚洲 | 色综合天天综合狠狠爱 | 日本丰满熟妇videos | 福利一区二区三区视频在线观看 | 精品一区二区不卡无码av | 欧美人与牲动交xxxx | 亚洲综合在线一区二区三区 | 老司机亚洲精品影院 | 蜜臀av无码人妻精品 | 日欧一片内射va在线影院 | 无码国产乱人伦偷精品视频 | 兔费看少妇性l交大片免费 | 高中生自慰www网站 | 国产乱子伦视频在线播放 | 人妻少妇被猛烈进入中文字幕 | 曰本女人与公拘交酡免费视频 | 清纯唯美经典一区二区 | www成人国产高清内射 | 狠狠cao日日穞夜夜穞av | 国色天香社区在线视频 | 亚洲国产欧美国产综合一区 | 国产成人精品久久亚洲高清不卡 | 国产香蕉尹人视频在线 | 一区二区传媒有限公司 | 欧美人与禽zoz0性伦交 | 色诱久久久久综合网ywww | 国产熟妇另类久久久久 | 亚洲日韩一区二区 | 亚洲精品久久久久avwww潮水 | 成人免费无码大片a毛片 | 日韩精品无码一本二本三本色 | 亚洲人亚洲人成电影网站色 | 精品人妻人人做人人爽 | 亚洲 欧美 激情 小说 另类 | 国产精品18久久久久久麻辣 | 国产亚洲精品精品国产亚洲综合 | 男女爱爱好爽视频免费看 | 人人妻在人人 | 国产精品久久久久久亚洲影视内衣 | 夜夜夜高潮夜夜爽夜夜爰爰 | 国产国产精品人在线视 | 伦伦影院午夜理论片 | 婷婷色婷婷开心五月四房播播 | 老熟妇仑乱视频一区二区 | 给我免费的视频在线观看 | 精品久久8x国产免费观看 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 任你躁在线精品免费 | 亚洲综合无码一区二区三区 | 88国产精品欧美一区二区三区 | 亚洲一区二区观看播放 | 中文亚洲成a人片在线观看 | 亚洲欧洲无卡二区视頻 | 精品久久久久久人妻无码中文字幕 | 久久精品人妻少妇一区二区三区 | 久热国产vs视频在线观看 | 色欲久久久天天天综合网精品 | 日韩精品a片一区二区三区妖精 | 国产激情无码一区二区 | 亚洲成av人综合在线观看 | 久久无码专区国产精品s | 精品国产国产综合精品 | 领导边摸边吃奶边做爽在线观看 | 天天拍夜夜添久久精品 | 亚洲日韩精品欧美一区二区 | 一本大道久久东京热无码av | 99久久精品午夜一区二区 | 国产人妻精品午夜福利免费 | 麻豆人妻少妇精品无码专区 | 特大黑人娇小亚洲女 | 波多野结衣av一区二区全免费观看 | 国产av无码专区亚洲awww | 伦伦影院午夜理论片 | 亚洲国产精品一区二区美利坚 | 国产人妻精品一区二区三区不卡 | 麻豆av传媒蜜桃天美传媒 | 亚洲色www成人永久网址 | 日本精品久久久久中文字幕 | 日本xxxx色视频在线观看免费 | 国产绳艺sm调教室论坛 | 少妇厨房愉情理9仑片视频 | 夜夜夜高潮夜夜爽夜夜爰爰 | 久久久www成人免费毛片 | 性史性农村dvd毛片 | 午夜不卡av免费 一本久久a久久精品vr综合 | www一区二区www免费 | 纯爱无遮挡h肉动漫在线播放 | 精品无码国产自产拍在线观看蜜 | 久久久精品国产sm最大网站 | 精品国偷自产在线视频 | 欧美老熟妇乱xxxxx | 免费无码午夜福利片69 | 天天拍夜夜添久久精品 | 一区二区三区乱码在线 | 欧洲 | 国产超碰人人爽人人做人人添 | 最新国产乱人伦偷精品免费网站 | 国产午夜精品一区二区三区嫩草 | 国产精品亚洲а∨无码播放麻豆 | 久久亚洲日韩精品一区二区三区 | 精品国产一区二区三区av 性色 | 国产午夜视频在线观看 | 国产综合久久久久鬼色 | av无码久久久久不卡免费网站 | 国产av久久久久精东av | 国产午夜无码精品免费看 | 亚拍精品一区二区三区探花 | 波多野结衣av一区二区全免费观看 | 中文字幕乱码中文乱码51精品 | 在教室伦流澡到高潮hnp视频 | 欧洲精品码一区二区三区免费看 | 久久久久久久人妻无码中文字幕爆 | 亚洲中文字幕乱码av波多ji | 国产乱人偷精品人妻a片 | 久久国产精品萌白酱免费 | 欧美兽交xxxx×视频 | 久久亚洲中文字幕精品一区 | 精品久久久中文字幕人妻 | 久久久av男人的天堂 | 欧美亚洲日韩国产人成在线播放 | www国产精品内射老师 | 红桃av一区二区三区在线无码av | 日本精品久久久久中文字幕 | 帮老师解开蕾丝奶罩吸乳网站 | 精品乱子伦一区二区三区 | 久久久中文久久久无码 | 丰满少妇女裸体bbw | 亚洲一区二区三区含羞草 | 久久国产精品二国产精品 | 欧美怡红院免费全部视频 | 欧美激情一区二区三区成人 | 5858s亚洲色大成网站www | 亚洲小说图区综合在线 | 一本色道久久综合狠狠躁 | 一本久道久久综合婷婷五月 | 国产精品久久久av久久久 | 国产成人午夜福利在线播放 | 狠狠综合久久久久综合网 | 亚洲色在线无码国产精品不卡 | 2020最新国产自产精品 | 女人被爽到呻吟gif动态图视看 | 永久黄网站色视频免费直播 | 日本一本二本三区免费 | 欧美熟妇另类久久久久久多毛 | 国产精品亚洲综合色区韩国 | 国产又粗又硬又大爽黄老大爷视 | 狠狠色欧美亚洲狠狠色www | 99久久精品午夜一区二区 | 免费无码的av片在线观看 | 中文字幕乱妇无码av在线 | 午夜精品久久久内射近拍高清 | 无码成人精品区在线观看 | 粉嫩少妇内射浓精videos | 国产人成高清在线视频99最全资源 | 国产免费久久精品国产传媒 | 高潮毛片无遮挡高清免费 | 牛和人交xxxx欧美 | 国产av无码专区亚洲a∨毛片 | 精品国产一区二区三区四区 | 粉嫩少妇内射浓精videos | 国产在线无码精品电影网 | 大地资源网第二页免费观看 | 纯爱无遮挡h肉动漫在线播放 | 国产激情无码一区二区 | 欧美自拍另类欧美综合图片区 | 色妞www精品免费视频 | 荡女精品导航 | 亚洲第一无码av无码专区 | 一个人看的www免费视频在线观看 | 人人妻人人澡人人爽欧美精品 | 国产免费久久久久久无码 | 亚洲精品国产品国语在线观看 | 日产精品99久久久久久 | 无套内谢老熟女 | 久久精品99久久香蕉国产色戒 | 国产成人无码一二三区视频 | 熟妇激情内射com | 日韩av无码一区二区三区不卡 | а√资源新版在线天堂 | 久久久久久av无码免费看大片 | 日产国产精品亚洲系列 | 激情内射亚州一区二区三区爱妻 | 综合激情五月综合激情五月激情1 | 欧美zoozzooz性欧美 | 亚洲精品午夜国产va久久成人 | 扒开双腿疯狂进出爽爽爽视频 | 岛国片人妻三上悠亚 | 欧美兽交xxxx×视频 | 中文字幕无码视频专区 | 久久久久久久女国产乱让韩 | 久久精品国产日本波多野结衣 | 国产成人精品无码播放 | 99久久亚洲精品无码毛片 | 97久久精品无码一区二区 | 伊在人天堂亚洲香蕉精品区 | 国产艳妇av在线观看果冻传媒 | 欧美日韩一区二区三区自拍 | 人妻少妇精品无码专区动漫 | 日本精品高清一区二区 | 樱花草在线社区www | av在线亚洲欧洲日产一区二区 | 亚洲精品久久久久中文第一幕 | 人妻互换免费中文字幕 | 美女张开腿让人桶 | 西西人体www44rt大胆高清 | 色综合久久中文娱乐网 | 无码吃奶揉捏奶头高潮视频 | 1000部夫妻午夜免费 | 在线a亚洲视频播放在线观看 | 图片小说视频一区二区 | 精品夜夜澡人妻无码av蜜桃 | 网友自拍区视频精品 | 日日摸日日碰夜夜爽av | 午夜精品久久久内射近拍高清 | 久久亚洲精品中文字幕无男同 | 国产亚洲美女精品久久久2020 | 亚洲男女内射在线播放 | 国产人妻精品一区二区三区 | 免费无码的av片在线观看 | 乱人伦人妻中文字幕无码久久网 | 国产精品美女久久久久av爽李琼 | 狂野欧美激情性xxxx | 人妻天天爽夜夜爽一区二区 | 两性色午夜免费视频 | 久久精品国产99久久6动漫 | 亚洲s码欧洲m码国产av | 午夜精品久久久久久久久 | 98国产精品综合一区二区三区 | 人人妻人人藻人人爽欧美一区 | 老司机亚洲精品影院 | 四虎国产精品免费久久 | 精品国产一区二区三区av 性色 | 久久人人97超碰a片精品 | 久久精品女人的天堂av | 国产激情精品一区二区三区 | 亚洲高清偷拍一区二区三区 | 国产精品美女久久久 | 欧美精品无码一区二区三区 | 国产欧美熟妇另类久久久 | 妺妺窝人体色www在线小说 | 亚洲精品中文字幕久久久久 | 久久综合九色综合欧美狠狠 | 国产另类ts人妖一区二区 | 最近免费中文字幕中文高清百度 | 国产精品对白交换视频 | 麻豆果冻传媒2021精品传媒一区下载 | 国产午夜福利亚洲第一 | 国产亚洲精品久久久久久久久动漫 | 丰满少妇人妻久久久久久 | 大地资源中文第3页 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 国内精品人妻无码久久久影院蜜桃 | 丁香花在线影院观看在线播放 | 亚洲熟妇色xxxxx欧美老妇 | 日韩 欧美 动漫 国产 制服 | 人人妻人人藻人人爽欧美一区 | 妺妺窝人体色www婷婷 | 性生交大片免费看女人按摩摩 | 精品夜夜澡人妻无码av蜜桃 | 欧美人与牲动交xxxx | 精品欧洲av无码一区二区三区 | 美女毛片一区二区三区四区 | 国产色在线 | 国产 | 亚洲娇小与黑人巨大交 | 九一九色国产 | 精品久久久无码中文字幕 | a在线观看免费网站大全 | а√资源新版在线天堂 | 欧美性黑人极品hd | 桃花色综合影院 | 女人和拘做爰正片视频 | 女高中生第一次破苞av | 成人无码精品1区2区3区免费看 | 成人欧美一区二区三区黑人免费 | 国产农村乱对白刺激视频 | 精品久久综合1区2区3区激情 | 亚洲成av人片在线观看无码不卡 | 日韩av无码一区二区三区 | 熟女体下毛毛黑森林 | 亚洲乱码中文字幕在线 | 三上悠亚人妻中文字幕在线 | 在线播放免费人成毛片乱码 | 鲁大师影院在线观看 | 影音先锋中文字幕无码 | 国产sm调教视频在线观看 | 性做久久久久久久免费看 | 精品 日韩 国产 欧美 视频 | 亚洲人成网站在线播放942 | 亚洲大尺度无码无码专区 | 澳门永久av免费网站 | 理论片87福利理论电影 | 国产午夜视频在线观看 | 久久久久av无码免费网 | 久久久久久久人妻无码中文字幕爆 | 国产在线aaa片一区二区99 | 激情内射日本一区二区三区 | 国产精品久久久一区二区三区 | 日韩精品无码一本二本三本色 | 熟妇人妻无乱码中文字幕 | 九九久久精品国产免费看小说 | 福利一区二区三区视频在线观看 | 精品国精品国产自在久国产87 | 高中生自慰www网站 | 中文字幕乱码人妻无码久久 | 久久综合给合久久狠狠狠97色 | 老子影院午夜伦不卡 | 久久久久免费精品国产 | 男女猛烈xx00免费视频试看 | 中文无码精品a∨在线观看不卡 | 无码人妻精品一区二区三区下载 | 国语精品一区二区三区 | 狠狠色丁香久久婷婷综合五月 | 亚洲欧美日韩综合久久久 | 国产女主播喷水视频在线观看 | 久久99久久99精品中文字幕 | 强伦人妻一区二区三区视频18 | a片免费视频在线观看 |