精品一区二区三区影院在线午夜_天天躁日日躁狠狠躁AV麻豆_国产午夜福利短视频_中文字幕乱偷无码AV先锋蜜桃_久久精品一二区东京热_国产成人亚洲日韩欧美久久久,国产成人精品久久一区二区三区

Delphi 的接口機制——接口操作的編譯器實(shí)現過(guò)程,1

學(xué)習COM編程技術(shù)也快有半個(gè)月了,這期間看了很多資料和別人的程序源碼,也嘗試了用delphi、C++、C#編寫(xiě)COM程序,個(gè)人感覺(jué)Delphi是最好上手的。C++的模版生成的代碼太過(guò)復雜繁瑣,大量使用編譯宏替代函數代碼,讓初學(xué)者知其然而不知其所以然;C#封裝過(guò)度,COM編程注定是要與操作系統頻繁打交道的,需要調用大量API函數和使用大量系統預定義的常量與類(lèi)型(結構體),這些在C#中都需手工聲明,不夠簡(jiǎn)便;Delphi就簡(jiǎn)單多了,通過(guò)模版創(chuàng )建的工程代碼關(guān)系結構非常清晰,而且其能非常容易使用API函數和系統預定義的常量和類(lèi)型(只需引用先關(guān)單元即可),但在使用過(guò)程中也發(fā)現了一些缺點(diǎn)。【注1】

(1)有些類(lèi)型(結構體)的成員類(lèi)型與C++中的不是等效對應關(guān)系,如SHFileOperation函數的參數類(lèi)型是SHFILEOPSTRUCT結構體,delphi中它的兩個(gè)路徑成員被定義成PWideChar型,與C++的LPCTSTR不一致,PWideChar是以空字符(\0)結尾的,致使這兩個(gè)成員不能包含多個(gè)文件路徑。【注2】

(2)有些接口的函數參數定義不一致,如IContextMenu.InvokeCommand函數參數在Delphi中是CMINVOKECOMMANDINFO類(lèi)型,在c++中是LPCMINVOKECOMMANDINFO型 ,致使該接口函數不能使用擴展的CMINVOKECOMMANDINFOEX型參數。【注3】

Delphi操作COM的另一便處在于他的接口的引用計數管理,這為我們寫(xiě)程序解決了一大麻煩:不用管接口的AddRef和Release了,直接把接口當“接口指針變量”(【注4】)使用,編譯器會(huì )執行一些特殊的代碼自動(dòng)維護接口的引用計數。當然,這也會(huì )帶來(lái)另一個(gè)問(wèn)題,接口相當于“變量”一樣使用,這就涉及到“變量”的生命周期問(wèn)題,當把這樣一個(gè)局部“變量”通過(guò)強制類(lèi)型轉換(【注5】)給一個(gè)全局變量時(shí),待之后轉換回來(lái)時(shí)將引發(fā)錯誤。因為局部“變量”生命已結束,要被清理,其所代表的接口被減少引用計數釋放了,如果人為讓“變量”AddRef一次,就能消除這個(gè)錯誤。

關(guān)于Delphi的接口引用計數管理,在網(wǎng)上看到的一篇介紹的文章,查很久了它的出處,目前已知最早是SaveTime于2004年2月3日發(fā)表于大富翁論壇。【注6】

下面將它整理了一下,以便加深對delphi對接口引用計數的理解。

接口指針變量賦值

接口是生存期自管理對象,即使是局部接口指針變量,也總是被初始化為 nil。接口被初始化為nil是很重要的,從下文中Delphi生成維護接口引用計數的代碼時(shí)可以看到這一點(diǎn)。

[delphi]view plaincopyprint?

  1. var
  2. MyObject: TMyObject;
  3. MyIntf, MyIntf2: IInterface;
  4. begin
  5. MyObject := TMyObject.Create; // 創(chuàng )建 TMyObject 對象
  6. MyIntf := MyObject; // 將接口指向 MyObject 對象
  7. MyIntf2 := MyIntf; // 接口指針的賦值
  8. end;

當接口與一個(gè)對象連接時(shí),編譯器會(huì )執行一些特殊的代碼維護接口對象的引用計數。例如以上代碼,當執行到MyIntf :=MyObject 語(yǔ)句時(shí),編譯器的實(shí)現是:

1. 如果 MyObject <> nil,則設置一臨時(shí)接口指針 P 指向 MyObject 對象內存空間中的“接口跳轉表”指針(后面會(huì )分析“接口跳轉表”),否則 P := nil;

2. 執行 System.pas 中的 _IntfCopy(MyIntf, P) 操作,進(jìn)行引用計數管理。

[delphi]view plaincopyprint?

  1. procedure _IntfCopy(var Dest: IInterface; const Source: IInterface);
  2. var
  3. P: Pointer;
  4. begin
  5. P := Pointer(Dest);
  6. if Source <> nil then
  7. Source._AddRef;
  8. Pointer(Dest) := Pointer(Source);
  9. if P <> nil then
  10. IInterface(P)._Release;
  11. end;

