65.9K
CodeProject 正在变化。 阅读更多。
Home

在 C# 中构建 COM 对象

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (49投票s)

2004 年 8 月 3 日

4分钟阅读

viewsIcon

413847

downloadIcon

2

在 C# 中构建 COM 对象。

引言

本文涵盖的主题包括:

  • 在 C# 中创建简单的 COM 对象(使用 COM Interop 属性)。
  • 通过 VC++ 客户端访问 COM。(我已使用 VC++6.0 和 VC++ .NET 进行测试)。客户端使用类型库(.TLB 文件)。

为了方便开发人员使用和测试此代码,我使用了 SQL Server 数据库默认安装中内置的 **Northwind** 数据库。

  • 修改 COM 对象中的 SQL Server 名称以连接到您的 SQL Server。
  • 此外,我还创建了一个默认的用户 ID 和密码 **scott / tiger** 来连接数据库。您可以创建此用户,或使用现有的 ID / 密码。

第一部分:在 C# 中创建简单的 COM 对象

COM 对象是 **ClassLibrary** 类型。COM 对象会生成一个 DLL 文件。要在 VS 开发环境中创建简单的 COM 对象,请选择...

New->Project->Visual C# Projects ->Class Library

创建一个名为 **Database_COMObject** 的项目。

**请记住**:将 VC# 对象暴露给 COM 世界需要遵循以下要求...

  • 类必须是 public 的。
  • 属性、方法和事件必须是 public 的。
  • 属性和方法必须在类接口上声明。
  • 事件必须在事件接口上声明。

类中未在这些接口上声明的其他公共成员对 COM 不可见,但对其他 .NET Framework 对象可见。要将属性和方法暴露给 COM,您必须在类接口上声明它们,并用 DispId 属性标记它们,并在类中实现它们。成员在接口中声明的顺序就是 COM **vtable** 中使用的顺序。要从您的类中暴露事件,您必须在事件接口上声明它们,并用 DispId 属性标记它们。该类不应实现此接口。该类实现类接口(它可以实现多个接口,但第一个实现将是默认类接口)。在此实现暴露给 COM 的方法和属性。它们必须标记为 public,并且必须与类接口中的声明匹配。此外,在此声明类引发的事件。它们必须标记为 public,并且必须与事件接口中的声明匹配。

每个接口都需要在接口名称之前设置一个 GUID 属性。要生成唯一的 GUID,请使用 guidgen.exe 工具并选择 **Registry Format**。

接口类看起来是这样的...

    [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 事件...

    // // 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
    {

请注意类前面的以下属性设置

    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);
    }

    // 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。为此,请右键单击“解决方案资源管理器”中的项目名称。单击“属性”。单击“配置”->“生成”。展开输出部分。将“注册 COM Interop”设置为“true”。

指示您的托管应用程序将公开一个 COM 对象(COM 可调用包装器),该对象允许 COM 对象与您的托管应用程序进行交互。

为了暴露 COM 对象,您的类库程序集也必须具有强名称。要创建强名称,请使用实用程序 SN.EXE

sn -k Database_COM_Key.snk

打开 AssemblyInfo.cs 并修改行

[assembly: AssemblyKeyFile("Database_COM_Key.snk")]

构建该对象。构建还会生成一个类型库,可以将其导入到您的托管或非托管代码中。

第二部分:使用 Visual C++ 创建客户端以访问此 COM 对象

我已使用 VC++ 6.0 和 VC++ .NET 环境测试了此 COM 对象。使用 VC++ 开发环境创建一个简单的项目。使用 #import 指令导入类型库。创建一个指向 Interface.Execute 的智能指针,这是从接口公开的函数。确保在应用程序加载时添加 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[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 ;
© . All rights reserved.