说明:

我是一个C#程序员,但是有一次一个需求只能用C/C++去写,恰好需要读取的数据存放在DB(SQL CE v3)里面,而我又不会C/C++(关键是用OleDB访问DB,这个实在是繁琐),所以催生了用C#写一个COM组件,用C/C++去调用的想法.可谓,很傻很天真.但是也是一种思路,如果MS提供C API的话,问题就简单多了.可是事实是,MS自己的.NET CF用着C API,给用户却暴露着COM API.....OK,言归正传.

主要内容:

  • 用C#创建一个简单的COM组件(通过COM Interop)
  • 用VC++写一个客户端去访问COM组件.客户端用TLB文件.

本着易于使用的目的,我把Northwind导入到了SQLServer,然后测试了我的代码.(the sake of simplycity这个不知道啥意思,难道是由于出现纸尿布....).

  • 修改COM组件里面的机器名为你的SQL Server的机器名.(2005以上需要 机器名\实例名)
  • 当然我在里面也创建了一个用户scott密码是tiger,用来连接数据库.你可以选择这个用户名,或者重新建一个.

Part I: 用C#创建一个简单的COM组件

COM对象是一种类库.COM组件将产生DLL文件.在VS环境里面创建COM组件请选择....

  File->New->Project->VisualC# Projects ->Class Library.

创建一个名为Database_COMObject的类库工程.

请记住:想要把C#对象当作COM对象需要以下几点...

  • class必须是public的
  • 属性,方法和事件必须是public
  • 属性和方法必须在Interface里面定义
  • 事件必须在事件的接口中

未在接口中定义的成员,而在实现里面是public的成员,对COM是不可见的,但是对其他的.NET程序是可见的.为了把属性和方法暴露给 COM,你必须在接口中定义他们,并且把他们用DispId属性标记,在class里面实现(.....).在接口里面定义的成员只是为了使用 vtable(虚函数表).要想暴露事件,你也必须把成员定义在事件接口里面并且标记DispId属性.类不需要实现此接口(???).类可以实现接口 (一个类可以实现多个接口,只有第一个接口才是默认的接口.).暴露给COM的那些属性方法其实就在类的实现里面.他们必须被标记为public,而且要符合接口里面的定义.Also, declare the events raised by the class here. They must be marked public and must match the declarations in the events interface. (这两句不知道具体的含义,代码里面也没看出端倪.)

每一个接口都要有一个GUID属性(我当时上学的时候,把他叫属性属性,或者定制属性,现在也不清楚到底叫什么..).你可以用guidgen.exe来产生一个GUID值.

