.NET進(jìn)階篇10-.NET和COM

知識(shí)需要不斷積累、總結(jié)和沉淀,思考和寫作是成長的催化劑

一、COM和.NET

COM組件對(duì)象模型是在.NET之前的一種編程規(guī)范,它允許不同的語言之間可以互相操作。由于COM規(guī)范比較復(fù)雜,注冊(cè)表,內(nèi)存對(duì)象管理,錯(cuò)誤處理機(jī)制都和.NET不同,.NET做為其后秀,應(yīng)用起來更簡單,但一般不會(huì)因?yàn)樾录夹g(shù)可用就重寫已有的代碼,所以就引來COM的互操作性

我們可能不必編寫COM組件,但了解是有用的。經(jīng)常會(huì)遇到嵌入互操作類型,為COM設(shè)置互操作問題

先看一下COM的一些基本概念,挑了幾個(gè)重要的也是比較好理解的

元數(shù)據(jù)

COM的元數(shù)據(jù)信息存儲(chǔ)在tlb類型庫中,包含接口、方法和參數(shù)名稱等,在.NET程序集中元數(shù)據(jù)都存儲(chǔ)在程序集中的。

內(nèi)存管理

我們知道.NET托管對(duì)象的內(nèi)存釋放都有垃圾回收器GC完成,不同于COM,COM依賴引用計(jì)數(shù),

接口

COM三個(gè)基本接口,IClassFactory、IUnknown、Idispatch
IClassFactory,每個(gè)組件都有一個(gè)相關(guān)的類廠用于創(chuàng)建COM組件對(duì)象。非托管對(duì)象,客戶端是無法直接New對(duì)象的,所以只能通過交給類廠來創(chuàng)建實(shí)例然后把實(shí)例的指針交給客戶端

每個(gè)COM對(duì)象必須實(shí)現(xiàn)IUnknown接口,QueryInterface用于查詢組件實(shí)現(xiàn)的其它接口,說白了也就是看看這個(gè)組件的父類中還有哪些接口類,AddRef()遞增引用計(jì)數(shù),Release()遞減引用計(jì)數(shù),為0后就銷毀對(duì)象

IDispatch調(diào)度接口派生自IUnknown接口,在其基礎(chǔ)上又增加了GetIDsOfNames()和Invoke(),調(diào)用接口會(huì)創(chuàng)建方法或?qū)傩詫?duì)應(yīng)的調(diào)用ID映射表,這樣調(diào)用時(shí)先獲取根據(jù)名字獲取調(diào)度ID然后Invoke調(diào)用。因?yàn)椴⒉皇撬械恼Z言(客戶端)(像一些js腳本語言)都支持指針,也就不能通過虛函數(shù)表來調(diào)用,所以用調(diào)度接口增加函數(shù)ID映射。

注冊(cè)

.NET中區(qū)分私有程序集和共享程序集。在COM中,通過注冊(cè)表配置的所有組件都是全局可用的。所有COM對(duì)象都有一個(gè)唯一標(biāo)識(shí)符CLSID類ID,創(chuàng)建COM對(duì)象時(shí),COM API調(diào)用CoCreateInstacne()方法,在注冊(cè)表中查找CLSID的dll或exe路徑,然后加載,實(shí)例化組件

線程

COM使用單元模型,單元模型有單線程單元模型STA和多線程單元模型MTA
STA單線程單元模型,在Winfrom程序中經(jīng)??吹組ain入口函數(shù)上面標(biāo)記STAThread特性。在STA中只允許創(chuàng)建實(shí)例的線程訪問組件。一個(gè)進(jìn)程中也可以包含多個(gè)STA
MTA多線程單元模型,在MTA中,多個(gè)線程可以同時(shí)訪問組件

編組

.NET和COM之間的數(shù)據(jù)傳遞必須經(jīng)過轉(zhuǎn)換,這種機(jī)制就是編組(marshaling)。轉(zhuǎn)換過程取決于數(shù)據(jù)類型。簡單的數(shù)據(jù)類型如byte、short、int和long屬性blittable類型,在com和net中是一樣的表示方法,其他nonblittable類型的則需要進(jìn)行轉(zhuǎn)換,當(dāng)然會(huì)有些開銷

