知識須要不斷積累、總結和沉澱,思考和寫做是成長的催化劑web
1、COM和.NET元數據內存管理接口註冊線程編組2、.NET客戶端調用COM組件3、COM客戶端調用.NET組件4、嵌入互操做類型5、平臺調用DllImport6、等等編程
COM組件對象模型是在.NET以前的一種編程規範,它容許不一樣的語言之間能夠互相操做。因爲COM規範比較複雜,註冊表,內存對象管理,錯誤處理機制都和.NET不一樣,.NET作爲其後秀,應用起來更簡單,但通常不會由於新技術可用就重寫已有的代碼,因此就引來COM的互操做性api
咱們可能沒必要編寫COM組件,但瞭解是有用的。常常會遇到嵌入互操做類型,爲COM設置互操做問題多線程
先看一下COM的一些基本概念,挑了幾個重要的也是比較好理解的app
COM的元數據信息存儲在tlb類型庫中,包含接口、方法和參數名稱等,在.NET程序集中元數據都存儲在程序集中的。函數
咱們知道.NET託管對象的內存釋放都有垃圾回收器GC完成,不一樣於COM,COM依賴引用計數,工具
COM三個基本接口,IClassFactory、IUnknown、Idispatch
IClassFactory,每一個組件都有一個相關的類廠用於建立COM組件對象。非託管對象,客戶端是沒法直接New對象的,因此只能經過交給類廠來建立實例而後把實例的指針交給客戶端ui
每一個COM對象必須實現IUnknown接口,QueryInterface用於查詢組件實現的其它接口,說白了也就是看看這個組件的父類中還有哪些接口類,AddRef()遞增引用計數,Release()遞減引用計數,爲0後就銷燬對象spa
IDispatch調度接口派生自IUnknown接口,在其基礎上又增長了GetIDsOfNames()和Invoke(),調用接口會建立方法或屬性對應的調用ID映射表,這樣調用時先獲取根據名字獲取調度ID而後Invoke調用。由於並非全部的語言(客戶端)(像一些js腳本語言)都支持指針,也就不能經過虛函數表來調用,因此用調度接口增長函數ID映射。線程
.NET中區分私有程序集和共享程序集。在COM中,經過註冊表配置的全部組件都是全局可用的。全部COM對象都有一個惟一標識符CLSID類ID,建立COM對象時,COM API調用CoCreateInstacne()方法,在註冊表中查找CLSID的dll或exe路徑,而後加載,實例化組件
COM使用單元模型,單元模型有單線程單元模型STA和多線程單元模型MTA
STA單線程單元模型,在Winfrom程序中常常看到Main入口函數上面標記STAThread特性。在STA中只容許建立實例的線程訪問組件。一個進程中也能夠包含多個STA
MTA多線程單元模型,在MTA中,多個線程能夠同時訪問組件
.NET和COM之間的數據傳遞必須通過轉換,這種機制就是編組(marshaling)。轉換過程取決於數據類型。簡單的數據類型如byte、short、int和long屬性blittable類型,在com和net中是同樣的表示方法,其餘nonblittable類型的則須要進行轉換,固然會有些開銷
COM數據類型 | .Net數據類型 |
---|---|
SAFEARRAY | Array |
VARIANT | Object |
BSTR | String |
Iunknown,Idispatch | Object |
因爲COM對象和.NET對象在生命週期、內存管理、接口服務上的差別,運行時提供了包裝類來使其互相調用。託管客戶端調用 COM 對象方法時,運行時就會建立一個運行時可調用包裝器 (RCW)
來封送引用機制之間的差別。 也會建立了一個 COM 可調用包裝器 (CCW)
來逆轉此過程
沒寫過COM,也不是很瞭解,但一些約定規範必須遵照,原理和.NET客戶端調用COM組件相似
好比在C#類庫的AssemblyInfo.cs中修改
// 將 ComVisible 設置爲 false 使此程序集中的類型
// 對 COM 組件不可見。若是須要從 COM 訪問此程序集中的類型,
// 則將該類型上的 ComVisible 特性設置爲 true。
[assembly: ComVisible(false)]
在程序集屬性中勾選COM互操做註冊
而後任何一個須要暴露給COM客戶端的都須要有接口
[ComVisible(true)]
[Guid("35A5CE1E-551C-41EC-81D4-005318550119")]
public interface IMyClass
{
void Initialize();
void Dispose();
int Add(int x, int y);
}
編譯時候由於勾選的爲COM互操做註冊,因此須要以管理員運行的才能註冊成功
引用PIA
(主互操做程序集,COM組件生成)時,能夠設置是否嵌入互操做類型。嵌入互操做類型時(True)則PIA不隨着程序一塊兒部署,程序只是引用COM中的類型信息,這樣的好處就是能夠部署到不一樣COM版本的環境中。好比經常使用的Office開發Microsoft.Office.Interop.Excel,設置嵌入互操做類型,就能夠不依賴office版本。改成互操做false後也就將PIA複製到本地
有時候會沒法嵌入互操做類型請改成適當的接口,單純一點就修改嵌入互操做設爲false,OK編譯經過。不太單純的,能夠修改建立對象的方式,像下面這樣,直接實例化的普通類,沒法嵌入互操做類型
Application excelApp = new ApplicationClass();
Application excelApp = new Application()
這樣是能夠的,Application雖然是一個接口,理論上應該不能實例化的,當它上面標記了
[CoClass(typeof (ApplicationClass))
告訴運行時CLR,當有人要建立類型爲Application的實例時,它實際上應該繼續建立ApplicationClass的實例。
用COM接口的能夠嵌入,直接使用coclass的沒法嵌入
還有一些非託管庫不包含COM對象,只包含倒出的函數,這時候須要使用平臺調用服務(P-Invoke)
,CLR會加載包含所需調用函數的dll,並編組參數。在C++的非託管庫中使用dllexport暴露函數,在C#中使用dllimport導入。基本語法以下
[DLLImport(「DLL文件」)]
修飾符 extern 返回變量類型 方法名稱 (參數列表)
dllimport在命名空間System.Runtime.InteropServices下,該特性用於對照非託管庫中導出的函數
[AttributeUsage(AttributeTargets.Method)]
public class DllImportAttribute: System.Attribute
{
public DllImportAttribute(string dllName) {…} //定位參數爲dllName
public CallingConvention CallingConvention; //入口點調用約定
public CharSet CharSet; //入口點採用的字符接
public string EntryPoint; //入口點名稱
public bool ExactSpelling; //是否必須與指示的入口點拼寫徹底一致,默認false
public bool PreserveSig; //方法的簽名是被保留仍是被轉換
public bool SetLastError; //FindLastError方法的返回值保存在這裏
public string Value { get {…} }
}
須要注意的就是數據類型的映射,必須映射到.NET數據類型上。可使用P/Invoke Interop Assistant
工具,它支持託管代碼和非託管代碼之間的方法簽名的轉換,能夠直接生成調用代碼
通常會在一些特殊場合來調用win 32的api,好比像輸入法程序設置永遠不獲取焦點,一些任務處理時不但願用戶點擊別的操做(固然窗體也不能崩了),這時候可使用下面設置窗體控件不可用
[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));
}
}
關於COM也只是知曉一二,日常主要寫業務,COM用的很少,充其量就是調用。作底層嵌入式開發應該用的比較多,好比設備打印機驅動等。瞭解總沒壞處,拜了個拜