在本篇文章中
我们将讨论下面的问题
·使用C#创建一个简单的COM对象(使用COM的Interop特性)
·从VC++客户端软件中访问COM客户端软件使用了TypeLibrary(TLB文件)为了简单和方便开发人员使用测试起见我们使用了SQLSERVER数据库软件的缺省安装中的Northwind数据库
·修改COM对象中SQLServer的名字与SQLServer连接
·我们已经创建了连接数据库用的分别为scotttiger的用户名和口令我们可以使用它或者其他现有的用户名和口令
第一部分用C#创建简单的COM对象
COM对象是ClassLibrary类它生成DLL文件要在VS开发环境中创建一个简单的COM对象我们可以依次选择文件>新创建>工程>VisualC#工程>类库然后创建一个名字为Database_COMObject的工程
需要注意的是在COM中调用VC#对象需要下面的条件
·类必须是public性质
·特性方法和事件必须是public性质的
·特性和方法必须在类接口中定义
·事件必须在事件接口中定义
不是在这些接口中定义的public性质的类成员不能被COM访问但它们可以被其他的NET Framework对象访问要让COM能够访问特性和方法我们必须在类接口中定义它们使它们具有DispId属性并在类中实现这些特性和方法这些成员定义时的顺序也就是它们在COM中顺序要让COM访问类中的事件必须在事件接口中定义这些事件并赋予它们DispId属性事件接口不应当由类完成类只实现类接口(它可以实现不止一个接口但第一个接口是缺省接口)应当在缺省接口中实现需要让COM访问的方法和特性方法和特性必须被标识为public性质并符合在类接口中的定义需要让COM访问的事件也在缺省的类接口中完成它们也必须被标识为public性质并符合事件接口中的定义
在接口名字之前每个接口需要一个GUID特性要生成变个唯一的Guid需要运行guidgenexe工具软件并选择注册表格式
下面是一个类界面
[Guid(CBFFDBC)]
public interface DBCOM_Interface
{
[DispId()]
void Init(string userid string password);
[DispId()]
bool ExecuteSelectCommand(string selCommand);
[DispId()]
bool NextRow();
[DispId()]
void ExecuteNonSelectCommand(string insCommand);
[DispId()]
string GetColumnData(int pos);
}
COM事件接口
// 事件接口Database_COMObjectEvents
[Guid(CECACDCF)
InterfaceType(ComInterfaceTypeInterfaceIsIDispatch)]
public interface DBCOM_Events
{
}
下面是实际的类定义
[Guid(EEFBDeeABEDBEDEE)
ClassInterface(ClassInterfaceTypeNone)
ComSourceInterfaces(typeof(DBCOM_Events))]
public class DBCOM_Class : DBCOM_Interface
{
需要注意的是在类的前面需要设置下面的特性
ClassInterface(ClassInterfaceTypeNone)
ComSourceInterfaces(typeof(DBCOM_Events))]
ClassInterfaceTypeNone表示没有为该类生成类接口如果没有明确地实现接口类只能通过IDispatch提供后期绑定访问用户希望通过明确地由类实现的接口使外部对象能够访问类的功能这也是推荐的ClassInterfaceAttribute的设置
ComSourceInterfaces(typeof(DBCOM_Events))]确定许多作为COM事件向外部对象提供的接口在本文的例子中我们不对外部对象开放任何事件
下面是COM对象完整的源代码
using System;
using SystemRuntimeInteropServices;
using SystemIO;
using SystemText;
using SystemDataSqlClient;
using SystemWindowsForms ;
namespace Database_COMObject
{
[Guid(CBFFDBC)]
public interface DBCOM_Interface
{
[DispId()]
void Init(string userid string password);
[DispId()]
bool ExecuteSelectCommand(string selCommand);
[DispId()]
bool NextRow();
[DispId()]
void ExecuteNonSelectCommand(string insCommand);
[DispId()]
string GetColumnData(int pos);
}
// 事件接口Database_COMObjectEvents
[Guid(CECACDCF)
InterfaceType(ComInterfaceTypeInterfaceIsIDispatch)]
public interface DBCOM_Events
{
}
[Guid(EEFBDeeABEDBEDEE)
ClassInterface(ClassInterfaceTypeNone)
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=;
myConnection = new SqlConnection(myConnectString);
myConnectionOpen();
MessageBoxShow(CONNECTED);
}
catch(Exception e)
{
MessageBoxShow(eMessage);
}
}
public bool ExecuteSelectCommand(string selCommand)
{
if ( myReader != null )
myReaderClose() ;
SqlCommand myCommand = new SqlCommand(selCommand);
myCommandConnection = myConnection;
myCommandExecuteNonQuery();
myReader = myCommandExecuteReader();
return true ;
}
public bool NextRow()
{
if ( ! myReaderRead() )
{
myReaderClose();
return false ;
}
return true ;
}
public string GetColumnData(int pos)
{
Object obj = myReaderGetValue(pos);
if ( obj == null ) return ;
return objToString() ;
}
public void ExecuteNonSelectCommand(string insCommand)
{
SqlCommand myCommand = new SqlCommand(insCommand myConnection);
int retRows = myCommandExecuteNonQuery();
}
}
}
在创建COM对象前我们必须向COM Interop注册该对象右击方案管理器中的工程名字点击快捷菜单上的属性选项然后再点击配置>创建扩展output小节将Register for COM Interop选项的值设置为true这样一个COM对象就能够与可管理性应用程序进行交互
为了使COM对象能够被外部对象调用类库组合必须有一个强名字创建强名字需要用到SNEXE名字
sn k Database_COM_Keysnk
打开AssemblyInfocs并修改下面一行的内容
[assembly: AssemblyKeyFile(Database_COM_Keysnk)]
创建对象创建对象会生成一个可以被导入到可管理性或非可管理性代码中的类库
第二部分使用Visual C++创建访问COM对象的客户端软件
·使用VC++开发环境创建一个简单的工程
·使用#import directive导入类型库
·在界面中创建一个Smart Pointer从接口中执行COM类提供的功能确保在应用程序加载时添加CoInitialize()调用
CoInitialize(NULL);
Database_COMObject::DBCOM_InterfacePtr p(__uuidof(Database_COMObject::DBCOM_Class));
db_com_ptr = p ;
db_com_ptr>Init(scott tiger);
下面的代码对Customers数据库表执行一个SQL命令返回给定ID的客户的信息
char cmd[];
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();
p = mData ;
m_address = (CString)p ;