COM數(shù)據(jù)類型 .Net數(shù)據(jù)類型
SAFEARRAY Array
VARIANT Object
BSTR String
Iunknown,Idispatch Object

二、.NET客戶端調(diào)用COM組件

由于COM對(duì)象和.NET對(duì)象在生命周期、內(nèi)存管理、接口服務(wù)上的差異,運(yùn)行時(shí)提供了包裝類來使其互相調(diào)用。托管客戶端調(diào)用 COM 對(duì)象方法時(shí),運(yùn)行時(shí)就會(huì)創(chuàng)建一個(gè)運(yùn)行時(shí)可調(diào)用包裝器 (RCW)來封送引用機(jī)制之間的差異。 也會(huì)創(chuàng)建了一個(gè) COM 可調(diào)用包裝器 (CCW) 來逆轉(zhuǎn)此過程

image.png

三、COM客戶端調(diào)用.NET組件

沒寫過COM,也不是很了解,但一些約定規(guī)范必須遵守,原理和.NET客戶端調(diào)用COM組件類似
比如在C#類庫的AssemblyInfo.cs中修改

// 將 ComVisible 設(shè)置為 false 使此程序集中的類型
// 對(duì) COM 組件不可見。如果需要從 COM 訪問此程序集中的類型,
// 則將該類型上的 ComVisible 特性設(shè)置為 true。
[assembly: ComVisible(false)]

在程序集屬性中勾選COM互操作注冊(cè)

image

然后任何一個(gè)需要暴露給COM客戶端的都需要有接口

[ComVisible(true)]  
[Guid("35A5CE1E-551C-41EC-81D4-005318550119")]  
public interface IMyClass  
{  
    void Initialize();  
    void Dispose();  
    int Add(int x, int y);
}  

編譯時(shí)候因?yàn)楣催x的為COM互操作注冊(cè),所以需要以管理員運(yùn)行的才能注冊(cè)成功

四、嵌入互操作類型

引用PIA(主互操作程序集,COM組件生成)時(shí),可以設(shè)置是否嵌入互操作類型。嵌入互操作類型時(shí)(True)則PIA不隨著程序一起部署,程序只是引用COM中的類型信息,這樣的好處就是可以部署到不同COM版本的環(huán)境中。比如常用的Office開發(fā)Microsoft.Office.Interop.Excel,設(shè)置嵌入互操作類型,就可以不依賴office版本。改為互操作false后也就將PIA復(fù)制到本地

有時(shí)候會(huì)無法嵌入互操作類型請(qǐng)改為適當(dāng)?shù)慕涌?,單純一點(diǎn)就修改嵌入互操作設(shè)為false,OK編譯通過。不太單純的,可以修改創(chuàng)建對(duì)象的方式,像下面這樣,直接實(shí)例化的普通類,無法嵌入互操作類型
Application excelApp = new ApplicationClass();
Application excelApp = new Application()
這樣是可以的,Application雖然是一個(gè)接口,理論上應(yīng)該不能實(shí)例化的,當(dāng)它上面標(biāo)記了
[CoClass(typeof (ApplicationClass))
告訴運(yùn)行時(shí)CLR,當(dāng)有人要?jiǎng)?chuàng)建類型為Application的實(shí)例時(shí),它實(shí)際上應(yīng)該繼續(xù)創(chuàng)建ApplicationClass的實(shí)例。

用COM接口的可以嵌入,直接使用coclass的無法嵌入

五、平臺(tái)調(diào)用DllImport

還有一些非托管庫不包含COM對(duì)象,只包含倒出的函數(shù),這時(shí)候需要使用平臺(tái)調(diào)用服務(wù)(P-Invoke),CLR會(huì)加載包含所需調(diào)用函數(shù)的dll,并編組參數(shù)。在C++的非托管庫中使用dllexport暴露函數(shù),在C#中使用dllimport導(dǎo)入?;菊Z法如下

