• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
    • 軟件測試技術
    • 軟件測試博客
    • 軟件測試視頻
    • 開源軟件測試技術
    • 軟件測試論壇
    • 軟件測試沙龍
    • 軟件測試資料下載
    • 軟件測試雜志
    • 軟件測試人才招聘
      暫時沒有公告

    字號: | 推薦給好友 上一篇 | 下一篇

    添加引用的背后:有人看見橋了嗎?

    發布: 2007-6-11 12:46 | 作者: 網絡轉載 | 來源: 網絡 | 查看: 100次 | 進入軟件測試論壇討論

    領測軟件測試網

    適用于:
       COM Interop
       Microsoft® .NET Framework
       C# 語言
       Microsoft Visual Studio® .NET

    摘要   Sam Gentile 解釋了 COM Interop 和 Microsoft .NET Framework 之間為什么需要橋,以及如何在 .NET Framework 中實現這些橋。

    前提條件:

    • 具有 Microsoft .NET 核心概念的基本知識(程序集、特性、反射、類、屬性和事件)
    • 能夠在 Visual Studio .NET 中用 C# 創建 Windows 窗體應用程序
    • 能夠使用語言編譯器來生成和管理應用程序

    下載相關的代碼示例(212 KB)。(請注意,在示例文件中,程序員的注釋使用的是英文,本文中將其譯為中文是為了便于讀者理解。)

    目錄

    簡介
    有人看見橋了嗎?
    橋的作用
    COM Callable Wrapper (CCW)
    求平方示例
    生成 Interop 程序集
    托管的平方程序
    本文小結及下一篇文章的內容

    簡介

    Interop 是 .NET Framework 中的一種非常有用且必需的技術。為什么是這樣呢?坦白地講,許多公司現在確實使用大量的現有 COM 代碼。雖然這些公司知道托管代碼的眾多好處,但是他們非常希望能夠利用在 COM 中的現有巨大投入來實現托管代碼,而無需重新從頭編寫其全部應用程序。一個好消息是公共語言運行庫 (CLR) 包含 COM Interop,從而可以借助 .NET Framework 從托管代碼中使用此功能。一個不太好的消息是通常這并不是一件簡單的事情。

    簡單地講,COM Interop 是 COM 和 .NET 之間的一座“橋”。許多開發人員注意到 Visual Studio .NET 包含神奇的 (COM) Add Reference(添加引用)向導(Add Reference | COM Wizard [添加引用 | COM 向導])時很高興。該向導使開發人員可以選擇 COM 組件并執行某些“神奇”的操作來處理 .NET 應用程序。常見的問題是,開發人員不得不深入到向導背后,才能為其應用程序提供實際的 COM Interop 技術。這就是困難所在。

    System.Runtime.InteropServices 命名空間包含近 300 個類。.NET Framework SDK 中還有各種 Interop 命令行工具。如果這些還不足以令人感到恐懼的話,此外任何復雜的 Interop 項目中都會出現許多問題,這些問題是由 COM 的細微差別以及 COM 和 .NET 之間的巨大差異造成的。根據我個人的經驗,開發人員經常需要深入到 (COM) 添加引用向導的背后并使用這些命令行工具,同時還要較深入地了解所發生的情況,以使各方面能夠按預期工作(或者甚至是僅僅能夠工作)。

    本文從“添加引用”后開始。我并不準備花時間向您顯示 Visual Studio .NET 向導的屏幕快照。MSDN 已經為其提供了大量信息(請參閱 Calling COM Components from .NET Clients [英文])。本文是一系列文章中的第一篇,目的是深入探究編程人員在使用 COM Interop 過程中將要遇到的各種問題,這些問題不太好理解或者現有資料不夠充分,但完成工作時又不可避免會遇上。

    本文以及后續文章的代碼示例都是用 C# 編寫的,這只是個人的偏好。但是,這里要強調一下,CLR 的操作方式比以前的 Microsoft 技術更為抽象。對于 CLR 來說,只有一個類程序庫和一個類型系統,而各種語言以不同的語法提供了各種公共服務?梢詫 .NET 語言視為覆蓋在基類程序庫 (BCL)、公共類型系統 (CTS) 和 CLR 上的語法糖衣,而使用的語法(語言)只是一個個人口味問題。

    可以開始了嗎?那就探討進一步的內容。

    有人看見橋了嗎?

    每個人都了解橋的作用。橋使您能夠在由無法通行的河流、海灣等隔開的兩個區域之間穿行,可以類似地將 COM Interop 視為橋。這里有兩個存在巨大差異的世界,即 COM 世界和 CLR 世界,它們之間界限明顯。需要“橋接”這些差異,從而使 COM 世界能夠與 CLR 托管世界一起工作。要想使 COM Interop 確實發揮作用,它必須是一座很好的橋,并隱藏掉兩個世界各自的一些細節。.NET 編程人員希望的是他們能夠像新建和使用其他任何 .NET 組件一樣處理 COM 組件。.NET 編程人員不應當被卷入到 COM 世界中并調用 CoCreateInstance (Ex) 來創建 COM 組件以及處理引用計數以及類似的概念。他們應當使用操作符 new 來創建對象并調用相關方法,就像處理任何其他 .NET 組件一樣。

    對于另一方也應當是這種情況。如果您將一個 .NET 組件提供給一個 COM 客戶端,則該組件看上去應當與其他任何 COM 組件一樣。這樣,通過 COM,開發人員便可以執行 QueryInterface 以及多年以來他們所進行的所有有趣的事情。我的意思是什么呢?也就是基礎組件不應當更改,編程模型也不應當更改。我們將看到,對于大多數情況都是這樣的,Interop 在橋接這些差異方面是非常成功的。但是,這兩個世界的差異非常大,某些情況下會導致問題十分棘手。我們將在以后的文章中討論這些問題。

    究竟為什么需要橋?簡單地講,雖然這兩種技術在 Interop 組件方面具有共同的目標,但這兩個世界的差異實在是太大了。人們不應當對這一點感到十分驚奇。CLR 是一個托管執行的世界,其中包含內存回收器、通用編程模型以及其他許多內容。COM 存在于一個非托管的、引用計數的世界中,其中包含具有很大差異的編程模型。雖然 COM 具有一個二進制標準,但是仍然存在許多不同的編程模型,例如,Visual Basic®、Microsoft 基礎類程序庫 (MFC) 和活動模板庫 (ATL)(這只是其中的幾個例子),這些模型的抽象程度各不相同。了解這一點后,我們將簡短介紹直接影響 Interop 的某些差異。這些差異包括標識、定位組件、對象的生存期、編程模型、類型信息、版本控制以及錯誤處理等方面。下一節將簡短討論這些方面的差異,請注意,每個主題的完整詳細說明已經在許多書和文章中進行了專門論述,本文不包含這方面的內容。

    標識

    所有 COM 編程人員都非常熟悉全局唯一標識符 (GUID),它用于在 COM 中唯一地標識各種對象。在全局范圍內,這些 128 位的數字無論在任何時候任何地點都是唯一的(除非您重復使用他人的標識符)。在 COM 中,任何位置都可以使用 GUID,可以作為 CLSID(類標識符)、IID(接口標識符)、LIBID(程序庫標識符)和 AppID(應用程序標識符)等,這只是其中的幾個例子。它們的作用都相同:賦予 COM 中的對象一個全局唯一的標識。

    大多數人都無法記住 128 位數字,因此類可以采用便于用戶記憶的名稱,這些名稱與 128 位數字相對應。

    CLR 沒有使用這種系統。類型只是由其名稱來標識,并進一步由其所在的命名空間來限定。這稱為“簡單名稱”。但是,任何 CLR 類型的完整類型標識都包括類型名稱、命名空間及其包含程序集。也就是說,程序集也作為部署和打包的類型范圍單元和邏輯單元。

    定位組件

    COM 和 .NET 定位組件的方式存在很大差異。COM 組件的物理位置可以任意,但是有關如何找到和加載它們的信息則是放在一個中心位置:注冊表。而 CLR 組件根本就不使用注冊表。所有托管程序集都將此信息以元數據的形式存儲在程序集內。此外,.NET 組件還可以作為私有組件與其應用程序存在于相同的目錄中,也可以作為全局共享組件存在于全局程序集緩存 (GAC) 中。

    要用 CoCreateInstance 實例化某個 COM 組件,COM 將在注冊表中查找 CLSID 項及其關聯值。通過這些值,COM 就會知道實現您要加載的 COM 公共類的 DLL 或 EXE 的名稱和位置。COM 具有引人注目的眾多特色,其中之一就是位置的透明性。簡單地講,COM 客戶端將以相同的方式調用對象,不管對象是與客戶端位于同一個進程、位于同一臺本地計算機上的不同進程,還是運行在根本不同的計算機上,注冊表都會將對象的位置告知 COM。這種系統很容易遭到破壞。如果文件更改了位置但沒有更改其注冊表設置,程序將完全破壞。這會導致出現糟糕的“DLL Hell”問題。

    由于這一點以及其他許多原因,.NET 組件采用了一種完全不同的方法。CLR 將查找三個位置之一:GAC、本地目錄或由配置文件指定的某個其他位置。.NET 的目標之一就是從根本上簡化部署。對于大多數應用程序,可以將組件部署到應用程序所在的本地目錄中,一切都將正常工作。這稱為 x-copy 部署。共享的程序集可以放在 GAC 中。有關這方面的詳細信息,請參閱 Jeffrey Richter 編寫的《Applied Microsoft .NET Framework Programming》(英文)。

    對象的生存期

    存在最大差異的一個方面可能是 COM 和 .NET 對以下問題的處理:對象在內存中應當存在多長時間以及如何確定該時間。

    COM 使用引用計數系統來確定對象的生存期。這增加了對象以及編程人員的負擔,因為這需要對象維護自己的生存期并確定應當在何時刪除自己。COM 規范清楚地說明了此模型的規則。整個方案的關鍵是 IUnknown 接口,所有 COM 組件都必須實現該接口,所有 COM 接口都是從該接口派生的。IUnknown 包含兩個直接負責處理引用計數的方法。Add 方法用于遞增引用數,而 Release 方法用于遞減引用數。當計數到達零時,對象可能被破壞。此方案會產生各種細微的差異,并且可能會創建對象循環。此外,此方案很容易出現錯誤,并且是造成困擾 COM 編程人員多年的許多問題的原因。

    CLR 將編程人員從這一責任中完全解脫出來。CLR 通過使用內存回收來管理所有對象引用。內存回收器確定對象不再被使用后釋放該對象。關鍵差異在于這是一個非確定性的過程。與 COM 不同,在最后一個客戶端使用完對象后,對象不會立即釋放,而是在內存回收器回收內存時才會釋放。釋放發生的時間是將來的某個不可預知的時間。大多數情況下,這不會導致問題。但是,在以后的文章中將看到,如果您的 COM 設計在某個時間點明確調用了釋放操作,則這可能成為一個大問題。.NET Framework 確實在 System.Runtime.InteropServices 命名空間中提供了一個 ReleaseComObject 系統調用,用于要求立即釋放,但是這會導致進一步的問題(我們稍后將對此加以說明)。

    編程模型

    COM 編程耗費編程人員大量的精力。雖然有許多可以大大簡化 COM 編程的抽象編程模型(例如 Microsoft Visual Basic),但事實是,COM 需要嚴格遵守一組規則并需要大量低級、晦澀的詳細信息才能有效地工作。此外,用于 COM 的編程工具也有很多,包括從 Delphi 到 MFC、ATL,再到 Visual Basic。雖然其中的每種工具都能夠生成有效的 COM 組件(這些組件符合內存中的 COM 的二進制 v 表布局),但是它們的編程模型卻存在很大差異。了解一種工具和模型的編程人員在選擇使用另一種工具時,不得不面對一個全新的編程模型。鑒于這一點以及其他許多原因,.NET Framework 進行了很大的簡化,將編程模型簡化為一個。.NET Framework 具有一個一致的面向對象的基類程序庫 (BCL) 框架,它獨立于編程語言和工具。

    在 COM 編程中,編程人員從來不會真正獲得一個對實際對象的引用。而是 COM 客戶端獲得一個接口引用并通過它調用對象的方法(當然,Visual Basic 給人的感覺是對類進行編程,但實際上使用的是 COM 接口)。另外,COM 還沒有實現繼承。

    .NET Framework 與此截然不同,它是一個完全面向對象的平臺,在其中編程人員可以充分使用各種類。雖然編程人員可以使用接口,但是該模型并不要求他們這樣做。

    類型信息

    在基于組件的系統中,有一點很重要:用某種方法說明組件的接口或約定,以及說明如何在組件及其客戶端或使用者之間交換信息。COM 規范沒有強制規定這樣一種交換格式,因而它不屬于該規范中的內容。這樣就出現了兩種不同的格式,而不是一種。

    第一種是 Microsoft 接口定義語言 (MIDL),它實際上源于 OSF DCE RPC,而后者使用 IDL 并以一種獨立于語言的方式編寫遠程過程調用 (RPC) 的說明。Microsoft IDL 提供了 DCE IDL 擴展,以支持 COM 接口、公共類定義、類型定義以及其他內容。使用 MIDL 編譯器編譯 IDL 時,將生成一組 C/C++ 頭文件,從而使網絡代碼能夠通過各種網絡協議進行遠程 RPC 調用。

    更為常見的情況是使用類型庫(TLB 文件)。類型庫是用 IDL 術語說明的,被編譯成一種二進制資源,然后類型庫瀏覽器就可以讀取和呈現該資源。其中的一個問題是,類型庫完全是可選的并且不完整。而且,COM 沒有強制要求其中的信息的正確性。此外,該格式是不可擴展的,也不試圖說明組件的相關性。

    為了創建一個真正一流的組件環境,類型信息以元數據的形式遍布于 CLR 的各個層次。除了 MSIL 以外,還需要 CLR 編譯器來發布標準的元數據。類型信息始終是最新的、完整的和精確的。這賦予了 .NET 組件“自我描述”特性。

    通過本節,您應當開始注意到我們需要通過某種轉換過程來將 COM 類型定義轉換為 .NET 元數據。

    版本控制

    在組件工程中,版本控制是一個重要的且難以解決的問題。接口可能會隨著時間發生變化,而這可能導致客戶端被破壞。COM 接口不存在版本控制問題。COM 接口被認為是不可改變的;在定義和公開后,它就不會再更改。任何更改(例如添加成員或更改方法中參數的順序)都會導致客戶端被破壞。因此,在 COM 中,如果對現有接口進行任何更改,我們都將定義一個全新的接口。如果定義并發布了一個 COM 接口 IFoo,并且希望進行更改或添加成員,則將定義一個新接口 IFoo2。

    COM 的二進制對象模型及其在內存中的表示實際定義了 COM 接口。COM 接口是內存中的一個 v 表,接口指針是一個指向它的 v 指針。這種非常精確的模型是很脆弱的。對 v 表順序或字段排列的任何更改都將導致客戶端被破壞。

    .NET Framework 從一開始就被設計為完全支持組件的版本控制。每個 .NET 程序集在被賦予一個嚴格的名稱后,都可以包含一個由四部分組成的版本號,其形式為 Major.Minor.Build.Revision,該版本號存儲在其清單中。CLR 完全支持內存中同時存在同一程序集的多個版本,這些版本彼此之間是獨立的。CLR 還支持一個完全的版本控制策略,該策略可以由管理員在 XML 配置文件中應用,而配置文件可以基于計算機或應用程序來應用,從而將客戶端綁定到特定版本的程序集。

    錯誤處理

    COM 和 .NET 處理錯誤報告的方式有很大差異。COM 具有多種處理錯誤的方式,但主要的方式是讓方法返回類型為 HRESULT 的錯誤代碼。HRESULT 是 32 位的狀態代碼,它可以告知調用者發生的錯誤的類型。HRESULTS 由三部分組成:設備代碼、信息代碼和嚴重度位。嚴重度由最顯著的位指示,它說明是成功還是失敗。設備代碼指示錯誤源。信息代碼位于最低的 16 位中,它包含錯誤或警告說明。

    本文不想花費更多時間介紹 HRESULT,只是想說明沒有強制客戶端來檢查它們,在 COM 運行庫中也沒有強制方法返回它們?梢詫⑺鼈兒雎。除了 HRESULT 以外,COM 公共類還可以支持附加接口 ISupportErrorInfo,該接口提供了豐富的錯誤信息。當然,由于它是一個接口,因此客戶端必需專門查詢它,檢查是否支持它,然后再處理錯誤。許多客戶端并不執行此操作。

    .NET Framework 強制采用一種一致的報告和處理錯誤的方法:異常。異常不能被忽略。此外,異常還將處理錯誤的代碼與實現邏輯的代碼分離開來。

    橋的作用

    正如已經看到的那樣,這兩個系統存在很大差別。要在這兩種模型之間實現 Interop,需要某種“橋”或包裝程序。對于 COM Interop,有兩種這樣的橋。一種是 Runtime Callable Wrapper(運行時可調用包裝程序,RCW),它獲取 COM 組件,將其包裝起來,并且使 .NET 客戶端能夠使用它。另一種是 COM Callable Wrapper(COM 可調用包裝程序,CCW),它包裝 .NET 對象以供 COM 客戶端使用。

    您可能已經注意到上述術語中的“運行時”一詞。這些橋或包裝程序是由 CLR 在運行時動態創建的。對于 RCW,CLR 將通過包含在已經生成的 Interop 程序集中的元數據來生成它。對于 CCW,不需要 Interop 程序集;COM 類型庫的生成完全是可選的。但是,需要在 Windows 注冊表中注冊程序集,以便 COM 可以調用它(我們將在系列文章中的下一篇中討論這方面的問題)。

    這些包裝程序將完全處理并掩蓋 COM 和 .NET 之間的轉換,并處理前面提到的所有差異:數據的封送處理、對象的生存期問題、錯誤處理以及其他許多問題。正如您所希望的那樣,通過橋,應當能夠安全地從一側到達另一側,而無需處理細節。使用對象創建語義(在 .NET 中為 new,在 COM 中為 CoCreateInstance)來創建包裝程序,可以在任意一側使用相應的語義,在內部創建實際的對象。然后只需調用該包裝程序,而包裝程序將進一步調用實際的對象。

    在較高的層次上,它如圖 1 所示:

    圖 1:使用對象創建語義創建包裝程序并調用它們

    包裝程序取決于類型信息。正如介紹的那樣,將 COM 類型數據與 CLR 元數據互換需要某種轉換過程或工具。.NET Framework 軟件開發工具包 (SDK) 提供了這些工具,我們將對它們進行簡短的介紹。

    我們已經討論了橋的總體思想,下面將詳細介紹 Runtime Callable Wrapper (RCW)。

    Runtime Callable Wrapper (RCW)

    .NET 客戶端從不與 COM 對象直接聯系,而是由托管代碼與調用 Runtime Callable Wrapper (RCW) 的包裝程序聯系。RCW 是由 CLR 根據 Interop 程序集中包含的元數據信息在運行時動態創建的代理。對于 .NET 客戶端,RCW 看上去與其他任何 CLR 對象一樣。同時,RCW 還作為對 .NET 客戶端和 COM 對象之間的調用進行封送處理的代理。不管每個 COM 對象具有多少托管引用,都只有一個 RCW。RCW 的工作是維護 COM 對象標識,它通過以下方式來完成此工作:內部調用 IUnknown->QueryInterface() 并緩存接口指針,在適當的時間調用 AddRefRelease。

    RCW 可以執行以下功能:

    • 調用封送方法
    • 代理 COM 接口
    • 保留對象標識
    • 維護 COM 對象生存期
    • 使用默認的 COM 接口,例如 IUnknownIDispatch

    該過程如圖 2 所示。

    圖 2:RCW 過程

    COM Callable Wrapper (CCW)

    COM Callable Wrapper (CCW) 在另一個方向上扮演類似的角色。它也是作為橋或代理,但用于當 COM 客戶端想要聯系 .NET 對象時。CCW 的主要工作是將來自 COM 客戶端的調用轉發給 .NET 對象,這些客戶端處在這樣一種假象下,即,它們是在與另一個 COM 對象聯系。根據這種情況,CCW 實現了標準的 COM 接口,例如 IUnknown、IDispatch 以及其他許多接口。某種 .NET 類型的多個 COM 客戶端可以共享一個 CCW。

    CCW 可以執行以下功能:

    • 將 COM 數據類型轉換為相應的 CLR 類型(封送處理)
    • 模擬 COM 引用計數
    • 提供封裝的標準 COM 接口實現

    類型庫導入程序 (TLBIMP.EXE)

    在給出最后的示例之前,需要簡單介紹一下類型庫導入程序工具。在我的下一篇文章中,將詳細討論類型庫導入程序,但在這里只給出簡短介紹。

    正如前面所述,CLR 無法用 COM 類型信息做任何事情。CLR 要求類型信息采用 CLR 元數據的形式存儲在程序集中。顯然,我們需要某種機制來讀取 COM 類型信息并將其轉換為程序集中的 CLR 元數據。這些程序集(稱為 Interop 程序集)可以通過三種不同的方式創建。

    第一種方式是使用 Visual Studio .NET 中的“添加 COM 引用”向導。我發現這種方式對于 Interop 具有很大的限制,因為選項中沒有提供任何靈活性。由于此原因以及已經有許多 MSDN 文檔論述了如何使用此向導,我將不會在本系列文章中進一步討論這方面的內容。第二種方式是使用類型庫導入程序工具 (TBLIMP.EXE)。最后一種方式是使用 System.Runtime.InteropServices.TypeLibConverter 類進行編程。前兩種方式實際上是調用此類來完成各自的工作。這里,我們將著重介紹 TLBIMP 工具。

    TLBIMP 是一個命令行工具,.NET Framework SDK(英文)和 Visual Studio .NET 中提供了該工具。它讀取 COM 類型信息文件(通常為 .tlb、*.dll 或 *.exe 文件)并將其轉換為 Interop 程序集中的 CLR 類型。此工具具有一整套選項,我們將在下一篇文章中深入討論,F在,只介紹其中的一個重要選項。您當然可以按照最簡單的形式來使用 TLBIMP,即,僅指定要轉換的 COM 文件的名稱。但遺憾的是,如果這樣做,TLBIMP 將用 Interop 程序集覆蓋該特定文件且不會發出警告。為避免出現這種情況,您可以指定 /out 選項。這使您可以指定所需的輸出文件的名稱。您的公司可能對此有特定的標準,但是我所喜歡采用的一個約定是在文件名前面加上“Interop.”。這樣,“foo.dll”將變為“Interop.foo.dll”。采用此約定,我們的 TLBIMP 的最簡單形式將變為:

    TLBIMP foo.dll /out:Interop.foo.dll

    現在,讓我們繼續考慮一個非常簡單的示例。

    求平方示例

    作為第一個示例,我選擇了實現一個簡單的 COM 組件,它具有一個接口 IMSDNComServer 和一個方法 SquareIt。您可以下載示例代碼。請注意,為使示例簡單,示例代碼沒有執行任何形式的錯誤檢查。在您開發的代碼中,顯然您會希望這樣做。這種令人驚奇的方法將獲取一個雙精度數作為輸入,并返回該數值的平方。我使用了 Visual C++® 6.0 來實現它。相關 IDL 如下所示:

    interface IMSDNComServer : IDispatch
    {
       [id(1), helpstring("method SquareIt")] HRESULT SquareIt([in] double dNum, 
          [out] double *dTheSquare);
    };
    
    coclass MSDNComServer
    {
          [default] interface IMSDNComServer;
    };
    
    In the file MSDNComServer.cpp, the SquareIt method looks like the following:
    
    STDMETHODIMP CMSDNComServer::SquareIt(double dNum, double *dTheSquare)
    {
    
       *dTheSquare = dNum * dNum;
    
       return S_OK;
    }
    

    下載內容中還包括一個 Visual Basic 6.0 測試客戶端,它將實例化 COM 服務器并調用 SquareIt 方法。該代碼非常簡單:

         Dim oSquare As MSDNComServer
        Set oSquare = New MSDNComServer
        Dim dIn As Double
        Dim dTheSquare As Double
        
        
        dIn = 3
        Call oSquare.SquareIt(dIn, dTheSquare)
        MsgBox Str(dTheSquare)
    

    當運行該 Visual Basic 6.0 測試應用程序時,我們將獲得預期的結果:

    圖 3:SquareIt 測試應用程序

    生成 Interop 程序集

    我們的目標是通過我們的 .NET 代碼使用此 COM 組件?梢允褂 Visual Studio .NET (COM) 添加引用向導(Add Reference | COM Wizard [添加引用 | COM 向導]);在這種非常簡單的 COM 組件中,TLBIMP 并沒有提供特別的優勢,但為了演示,我們將使用 TLBIMP 命令。要使用此命令,請在“程序”菜單中,單擊 Visual Studio .NET Tools | Visual Studio .NET 2003 Command Line Prompt(Visual Studio .NET 工具 | Visual Studio .NET 2003 命令行提示)。通過這種方式可以設置正確的路徑和環境變量。

    可以通過鍵入以下內容來查看 TLBIMP 提供的眾多選項:

    C:\Code\MSDN\MSDNManagedSquare>tlbimp /?

    將會列出大量選項。在本系列的第二篇文章中,我們將介紹其中的許多選項,以及它們對所生成的 Interop 程序集的影響。在本簡單示例中,將只指定輸出文件的名稱。如前面所述,如果沒有指定此選項,TLBIMP 將用生成的程序集覆蓋指定的文件。使用的命令行如下所示:

    C:\Code\MSDN\MSDNManagedSquare>tlbimp /out:Interop.MSDNCom.dll MSDNCom.dll

    此特定 TLBIMP 變換將獲取我們的 COM 服務器(位于 MSDNCom.dll 中)并生成一個名為 Interop.MSDNCom.dll 的 Interop 程序集。有一點非常重要,即,基礎 COM 組件保持不變;它在任何方式下都不會更改。我們所做的是為它創建另一個“視圖”,一個從 CLR 角度看到的視圖。

    要查看該“托管視圖”,我們可以使用 ILDASM.exe。此工具也是 .NET Framework SDK 和 Visual Studio .NET 附帶的,它使我們可以查看包含在托管程序集中的元數據和 IL。當自定義類型庫導入和導出過程時,您會發現此工具在 Interop 工作中是不可或缺的。通過對我們的 Interop 程序集調用 ILDASM,頂層視圖將如下所示:

    圖 4:ILDASM 提供的托管視圖

    我們的 Interop 程序集包含兩項內容:清單 (MANIFEST) 和稱為 Interop.MSDNCom 的命名空間。展開該命名空間,我們發現類型庫導入進程已經生成了三項內容!

    圖 5:類型庫導入程序進程的結果

    類型庫導入程序生成了一個抽象接口 (IMSDNComServer) 和兩個類(MSDNComServer 與 MSDNComServerClass)。這里面的原因有些復雜,我的下一篇文章將以此為主題,對導入進程進行詳細討論。此外,注意到這一點就夠了:這是由于編程模型存在巨大差異以及組件的版本控制方式所引起的。

    要注意的一件事是,Interop 程序集通常情況下包含的都是元數據。方法通常是將調用轉發給基礎 COM 組件,突出了橋或代理的作用。查看 SquareIt 方法的反匯編就可以說明這一點。

    .method public hidebysig newslot virtual 
            instance void  SquareIt([in] float64 dNum,
                                    [out] float64& dTheSquare) runtime managed internalcall
    {
      .custom instance void 
    [mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32) = ( 01 00 01 00 00 00 00 00 ) 
      .override Interop.MSDNCom.IMSDNComServer::SquareIt
    } // 方法 MSDNComServerClass::SquareIt 的結尾
    

    托管的平方程序

    由于已經生成了 Interop 程序集,我們可以通過 .NET 客戶端使用它。為了通過示例進行說明,用 C# 生成了一個 Windows 窗體應用程序。假設您知道如何使用 Visual Studio .NET 來創建這樣一個項目。代碼是作為 MSDNManagedSquare 項目提供的。該項目引用了 Interop 程序集 Interop.MSDNCom。引用完成后,可以用 C# 的 using 語句來使用該程序集的元數據:

    using Interop.MSDNCom;
    
    要通過托管代碼調用 COM 服務器,只需實例化服務器類并
    調用 SquareIt 方法。
    
    private void button1_Click(object sender, System.EventArgs e)
    {
    double numToSquare = System.Convert.ToDouble(textBox1.Text);
       double squaredNumber;
    
       MSDNComServerClass squareServer = new MSDNComServerClass();
       squareServer.SquareIt(numToSquare, out squaredNumber);
    
       textBox2.Text = squaredNumber.ToString();
    }
    

    請注意,該代碼在實例化對象和調用其方法方面與其他任何 .NET 代碼類似。要與 COM 一起工作,不必編寫任何特殊代碼,也不必使用 GUID、CoCreateInstanceEx 和其他 COM 編程結構。當運行我們的應用程序時,它會像預期的那樣工作。在內部,CLR 將動態創建一個 RCW,通過它來調用 SquareIt 方法并返回結果。但是,這對于執行應用程序是完全透明的。

    本文小結及下一篇文章的內容

    本文介紹了由于 COM 和 .NET 在標識、定位組件、對象的生存期、編程模型、類型信息、版本控制以及錯誤處理等方面存在巨大差異,因此兩者之間需要建立橋。討論了兩種用于 COM 和 .NET 的橋:Runtime Callable Wrapper (RCW) 和 COM Callable Wrapper (CCW)。由于其中的一個重要差異是類型信息(這是因為這兩個系統使用了不兼容的類型系統),因而我們探討了使用類型庫導入程序 (TLBIMP) 將 COM 數據類型轉換為元數據形式的 CLR 類型。

    第一個示例是一個求某個數值的平方的 COM 組件,我們使用 TLBIMP 生成了一個 Interop 程序集,然后通過一個基于 C# 的 Windows 窗體應用程序來使用它。

    在下一篇文章中,將進一步討論 TLBIMP 和類型庫導入程序進程,同時詳細介紹封送處理進程以及如何使用 System.Runtime.InteropServices 命名空間中的屬性和類來構造特定的導入進程。

    延伸閱讀

    文章來源于領測軟件測試網 http://www.kjueaiud.com/

    TAG: 添加 引用 背后 有人 見橋


    關于領測軟件測試網 | 領測軟件測試網合作伙伴 | 廣告服務 | 投稿指南 | 聯系我們 | 網站地圖 | 友情鏈接
    版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
    北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備10010545號-5
    技術支持和業務聯系:info@testage.com.cn 電話:010-51297073

    軟件測試 | 領測國際ISTQBISTQB官網TMMiTMMi認證國際軟件測試工程師認證領測軟件測試網

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>