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

首頁 > 編程 > .NET > 正文

在.Net 中枚舉COM對象的方法和屬性名稱

2024-07-10 13:04:04
字體:
來源:轉載
供稿:網友
在.net 中枚舉com對象的方法和屬性名稱

author:zee

恩,以前滿世界問過這個問題,沒有人理偶的說,還是自己動手搞定比較好。

一般來說,一個com對象在提供的時候,通常還會提供一個類型庫,在其中定義了com對象的所有方法名稱、參數名稱、屬性名稱等等信息。我們要做的就是從類型庫中取出這些信息。
當然,某些只供c++程序員使用的com對象沒有類型庫,而代之以c++的頭文件和/或idl文件,對這種情況,一般沒有辦法在程序中枚舉出對象的方法屬性:畢竟去找c++頭文件不太現實,何況在非開發環境下,根本就沒有頭文件的說。
因此,我們將討論當com對象存在typelib的情況下,枚舉方法/屬性名稱的問題。
從com對象定位到typelib
在一般情況下,com對象的typelib信息存儲在注冊表中:在hk_classroot/clsid/{classid}/的注冊表項下,有一個名為typelib的子項,其中定義了這個com對象類型庫的id;而在hk_classroot/typelib 注冊表項下,列舉了系統中所有typelib。
看看我們首先要做什么:從progid 取得 classid,這個工作可以通過調用com 基礎庫的 clsidfromprogid 函數來完成,在platform sdk中,該函數的定義如下:
hresult clsidfromprogid(
  lpcolestr lpszprogid,
  lpclsid pclsid

);
為了在.net中使用這個函數,我們用dllimport attribute 把這個函數引入.net 中:
class unsafenativemethods{
[dllimport("ole32.dll",charset=charset.unicode,preservesig=false)]
public static extern void clsidfromprogid([in,marshalas(unmanagedtype.bstr)] string lpszprogid,[out]out guid pclsid);
………
然后,我們可以在.net 中調用這個函數取得classid了:
guid clsid;
unsafenativemethods.clsidfromprogid(progid,out clsid);
ok, 升級寶物class id 入手,level up!strength + 3, life + 5,必殺技 dll import 習得。 :) 下一個任務:取得typelib。
l         取得typelib。
為訪問typelib,com 提供了二個接口:itypelib 和 itypeinfo,其中itypelib 提供對 typelib 的訪問,而itypeinfo 則表示typelib中定義的某一項itypeinfo。
要獲得itypeinfo,com有二個函數可以做這件事情:loadtypelib 和 loadregtypelib。其中 loadtypelib 需要 typelib 文件的路徑作為參數,而loadregtypelib 則根據typelib的typelib id和typelib的版本號取得 itypelib。在這里,我們用loadregtypelib來取得itypelib 接口。
先來準備需要的參數:typelibid和typelib的版本號,這些信息需要從注冊表里得到:
registrykey regkey = registry.classesroot;
regkey = regkey.opensubkey("clsid//{" + clsid.tostring() + "}//typelib");
guid typelibid = new guid(regkey.getvalue("").tostring());
//get typelib versions
short imajorver,iminusver;
regkey = microsoft.win32.registry.classesroot;
regkey = regkey.opensubkey("typelib//{" + typelibid.tostring() + "}");
string[] arytemp = regkey.getsubkeynames();
string sversion = arytemp[0];
arytemp = sversion.split('.');
imajorver = short.parse(arytemp[0],system.globalization.numberstyles.allowhexspecifier);
iminusver = short.parse(arytemp[1] ,system.globalization.numberstyles.allowhexspecifier);
這里要注意一點:在注冊表里記錄的typelib版本號是以十六進制格式表示的,運氣好的話,你會發現類似”1.a”之類的版本號,所以我們最好把它們看成16進制來轉換。
現在可以調用loadregtypelib 了,和clsidfromprogid一樣,先import進來:
這是loadregtypelib 的函數原型:
hresult loadregtypelib( 
  refguid  rguid,             
  unsigned short  wvermajor,  
  unsigned short  wverminor,  
  lcid  lcid,                 
  itypelib far* far*  pptlib  

);
恩,在.net里,這個函數是這個樣子的:
[dllimport("oleaut32.dll",charset=charset.unicode,preservesig=false)]
[lcidconversion(3)]
public static extern ucomitypelib loadregtypelib(ref guid rguid, [in,marshalas(unmanagedtype.u2)]short wvermajor, [in,marshalas(unmanagedtype.u2)]short wverminor);
哈,一個小小的技巧:在.net 的定義里,偶沒有定義原型里 lcid 這個參數,這是因為偶應用了lcidconversionattribute,這個attribute 意思就是說這個方法需要一個lcid做參數,參數的位置嘛:[lcidconversion(3)]——第三個參數。這樣在調用這個方法的時候,.net 的封送拆收器將自動提供 lcid 參數。不錯把:)
另外,在.net framwork的system.runtime.interopservices 命名空間里,定義了一些常用com interface的.net托管定義,雖然不多,但幸運的是我們要用到的itypelib和 itypeinfo這二個接口都有,也就是system.runtime.interopservice.ucomitypelibsystem.runtime.interopservice.ucomitypeinfo。這就省下了我們自己定義接口的工作。
好了,接下來的事情狠簡單了:
ucomitypelib typelib;
typelib = unsafenativemethods.loadregtypelib(ref typelibid,imajorver,iminusver);
bingo!mission complete!只剩下最后一個任務:定位到對應我們的com對象的itypeinfo,并從中取出我們需要的信息。
itypeinfo接口的getitypeinfo方法和getitypeinfocount方法一起提供了遍歷typelib中所有itypeinfo的能力,不過既然我們手上有com對象的classid,利用getitypeinfoofguid 方法就可以獲得com對象的itypeinfo了。
ucomitypeinfo itypeinfo;
typelib.getitypeinfoofguid(ref clsid,out itypeinfo);
拿到itypeinfo之后,首先我們需要看看這個itypeinfo里有多少方法/屬性,這需要我們調用它的gettypeattr 方法獲得typeattr結構。
typeattr typeattr;
intptr p_typeattr = intptr.zero;
itypeinfo.gettypeattr(out p_typeattr);
typeattr = (typeattr)marshal.ptrtostructure(p_typeattr,typeof(typeattr));
獲得typeattr結構有那么一點點麻煩,因為 .net的不支持非托管簽名的 typeattr** 參數,所以只有使用引用 intptr 參數定義 gettypeattr。然后我們需要用marshal.ptrtostructure將數據從非托管內存塊封送到托管對象。在typeattr結構中,cfuns字段表示當前trpeinfo描述的函數數目,而每個函數的描述則是通過itypeinfo的getfuncdesc方法取得的funcdesc結構描述的。
if(typeattr.cfuncs > 0)
{
for(int i=0;i<typeattr.cfuncs;++i)
{
//get funcdesc struct
funcdesc funcdesc;
intptr p_funcdesc;
itypeinfo.getfuncdesc(i,out p_funcdesc);
funcdesc = (funcdesc)marshal.ptrtostructure(p_funcdesc,typeof(funcdesc));
……
和typeattr一樣,funcdesc結構也需要marshal.ptrtostructure處理一下,偶就不多說了。
討厭的是:funcdesc結構里并沒有函數的名稱,我們只能通過它的memid字段和invkind字段知道這個函數的成員id和函數的類型。函數的類型是我們需要的:它告訴我們這個函數是一個方法或者是一個屬性的get/set方法,而名稱這個東西,我們還得求助于itypeinfo:getnames 方法獲取具有指定成員id的成員名稱,它的返回一個string數組,對方法而言,這個數組第一個元素是方法名稱,后面的元素則是方法的參數名,而對屬性而言,屬性名稱也出現在數組的第一個元素。
好了,現在除了一件事情,該說的偶已經都說了,我們已經知道了如何從itypeinfo獲得方法/屬性的名稱,至于如何如何先建立二個空的collection分別用于存放方法和屬性名稱;如何如何遍歷itypeinfo的所有funcdesc,根據每個不同的函數類型向對應的collection中插入元素,這些簡單操作偶就不想多說了。我們剩下的唯一的問題是:對所有vb生成的com對象,按照上面的步驟走下來,我們什么方法屬性也看不到。
原因嘛,用oleview看一下這些dll的typelib就明白了:vb會生成一個名為”_”+類名的類接口,這個接口繼承自idispatch,所有的方法屬性都在這個接口上定義,而實現類只是簡單的實現這個接口,在它的typelib里,真正對應classid的實現類里沒有任何成員。
既然知道了原因,辦法也就有了:我們在枚舉一個com對象的所有方法/屬性時,不應該只枚舉僅對應它自己classid的typeinfo,這個typeinfo繼承的所有其他接口中定義的方法/屬性也要照樣拿出來,而要定位到它繼承的其他接口,我們要做的事情其實和遍歷這個itypeinfo的所有funcdesc差不多:
if(typeattr.cimpltypes > 0)
{
for(int i=0;i<typeattr.cimpltypes;++i)
{
       int href;
       ucomitypeinfo imptypeinfo;
       typeinfo.getreftypeofimpltype(i,out href);
       typeinfo.getreftypeinfo(href,out imptypeinfo);
       //now we can do the same thing to the imptypeinfo like typeinfo
……
}
}

typeattr的cimpltypes字段表示這個itypeinfo實現的接口數目,itypeinfo的getreftypeofimpltype 方法獲取對某個已實現接口的句柄的引用,而getreftypeinfo 方法從這個句柄的引用獲取該接口的itypeinfo。很明顯,我們可以寫一個遞歸函數來走遍所有com對象實現的接口,而且我們可以確信這個遞歸是有出口的:因為com里所有的接口歸根到底都派生自“我不知道”接口 。^-^
最后,我想在大多數情況下,你不會希望在com對象的方法列表里看到queryinterface或者addref這類iunknown接口的方法,而idispatch接口那些類似invoke之類的方法想來有興趣的人也不多,不過反正這種底層方法就那么幾個,在你遍歷的時候盡可以判斷一下過濾掉這些方法名稱。

免責聲明:
在本文中,為了清晰起見,所有給出的代碼中都沒有錯誤處理。如果你在你的代碼中使用本文中的部分代碼,由此造成的諸如程序出錯、系統宕機、走路撞樹、手機爆炸、洪水毀堤、地球毀滅等等一切后果,本人概不負責。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 河津市| 土默特左旗| 方山县| 日照市| 汉中市| 资中县| 郴州市| 沙湾县| 襄城县| 延津县| 望谟县| 呈贡县| 西昌市| 克什克腾旗| 东台市| 开平市| 公安县| 博爱县| 九江市| 苏尼特右旗| 百色市| 都匀市| 巴彦县| 湛江市| 北川| 临潭县| 天津市| 永靖县| 阆中市| 赫章县| 大新县| 静安区| 阜新市| 贞丰县| 崇义县| 长泰县| 中江县| 于田县| 金华市| 塔城市| 绵竹市|