在本文中,将探讨如何创建一个简单的COM对象,并使用VC++客户端访问该对象以实现数据库操作。将使用C#来创建COM对象,并使用VC++6.0或VC++.NET环境来创建客户端。
COM对象通常是一个类库(ClassLibrary),生成DLL文件。要在Visual Studio开发环境中创建一个简单的COM对象,选择“文件”->“新建”->“项目”->“Visual C#项目”->“类库”。创建一个名为Database_COMObject的项目。
为了将C#对象暴露给COM世界,需要满足以下条件:
为了将属性和方法暴露给COM,必须在类接口上声明它们,并使用DispId属性标记它们,并在类中实现它们。事件必须在事件接口上声明,并使用DispId属性标记。类不应实现这个接口。类实现类接口(它可以实现多个接口,但第一个实现将是默认的类接口)。在这里实现暴露给COM的方法和属性。它们必须被标记为public并且必须与类接口中的声明匹配。
每个接口都需要在接口名称前设置一个GUID属性。要生成唯一的GUID,请使用guidgen.exe工具并选择“注册表格式”。
以下是接口类的样子:
[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);
}
对于COM事件:
[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
{
// 类的实现
}
注意类前的属性设置:
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(DBCOM_Events))]
ClassInterfaceType.None表示不为类生成类接口。如果没有显式实现接口,类将只通过IDispatch提供后期绑定访问。这是ClassInterfaceAttribute的推荐设置。ComSourceInterfaces(typeof(DBCOM_Events)]标识作为COM事件源暴露的接口列表。在例子中,没有暴露任何事件。
以下是完整的COM对象源代码:
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);
}
[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;
private 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互操作注册对象。要这样做,请在解决方案资源管理器中右键单击项目名称。单击属性。单击配置->构建。展开输出部分。将“注册为COM互操作”设置为true。这表明托管应用程序将公开一个COM对象(一个COM可调用包装器),允许COM对象与托管应用程序交互。
为了使COM对象被公开,类库程序集还必须具有强名称。要创建强名称,请使用SN.EXE工具。
sn -k Database_COM_Key.snk
打开AssemblyInfo.cs并修改该行:
[assembly: AssemblyKeyFile("Database_COM_Key.snk")]
构建对象。构建还会产生一个类型库,可以导入到托管或非托管代码中。
已经在VC++ 6.0和VC++ .NET环境中测试了COM对象。使用VC++开发环境创建一个简单的项目。使用#import指令导入类型库。创建一个智能指针到Interface.Execute,这是接口公开的函数。确保在应用程序加载时添加CoInitialize()调用。
以下是客户端代码示例:
CoInitialize(NULL);
Database_COMObject::DBCOM_InterfacePtr db_com_ptr(__uuidof(Database_COMObject::DBCOM_Class));
db_com_ptr->Init("scott", "tiger");
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;