函數_IntfCopy 的代碼比較簡(jiǎn)單,就是增加 Source 接口對象的引用計數,減少被賦值的接口對象的引用計數,最后把源接口賦值至目標接口。

對于兩個(gè)接口的賦值的情況,如MyIntf2 := MyIntf,這時(shí)比 MyIntf := MyObject 的情況要簡(jiǎn)單一些,編譯器不需要進(jìn)行對象到接口的轉換工作,這時(shí)真正執行的代碼是:_IntfCopy(MyIntf2, MyIntf)。

接口指針變量的清除工作

在一個(gè)過(guò)程(procedure/function)執行結束時(shí),編譯器會(huì )生成代碼減少接口指針變量的引用計數。編譯器使用接口指針為參數調用 _IntfClear 函數,_IntfClear 函數的作用是減少接口對象的引用計數并設置接口為 nil :

[delphi]view plaincopyprint?

  1. function _IntfClear(var Dest: IInterface): Pointer;
  2. var
  3. P:Pointer;
  4. begin
  5. Result := @Dest;
  6. if Dest <> nil then
  7. begin
  8. P := Pointer(Dest);
  9. Pointer(Dest) := nil;
  10. IInterface(P)._Release;
  11. end;
  12. end;

通過(guò)對以上代碼及分析,我們可以總結過(guò)程(procedure/function)中的接口引用計數使用規則:

1. 一般不需要使用 _AddRef/_Release 函數設置接口引用計數;

2. 可以將接口賦值為接口或對象,Delphi 自動(dòng)處理源/目標接口對象的引用計數;

3. 如果要提前釋放接口對象,可以設置接口指針為 nil,但不要調用 _Release。因為 _Release 不會(huì )把接口指針變量設置為 nil,最后 Delphi 自動(dòng)調用 _IntfClear時(shí)會(huì )出錯。

對于全局接口指針變量,在接口指針變量被賦值時(shí)增加對象的引用計數,在程序退出之前編譯器自動(dòng)調用 _IntfClear 函數減少引用計數以清除對象。



接口指針作為參數

1. 以var 或const 方式傳遞接口指針時(shí),像普通的參數傳遞一樣。

2. 以out 方式傳遞接口指針時(shí),編譯器會(huì )先調用_IntfClear 函數減少引用計數,清除接口指針為 nil 。(out 也是以引用方式傳送參數)。

3. 以傳值方式傳遞接口指針時(shí),編譯器會(huì )在參數被使用之前調用_IntfAddRef 函數增加引用計數,在過(guò)程結束之前調用_IntfClear 函數減少引用計數。

[delphi]view plaincopyprint?

  1. { System.pas }
  2. procedure _IntfAddRef(const Dest: IInterface);
  3. begin
  4. if Dest <> nil then Dest._AddRef;
  5. end;

為什么以傳值方式要特別處理引用計數呢?因為復制了接口指針。

下一節介紹接口對象的內存空間


1 我用的是Delphi2010,更新的XE、XE2版本可能已更正了這些問(wèn)題,在此舉例說(shuō)明而已。

2 有關(guān)結構體SHFILEOPSTRUCT及其兩個(gè)路徑成員的詳細介紹請參見(jiàn)http://blog.csdn.net/tht2009/article/details/6753706http://msdn.microsoft.com/en-us/library/bb759795(VS.85).aspx

3 有關(guān)接口函數InvokeCommand的詳細介紹請參見(jiàn)http://msdn.microsoft.com/en-us/library/bb776096(VS.85).aspx

4 我也不知嚴格上能否這樣稱(chēng)呼,姑且這樣類(lèi)比吧!

5 如通過(guò)Pointer(IShellFolder)將一個(gè)局部聲明的IShellFolder接口保存到一個(gè)Pointer型的變量Data中,通過(guò)Data:=Pointer(IShellFolder)不會(huì )增加IShellFolder接口對象的引用。實(shí)際中很少遇到這種情況,我也是在無(wú)意中發(fā)現這個(gè)問(wèn)題的。

6 請見(jiàn)http://blog.csdn.net/huangsn10/article/details/6112546,由于大富翁論壇好像已關(guān)閉了,所以真正出處已無(wú)從考證。

http://blog.csdn.net/tht2009/article/details/6767435

皮山县| 博爱县| 麟游县| 崇明县| 景谷| 雷山县| 麻阳| 铜陵市| 庆阳市| 东丰县| 中山市| 凌海市| 江源县| 威远县| 乡宁县| 始兴县| 老河口市| 清新县| 宁化县| 新邵县| 漳州市| 蒙城县| 柯坪县| 靖远县| 南陵县| 三穗县| 车险| 合山市| 宁蒗| 西青区| 十堰市| 新竹市| 尉犁县| 孟村| 嘉荫县| 济宁市| 信丰县| 华亭县| 观塘区| 比如县| 堆龙德庆县|