国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

如何通過COM接口得到實現(xiàn)該接口的對象實例

2019-11-18 18:13:40
字體:
供稿:網(wǎng)友
如何通過COM接口得到實現(xiàn)該接口的對象實例

問題由來

我的程序為一個基于COM的插件結(jié)構(gòu),框架需要向插件傳遞一個IResource接口。IResource
需要根據(jù)不同的插件傳遞不同的內(nèi)容。
接口定義
IResource = Interface(IDispatch)
  Function GetPath: String; safecall;
End;
實現(xiàn)類
TResource = TClass(TAutoObject, IResource)
PRotected
  Function GetPath: String; SafeCall;
Public
  Path: String;
End;

Function GetPath: String;
Begin
  Result:= Path;
End;

調(diào)用部分:
Var
  Resource: IResource;
  ResourceObj: TResource;
Begin
  Resource:= CreateComObject(CLASS_Resource) As IResource;
  //想通過強制轉(zhuǎn)換得到TResource;結(jié)果失敗了:(
  ResourceObj:= TResource(Resource);
  ResourceObj.Path:= '這里設置不同的值';
End;

請問:
    如何通過IResource得到TResource,從而達到設置PATH值的目的?

目前我采用的方案是再定義一個ISetValue的接口修改里面的PATH屬性,感覺用起來比較
麻煩。

問題的延伸

如果從解決問題出發(fā),通過定義配置接口,如:
IObjRef = Interface
  function GetObjRef: TObject; safecall;
end;
這樣得到對象,再對PATH賦值,這樣做在沒有破壞COM的封裝,實現(xiàn)起來也比較清晰。問題至此基本解決。

但本著從分析DELPHI對象與接口之間的關(guān)系的出發(fā)點,我們還是繼續(xù)標題中提出的問題:

如何通過COM接口得到實現(xiàn)該接口的對象實例 ?

SAVETIME的線索

http://www.delphibbs.com/delphibbs/dispq.asp?lid=2433841  
SAVETIME的這篇文章中提到了關(guān)于DELPHI中對象與接口之間在編譯器實現(xiàn)的內(nèi)存空間情況:
----------------|-----------------|----------|--------------|-----------------
 對象/接口指針   | 對象內(nèi)存空間    |          | 虛方法表     |
 ----------------|-----------------|----------|--------------|-----------------
 MyObject    ->  | VMTptr        00|--------->| VirtA      00|
                 | FRefCount     04|          | VirtB      04|
 MyIntf      ->  | IInterface    08|----|          
                 | FFieldA       0C|    |           | IInterface    跳轉(zhuǎn)表   |
                 | FFieldB       10|    |---------> | addr of QueryInterface |
 MyIntfB     ->  | IIntfB        14|---------|      | addr of _AddRef        |
 MyIntfA     ->  | IIntfA        18|--|      |      | addr of _Release       |
                                      |      |
                                      |      |      | IIntfB        跳轉(zhuǎn)表   |
                                      |      |----> | addr of ProcB          |
                                      |             | addr of VirtB          |
                                      |
                                      |             | IIntfA        跳轉(zhuǎn)表   |
                                      |-----------> | addr of ProcA          |
                                                    | addr of VirtA          |
 ------------------------------------------------------------------------------
一個對象在調(diào)用類的成員函數(shù)的時候,比如執(zhí)行 MyObject.ProcA,會隱含傳遞一個 Self 指針給這個成員函數(shù):MyObject.ProcA(Self)。Self 就是對象數(shù)據(jù)空間的地址。那么編譯器如何知道 Self 指針?原來對象指針 MyObject 指向的地址就是 Self,編譯器直接取出 MyObject^ 就可以作為 Self。