[DLLImport(“DLL文件”)]
修飾符 extern 返回變量類型 方法名稱 (參數(shù)列表)

dllimport在命名空間System.Runtime.InteropServices下,該特性用于對(duì)照非托管庫中導(dǎo)出的函數(shù)

[AttributeUsage(AttributeTargets.Method)]
public class DllImportAttribute: System.Attribute
{
   public DllImportAttribute(string dllName) {…}    //定位參數(shù)為dllName
   public CallingConvention CallingConvention;      //入口點(diǎn)調(diào)用約定
   public CharSet CharSet;                              //入口點(diǎn)采用的字符接
   public string EntryPoint;                //入口點(diǎn)名稱
   public bool ExactSpelling;               //是否必須與指示的入口點(diǎn)拼寫完全一致,默認(rèn)false
   public bool PreserveSig;                 //方法的簽名是被保留還是被轉(zhuǎn)換
   public bool SetLastError;                //FindLastError方法的返回值保存在這里
   public string Value { get {…} }
}

需要注意的就是數(shù)據(jù)類型的映射,必須映射到.NET數(shù)據(jù)類型上??梢允褂?strong>P/Invoke Interop Assistant工具,它支持托管代碼和非托管代碼之間的方法簽名的轉(zhuǎn)換,可以直接生成調(diào)用代碼

image.png

一般會(huì)在一些特殊場(chǎng)合來調(diào)用win 32的api,比如像輸入法程序設(shè)置永遠(yuǎn)不獲取焦點(diǎn),一些任務(wù)處理時(shí)不希望用戶點(diǎn)擊別的操作(當(dāng)然窗體也不能崩了),這時(shí)候可以使用下面設(shè)置窗體控件不可用

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int wndproc);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
 
public const int GWL_STYLE = -16;
public const int WS_DISABLED = 0x8000000;
public static void SetControlEnabled(Control c, bool enabled)
{
    if (enabled)
    { 
        SetWindowLong(c.Handle, GWL_STYLE, (~WS_DISABLED) & GetWindowLong(c.Handle, GWL_STYLE));
    }
    else
    { 
        SetWindowLong(c.Handle, GWL_STYLE, WS_DISABLED + GetWindowLong(c.Handle, GWL_STYLE)); 
    }
}

六、等等

關(guān)于COM也只是知曉一二,平常主要寫業(yè)務(wù),COM用的不多,充其量就是調(diào)用。做底層嵌入式開發(fā)應(yīng)該用的比較多,比如設(shè)備打印機(jī)驅(qū)動(dòng)等。了解總沒壞處,拜了個(gè)拜

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 分享人:傅云特邀嘉賓: 周振濤 原文出處: 鏈接:https://bbs.kafan.cn/thread-20...
    胡諾閱讀 1,472評(píng)論 0 0
  • [TOC] 內(nèi)存管理 一、托管堆基礎(chǔ) 在面向?qū)ο笾校總€(gè)類型代表一種可使用的資源,要使用該資源,必須為代表資源的類...
    _秦同學(xué)_閱讀 4,095評(píng)論 0 3
  • 小編費(fèi)力收集:給你想要的面試集合 1.C++或Java中的異常處理機(jī)制的簡單原理和應(yīng)用。 當(dāng)JAVA程序違反了JA...
    八爺君閱讀 5,148評(píng)論 1 114
  • 愛上烘焙后,第一次做慕斯蛋糕。
    英紫愛插畫閱讀 189評(píng)論 0 1
  • 看過很多心靈雞湯,譬如一個(gè)男生如果對(duì)你有好感,那么他一定會(huì)主動(dòng)聯(lián)系你,因?yàn)閻凼遣夭蛔〉难剑? 就如我一樣,我喜...
    Shelly07閱讀 333評(píng)論 0 2

友情鏈接更多精彩內(nèi)容