这个接口就长这个样子:

    [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 {}

实现接口的类:

[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那里注册.

打开Solution Explorer->Properties->Configuration->Build->Expand the output section->Register for COM Interop改为True.

(我在VS 2008里面貌似不是这么操作的,工程上面点右键->属性->Build->最下面的Output->Register for COM Interop改为True).

表明,我要把Managed程序导出一个COM对象,并且COM对象可以和我们的托管程序交互.

为了(真正)导出COM对象,程序集还需要强命名,可以用sn.exe生成一个StrongName:

sn -k Database_COM_Key.snk

在AssemblyInfo.cs文件里面修改:

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

编译这个对象.会产生一个tlb文件,通过这个可以使Managed代码和Native代码都能访问你的COM对象.

Part II : 用VC++创建一个客户端去访问这个COM对象

我已经在VC++6.0和VC++.NET下面访问呢过改COM对象.

创建一个简单的工程,通过 #import directive导入type library.

创建一个只能指针指向接口的实例,执行那些导出函数,确保在程序加载的时候执行CoInitialize().

    CoInitialize(NULL);Database_COMObject::DBCOM_InterfacePtr p(__uuidof(Database_COMObject::DBCOM_Class));db_com_ptr = p ;db_com_ptr->Init("scott" , "tiger");

下面的代码通过一个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文.

我只能说我很悲剧,当初想法很好,只可惜,.NET CF下面,不支持用C#写一个COM组件.

我的口头禅就是:.NET CF除了慢,再没有其他优点.

转载于:https://www.cnblogs.com/SuperBrothers/archive/2012/10/29/2744933.html

【译文转帖】用C#写COM组件 Building COM Objects in C#相关推荐

  1. vue-class-component 以class的模式写vue组件

    vue英文官网推荐了一个叫vue-class-component的包,可以以class的模式写vue组件.vue-class-component(以下简称Component)带来了很多便利: 1.me ...

  2. vue中写svg组件svg图片加载不出来

    vue中写svg组件svg图片加载不出来 结构 首先要安装3个插件:svg-sprite-loader,svgo,svgo-loader npm install svg-sprite-loader - ...

  3. 记录一下小程序的手写签名组件

    文章目录 前言 一..js文件的内容 二..json文件的内容 三..wxml文件的内容 四..wxss文件的内容 五.要引用的.wxml文件的内容 六.要引用的.js文件的内容 七.总结 前言 由于 ...

  4. uniapp 手写签名组件

    组件地址: 手写签名组件,弹框签名,可配置签名,签名返回base64,签名专用 - DCloud 插件市场 # 欢迎使用 手写签名组件 **手写签名组件,签名后可以获取到base64编码,同时内置了弹 ...

  5. 学习用TypeScript写React组件

    为了折腾, 简单的学习了下TypeScript, 感觉确实不错. 也为了不断学习, 避免落伍, 所以就折腾不断. 前段时间用ES6,antd+dva写了一些demo, 发现antd使用TypeScri ...

  6. 脑洞神器Creator手写识别组件,三分钟上手!

    这两天有两位伙伴,通过微店购买了Shawn的数字大冒险,在此感谢大家的支持! 抱歉的是该工程没有文档,不少朋友是看中了其中的手写数字识别功能,在此我录制了一段小视频,可以帮助你快速上手手写数字识别组件 ...

  7. java月历组件_vue之手把手教你写日历组件

    ---恢复内容开始--- 1.日历组件 1.分析功能:日历基本功能,点击事件改变日期,样式的改变 1.结构分析:html 1.分为上下两个部分 2.上面分为左按钮,中间内容展示,右按钮 下面分为周几展 ...

  8. vue日历排班组件_vue之手把手教你写日历组件

    ---恢复内容开始--- 1.日历组件 1.分析功能:日历基本功能,点击事件改变日期,样式的改变 1.结构分析:html 1.分为上下两个部分 2.上面分为左按钮,中间内容展示,右按钮 下面分为周几展 ...

  9. 微信小程序自定义canvas手写签名组件

    需求如下图展示,因为小程序中有好几个地方使用签名,所以做成组件. 1.创建组件 signature.js const app = getApp(); Component({/*** 组件的属性列表*/ ...

最新文章

  1. 使用Python+OpenCV预测年龄与性别
  2. controller接收json数据_答疑 | 前后端分离,如何接收json数据?
  3. 2022年跨境品牌出海新玩法策略:Tiktok+速卖通平台运营必不可少
  4. 想学IT的必看!今年Android面试必问的这些技术面,架构师必备技能
  5. c++ cdi+示例_C ++中带有示例的本地类
  6. leetcode —— 1217. 玩筹码
  7. 深入理解 RecyclerView 系列之一:ItemDecoration
  8. Unieap3.5-Grid编辑列中数字与下拉改变
  9. mysql 内置存储过程_mysql 内置存储过程
  10. 【Vue】详解 SFC 与 vue-loader
  11. 关于雄安新区的一点观察和思考
  12. 常见EDA软件的license管理
  13. 交通分配四阶段法(一)
  14. 镁光ddr3布线规则_讨论一下DDR3 缓存的电压和频率
  15. seaborn库——分类图
  16. SGX 是什么技术?
  17. 微软自带比对神器,快速比对两个表格-高效好用又简单(99.9%的人没听说过)
  18. 深度linux15.4安装教程,国产操作系统深度Deepin15.5安装过程体验!
  19. Circuit Design 三极管驱动蜂鸣器电路 及 蜂鸣器两端电压正确但是不响的解决方案
  20. Android知识点 015 —— 2.3.9 CountDownTimer倒计时(补充 疯狂Android讲义)

热门文章

  1. 上传图片被防火墙拦截_Web安全:文件上传漏洞
  2. const与define相比优点_const与#define的区别、优点
  3. html里的swal添加倒计时,使用甜蜜警报插件的setInterval函数倒计时
  4. inum在linux中含义,linux
  5. python中数据类型为list_python_数据类型_list
  6. 楚留香手游系统互通的服务器,楚留香手游互通服务器汇总 哪些服能一起玩
  7. Docker容器化部署config-server无法直接访问
  8. 【camera】4.图像的颜色空间
  9. R6010-abort() has been called,Error openning file ../../modules/highgui/src/cap_ffmpeg_impl.hpp解决方法
  10. python3.8.0shell_Python 3.8.0 正式发布 更新内容