在以接口的方式調(diào)用成員函數(shù)的時候,比如 MyIntfA.ProcA,這時編譯器不知道 MyIntfA 到底指向哪種類型(class)的對象,無法知道 MyIntfA 與 Self 之間的距離(實際上,在上面的例子中 Delphi 編譯器知道 MyIntfA 與 Self 之間的距離,只是為了與 COM 的二進制格式兼容,使其它語言也能夠使用接口指針調(diào)用接口成員函數(shù),必須使用后期的 Self 指針修正),編譯器直接把 MyIntfA 指向的地址設置為 Self。從上圖可以看到,MyIntfA 指向 MyObject 對象空間中 $18 偏移地址。這時的 Self 指針當然是錯誤的,編譯器不能直接調(diào)用 TMyObject.ProcA,而是調(diào)用 IIntfA 的“接口跳轉(zhuǎn)表”中的 ProcA。“接口跳轉(zhuǎn)表”中的 ProcA 的內(nèi)容就是對 Self 指針進行修正(Self - $18),然后再調(diào)用 TMyObject.ProcA,這時就是正確調(diào)用對象的成員函數(shù)了。由于每個類實現(xiàn)接口的順序不一定相同,因此對于相同的接口在不同的類中實現(xiàn),就有不同的接口跳轉(zhuǎn)表(當然,可能編輯器能夠聰明地檢查到一些類的“接口跳轉(zhuǎn)表”偏移量相同,也可以共享使用)。

通過這里得到了解決問題的關(guān)鍵,如果能得到接口的偏移地址,那么就可以得到對象實例

呵呵~~看到曙光了,加油!

尋找偏移地址

眾所周知,所有的DELPHI對象都是從TObject繼承下來的,而創(chuàng)建對象也是通過
class function TObject.InitInstance(Instance: Pointer): TObject;
來分配內(nèi)存空間的,仔細分析這段代碼。
class function TObject.InitInstance(Instance: Pointer): TObject;
{$IFDEF PUREPASCAL}
var
  IntfTable: PInterfaceTable;
  ClassPtr: TClass;
  I: Integer;
begin
  FillChar(Instance^, InstanceSize, 0);
  PInteger(Instance)^ := Integer(Self);
  ClassPtr := Self;
  while ClassPtr <> nil do
  begin
    IntfTable := ClassPtr.GetInterfaceTable;
    if IntfTable <> nil then
      for I := 0 to IntfTable.EntryCount-1 do
  with IntfTable.Entries[I] do
  begin
    if VTable <> nil then
      //就是它了IOffset,它就是接口的偏移地址
      PInteger(@PChar(Instance)[IOffset])^ := Integer(VTable);
  end;
    ClassPtr := ClassPtr.ClassParent;
  end;
  Result := Instance;
end;

找到了IOffset,在跟蹤發(fā)現(xiàn)它屬于 接口標識的接口項(PInterfaceEntry)
  PInterfaceEntry = ^TInterfaceEntry;
  TInterfaceEntry = packed record
    IID: TGUID;
    VTable: Pointer;
    IOffset: Integer;
    ImplGetter: Integer;
  end;

問題出來了,得到PInterfaceEntry 就得到了一切

輕松得到PInterfaceEntry

Var
  eResourceObj: TResource;
  eEntry: PInterfaceEntry;
  eAutoObjFactory: TAutoObjectFactory;
Begin
  eResource:= CreateComObject(CLASS_Resource) as IResource;
  //得到類工廠
  eAutoObjFactory:= TAutoObjectFactory(ComClassManager.GetFactoryFromClassID(CLASS_Resource));
  //得到接口標識的接口項
  eEntry:= eAutoObjFactory.DispIntfEntry;
  //IOffset為接口的偏移地址,eResource減去IOffset所得到的地址就是對象實例
  eResourceObj:= TResource(Integer(eResource)-eEntry.IOffset);
  eResourceObj.Path:= '這里設置不同的值'';
End;  
 

結(jié)論

費勁周折得來的結(jié)果,可能對整個問題并沒有太多的意義
但是,過程確實非常有意義,通過這個過程讓我對DELPHI對象和接口的實質(zhì)有了更深層次的了解。
 

上一篇:Tchart分析報告

下一篇:如何接受消息,中斷正在處理的過程

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
學習交流
熱門圖片

新聞熱點

疑難解答

圖片精選

網(wǎng)友關(guān)注

主站蜘蛛池模板: 合水县| 新乡县| 龙陵县| 松滋市| 灵石县| 郯城县| 筠连县| 林芝县| 宜丰县| 保山市| 靖远县| 岗巴县| 巍山| 长武县| 宜良县| 建宁县| 平南县| 石家庄市| 霍林郭勒市| 开平市| 永仁县| 石家庄市| 治县。| 监利县| 红桥区| 驻马店市| 上思县| 兰坪| 卓尼县| 龙游县| 墨玉县| 南川市| 高尔夫| 屏边| 石阡县| 长垣县| 德化县| 祁连县| 石家庄市| 自治县| 徐州市|