【译文转帖】用C#写COM组件 Building COM Objects in C#
說明:
我是一個(gè)C#程序員,但是有一次一個(gè)需求只能用C/C++去寫,恰好需要讀取的數(shù)據(jù)存放在DB(SQL CE v3)里面,而我又不會(huì)C/C++(關(guān)鍵是用OleDB訪問DB,這個(gè)實(shí)在是繁瑣),所以催生了用C#寫一個(gè)COM組件,用C/C++去調(diào)用的想法.可謂,很傻很天真.但是也是一種思路,如果MS提供C API的話,問題就簡(jiǎn)單多了.可是事實(shí)是,MS自己的.NET CF用著C API,給用戶卻暴露著COM API.....OK,言歸正傳.
?
主要內(nèi)容:
- 用C#創(chuàng)建一個(gè)簡(jiǎn)單的COM組件(通過COM Interop)
- 用VC++寫一個(gè)客戶端去訪問COM組件.客戶端用TLB文件.
本著易于使用的目的,我把Northwind導(dǎo)入到了SQLServer,然后測(cè)試了我的代碼.(the sake of simplycity這個(gè)不知道啥意思,難道是由于出現(xiàn)紙尿布....).
- 修改COM組件里面的機(jī)器名為你的SQL Server的機(jī)器名.(2005以上需要 機(jī)器名\實(shí)例名)
- 當(dāng)然我在里面也創(chuàng)建了一個(gè)用戶scott密碼是tiger,用來連接數(shù)據(jù)庫.你可以選擇這個(gè)用戶名,或者重新建一個(gè).
Part I: 用C#創(chuàng)建一個(gè)簡(jiǎn)單的COM組件
COM對(duì)象是一種類庫.COM組件將產(chǎn)生DLL文件.在VS環(huán)境里面創(chuàng)建COM組件請(qǐng)選擇....
File->New->Project->VisualC# Projects ->Class Library.
創(chuàng)建一個(gè)名為Database_COMObject的類庫工程.
請(qǐng)記住:想要把C#對(duì)象當(dāng)作COM對(duì)象需要以下幾點(diǎn)...
- class必須是public的
- 屬性,方法和事件必須是public
- 屬性和方法必須在Interface里面定義
- 事件必須在事件的接口中
未在接口中定義的成員,而在實(shí)現(xiàn)里面是public的成員,對(duì)COM是不可見的,但是對(duì)其他的.NET程序是可見的.為了把屬性和方法暴露給 COM,你必須在接口中定義他們,并且把他們用DispId屬性標(biāo)記,在class里面實(shí)現(xiàn)(.....).在接口里面定義的成員只是為了使用 vtable(虛函數(shù)表).要想暴露事件,你也必須把成員定義在事件接口里面并且標(biāo)記DispId屬性.類不需要實(shí)現(xiàn)此接口(???).類可以實(shí)現(xiàn)接口 (一個(gè)類可以實(shí)現(xiàn)多個(gè)接口,只有第一個(gè)接口才是默認(rèn)的接口.).暴露給COM的那些屬性方法其實(shí)就在類的實(shí)現(xiàn)里面.他們必須被標(biāo)記為public,而且要符合接口里面的定義.Also, declare the events raised by the class here. They must be marked public and must match the declarations in the events interface. (這兩句不知道具體的含義,代碼里面也沒看出端倪.)
每一個(gè)接口都要有一個(gè)GUID屬性(我當(dāng)時(shí)上學(xué)的時(shí)候,把他叫屬性屬性,或者定制屬性,現(xiàn)在也不清楚到底叫什么..).你可以用guidgen.exe來產(chǎn)生一個(gè)GUID值.
這個(gè)接口就長(zhǎng)這個(gè)樣子:
?
[Guid("694C1820-04B6-4988-928F-FD858B95C880")]public interface DBCOM_Interface{[DispId(1)]void Init(string userid , string password);[DispId(2)]bool ExecuteSelectCommand(string selCommand);[DispId(3)]bool NextRow();[DispId(4)]void ExecuteNonSelectCommand(string insCommand);[DispId(5)]string GetColumnData(int pos);}
?
?
[Guid("694C1820-04B6-4988-928F-FD858B95C880")]publicinterfaceDBCOM_Interface
{
[DispId(1)]
voidInit(stringuserid , stringpassword);
[DispId(2)]
boolExecuteSelectCommand(stringselCommand);
[DispId(3)]
boolNextRow();
[DispId(4)]
voidExecuteNonSelectCommand(stringinsCommand);
[DispId(5)]
stringGetColumnData(intpos);
}
COM事件:
?
// // Events interface Database_COMObjectEvents [Guid("47C976E0-C208-4740-AC42-41212D3C34F0"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]public interface DBCOM_Events {}
?
實(shí)現(xiàn)接口的類:
?
[Guid("9E5E5FB2-219D-4ee7-AB27-E4DBED8E123E"),ClassInterface(ClassInterfaceType.None),ComSourceInterfaces(typeof(DBCOM_Events))]public class DBCOM_Class : DBCOM_Interface{
?
在類的前面標(biāo)記:
?
ClassInterface(ClassInterfaceType.None), ComSourceInterfaces(typeof(DBCOM_Events))]
ClassInterfaceType.None表示,這個(gè)類不會(huì)產(chǎn)生類接口.如果沒有顯式的接口實(shí)現(xiàn),那么這個(gè)類只能提供對(duì)IDispatch的訪問.用戶期待通過接口導(dǎo)出該類顯式實(shí)現(xiàn)了的成員.所以需要使用設(shè)置ClassInterfaceAttribute.
ComSourceInterfaces(typeof(DBCOM_Events))]標(biāo)明標(biāo)記的這個(gè)類會(huì)把接口暴露給COM事件源.在我們的例子中,沒有什么需要暴露的..
下面是完整的COM對(duì)象:
?
using System; using System.Runtime.InteropServices; using System.IO; using System.Text; using System.Data.SqlClient; using System.Windows.Forms ;namespace Database_COMObject {[Guid("694C1820-04B6-4988-928F-FD858B95C880")]public interface DBCOM_Interface{[DispId(1)]void Init(string userid , string password);[DispId(2)]bool ExecuteSelectCommand(string selCommand);[DispId(3)]bool NextRow();[DispId(4)]void ExecuteNonSelectCommand(string insCommand);[DispId(5)]string GetColumnData(int pos);}// Events interface Database_COMObjectEvents [Guid("47C976E0-C208-4740-AC42-41212D3C34F0"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]public interface DBCOM_Events {}[Guid("9E5E5FB2-219D-4ee7-AB27-E4DBED8E123E"),ClassInterface(ClassInterfaceType.None),ComSourceInterfaces(typeof(DBCOM_Events))]public class DBCOM_Class : DBCOM_Interface{private SqlConnection myConnection = null ; SqlDataReader myReader = null ;public DBCOM_Class(){}public void Init(string userid , string password){try{string myConnectString = "user id="+userid+";password="+password+";Database=NorthWind;Server=SKYWALKER;Connect Timeout=30";myConnection = new SqlConnection(myConnectString);myConnection.Open();//MessageBox.Show("CONNECTED"); }catch(Exception e){MessageBox.Show(e.Message);}}public bool ExecuteSelectCommand(string selCommand){if ( myReader != null ) myReader.Close() ;SqlCommand myCommand = new SqlCommand(selCommand);myCommand.Connection = myConnection;myCommand.ExecuteNonQuery();myReader = myCommand.ExecuteReader();return true ;}public bool NextRow(){if ( ! myReader.Read() ){myReader.Close();return false ;}return true ;}public string GetColumnData(int pos){Object obj = myReader.GetValue(pos);if ( obj == null ) return "" ;return obj.ToString() ;}public void ExecuteNonSelectCommand(string insCommand){SqlCommand myCommand = new SqlCommand(insCommand , myConnection);int retRows = myCommand.ExecuteNonQuery();}} }
?
?
在編譯COM組件之前,需要在COM Interop那里注冊(cè).
打開Solution Explorer->Properties->Configuration->Build->Expand the output section->Register for COM Interop改為True.
(我在VS 2008里面貌似不是這么操作的,工程上面點(diǎn)右鍵->屬性->Build->最下面的Output->Register for COM Interop改為True).
表明,我要把Managed程序?qū)С鲆粋€(gè)COM對(duì)象,并且COM對(duì)象可以和我們的托管程序交互.
為了(真正)導(dǎo)出COM對(duì)象,程序集還需要強(qiáng)命名,可以用sn.exe生成一個(gè)StrongName:
sn -k Database_COM_Key.snk
在AssemblyInfo.cs文件里面修改:
[assembly: AssemblyKeyFile("Database_COM_Key.snk")]
編譯這個(gè)對(duì)象.會(huì)產(chǎn)生一個(gè)tlb文件,通過這個(gè)可以使Managed代碼和Native代碼都能訪問你的COM對(duì)象.
Part II : 用VC++創(chuàng)建一個(gè)客戶端去訪問這個(gè)COM對(duì)象
我已經(jīng)在VC++6.0和VC++.NET下面訪問呢過改COM對(duì)象.
創(chuàng)建一個(gè)簡(jiǎn)單的工程,通過?#import directive導(dǎo)入type library.
?
創(chuàng)建一個(gè)只能指針指向接口的實(shí)例,執(zhí)行那些導(dǎo)出函數(shù),確保在程序加載的時(shí)候執(zhí)行CoInitialize().
?
CoInitialize(NULL);Database_COMObject::DBCOM_InterfacePtr p(__uuidof(Database_COMObject::DBCOM_Class));db_com_ptr = p ;db_com_ptr->Init("scott" , "tiger");
?
下面的代碼通過一個(gè)Customer ID去在Customer表中查詢:
?
char cmd[1024];sprintf(cmd , "SELECT COMPANYNAME , CONTACTNAME ,CONTACTTITLE , ADDRESS FROM CUSTOMERS WHERE CUSTOMERID = '%s'" , m_id );const char *p ;bool ret = db_com_ptr->ExecuteSelectCommand(cmd);if ( ! db_com_ptr->NextRow() ) return ;_bstr_t mData = db_com_ptr->GetColumnData(3);p = mData ;m_address = (CString)p ;
?
PS:
總算翻譯完了....英語不好,有一些句子不能理解含義,英語好的童鞋推薦直接看e文.
我只能說我很悲劇,當(dāng)初想法很好,只可惜,.NET CF下面,不支持用C#寫一個(gè)COM組件.
我的口頭禪就是:.NET CF除了慢,再?zèng)]有其他優(yōu)點(diǎn).
轉(zhuǎn)載于:https://www.cnblogs.com/SuperBrothers/archive/2012/10/29/2744933.html
總結(jié)
以上是生活随笔為你收集整理的【译文转帖】用C#写COM组件 Building COM Objects in C#的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 星际战甲国服英雄价格大概都是多少人民币
- 下一篇: 求一个暖男气质的qq网名!