用C#编写COM服务器

C# 注册COM+组件步骤 1. 编写Com+组件在建立C#工程之后必须要给类一个强名称.    要创建强名称,可以使用 sn 工具.此命令行工具有许多选项,在命令提示符处键入 sn /? 可以查看所有选项.对程序集签名所需的选项是“-k”,它将创建一个密钥文件.默认情况下,该文件使用 .snk 作为扩展名.例如,要创建一个名为 MyCom.snk 的密钥文件,可以使用以下命令行:sn -k MyCom. 阅读详情

 

用C#编写COM服务器(答raffles同学的提问)


本文是为了回答raffles同学的提问(c# 程序间数据的相互调用 GetObject(工程名.Application))而写。
是qguai的原创作品,转载请注明出处。

因为raffles的标题不是很贴切,所以另起一题,以便有兴趣的同学查阅。

本文不是COM的教程,有关COM的资料,请查阅MSDN。
为了与raffles的问题相应,客户代码用VB(A)/VBScript写出。

1、COM的客户(client)是如何得到COM对象(COM object)的

COM客户要使用COM对象,就必须跟COM服务器要。

先看一个例子:
'VB Code
set ie = CreateObject("InternetExplorer.Application")
ie.Navigate2 "www.itpub.net"
ie.Visible = true
set ie = Nothing

我们叫CreateObject帮我们要一个ID为"InternetExplorer.Application"的COM对象,然后叫这个COM对象去为我们工作。
CreateObject找谁要呢?当然是IE了(IExplorer.exe)。IExplorer.exe就是一个COM服务器。
COM服务器是要生产COM对象的。在哪儿生产?在工厂里:)(联想一下设计模式中的Factory)

COM是通过接口(interface)与外界打交道的(联想一下OO设计中的“Code to an Interface, Not to an Implementation”原则)
负责生产COM对象的接口就是IClassFactory。其实CreateObject就是找IE要一个工厂,然后叫工厂生产一个对象。
如果是一个In-Process的COM服务器(DLL),就调用DLL输出的DllGetClassObject方法。如果是Out-of-Process的COM服务器(EXE),则这个EXE要负责向系统报告它的类工厂,然后系统转交给客户。
当然,这些都是在WIN32 API层面上的事情,C#开发员先不要着急。

2、用C#编写In-Process的COM服务器(DLL)
严格来说,用C#编写出来的DLL只是一个.NET程序集(assembly),不是COM服务器。
要使一个.NET DLL成为一个COM服务器,要借助中间人mscoree.dll。
mscoree.dll是谁呀?呵呵,就是大名鼎鼎的Microsoft .NET Runtime Execution Engine。

好了,先来看看我们的.NET DLL怎么写(这里假设是.NET 2.0以上,各位同学知道如何使用VS2003或2005或2008):
做一个C#的DLL工程Project1。
namespace qguai
{
    public interface qgIApplication
    {
        string GetText();

    }
    [ClassInterface(ClassInterfaceType.None)]
    [ComVisible(true)]
    public class qgApplication : qgIApplication
    {
        public string GetText()
        {
            Debug.WriteLine("GetText called.");
            //你可以用各种方法取得数据
        return ".NET class as coclass!";
        }
    }
}
编译成Project1.DLL。别忘了,要签发成为强名称的程序集(strong-named assembly)。

用RegAsm.exe工具注册我们的类: RegAsm Project1.DLL /codebase

用VB试试:(最简单的方法就是用记事本建一个test.vbs文件,把下面四行代码拷入保存。然后双击这个test.vbs就看到结果了。)
set app = createobject("qguai.qgApplication")
str = app.GetText()
msgbox str
set app = nothing

做In-Process COM服务器,就这么简单。当然,你如果想深入,则大有天地。
raffles同学请注意了,你如果是在IDE里调试,不要按F5。你要把你的调试程序编译成EXE,然后去运行那个EXE。

3、中间人mscoree.dll

为什么一个.NET类,加两个属性,就可以成为一个COM类了呢?
那是因为有了mscoree.dll的帮忙。mscoree.dll就是一个大的COM服务器。
先来看看它输出什么:

C:/WINDOWS/system32>dumpbin /exports mscoree.dll
...
         58   18 00011032 DllCanUnloadNow = _DllCanUnloadNow@0
         59   19 00003E1F DllGetClassObject = _DllGetClassObject@12
         60   1A 00019DCB DllRegisterServer = _DllRegisterServer@0
         61   1B 00019E61 DllUnregisterServer = _DllUnregisterServer@0
...
看到DllGetClassObject了没有?当我们set app = createobject("qguai.qgApplication")的时候,就是这个DllGetClassObject给我们提供类工厂。
理论上说,只要一个DLL输出DllCanUnloadNow和DllGetClassObject,就算是一个COM服务器了。
当然了,你不能光挂职不干活,对吧?人家要工厂的时候,你要有工厂才行。人家要产品的时候,你要能生产才行。
那么mscoree.dll提供的工厂怎么知道生产出我们要的"qguai.qgApplication"?

其实,当你用RegAsm.exe注册我们的.NET类的时候,相关信息就被写在注册表里面了。
请看:


[HKEY_CLASSES_ROOT/qguai.qgApplication]
@="qguai.qgApplication"

[HKEY_CLASSES_ROOT/qguai.qgApplication/CLSID]
@="{55345299-3217-3EA7-8FA3-84A1D5D6C642}"

[HKEY_CLASSES_ROOT/CLSID/{55345299-3217-3EA7-8FA3-84A1D5D6C642}]
@="qguai.qgApplication"

[HKEY_CLASSES_ROOT/CLSID/{55345299-3217-3EA7-8FA3-84A1D5D6C642}/InprocServer32]
@="mscoree.dll"
"ThreadingModel"="Both"
"Class"="qguai.qgApplication"
"Assembly"="qguai, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5f281c1aa508859f"
"RuntimeVersion"="v2.0.50727"
"CodeBase"="file:///C:/Projects/qguai/bin/Debug/Project1.dll"

[HKEY_CLASSES_ROOT/CLSID/{55345299-3217-3EA7-8FA3-84A1D5D6C642}/ProgId]
@="qguai.qgApplication"

[HKEY_CLASSES_ROOT/CLSID/{55345299-3217-3EA7-8FA3-84A1D5D6C642}/Implemented Categories/{62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}]

根据注册表信息,mscoree.dll知道去哪找我们的代码。"qguai.qgApplication"只是我们类的程序标识。
真正能唯一标识我们的类的,是这个叫CLSID的东西{55345299-3217-3EA7-8FA3-84A1D5D6C642}。
这一串数字,就是C#中的Guid结构中装的东西。除非你真的需要特定的CLSID(class id),否则还是让RegAsm.exe为你自动生成为好。
RegAsm.exe其实是调用了System.Runtime.InteropServices.RegistrationServices的RegisterAssembly方法来注册的。
你也可以自己写一个C#小程序调用这个方法,而不用RegAsm.exe。
System.Runtime.InteropServices.RegistrationServices这个类在我们编写Out-of-Process COM服务器的时候很有用。
请先预习一下,我们下面就会用到。

4、用C#编写Out-of-Process的COM服务器(EXE)

前面说过,一个DLL可以输出一个DllGetClassObject函数,让系统/客户调用,以便生产COM对象。
但是,一个可独立运行的EXE程序,是不输出DllGetClassObject的。那么它是如何让客户得到对象工厂的呢?
它必须在一开始运行时,调用CoRegisterClassObject,向系统注册它的工厂。而且必须快,在启动的一两分钟内要完成注册。
当程序退出运行时,它必须调用CoRevokeClassObject,向系统注销它的工厂。

幸运的是,我们的C#程序不用去做P/Invoke。我们只须利用RegistrationServices的服务就行了。
RegistrationServices有两个方法,对应于以上两个API函数:RegisterTypeForComClients和UnregisterTypeForComClients。

下面我们就来看看具体怎么做:
为了简便起见,咱们就以一个控制台程序为例。

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace qguai
{
    public interface qgIApplication2
    {
        string GetText();

    }
    [ClassInterface(ClassInterfaceType.None)]
    [ComVisible(true)]
    public class qgApplication2 : qgIApplication2
    {
        public string GetText()
        {
            Debug.WriteLine("GetText called.");
            //你可以用各种方法取得数据
            //比如象EXCEL一样,取某一例的数据,取一个文本框的内容,等等
            return ".NET class as coclass from COM LocalServer!";
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting COM server ...");
            //程序启动,咱们马上注册要当作COM对象的类
            int cookie = 0;
            RegistrationServices rs = new RegistrationServices();
            cookie = rs.RegisterTypeForComClients(typeof(qgApplication2), RegistrationClassContext.LocalServer, RegistrationConnectionType.MultipleUse);
            Debug.Assert(cookie != 0);
            //现在可做你想做的事情了,如果是Windows程序,你就可以显示你的窗口
            Console.WriteLine("COM server has started.");
            Console.WriteLine("Press Enter to exit.");
            Console.ReadLine();

            //程序结束了,要注销我们已经注册过的类
            if (cookie != 0)
            {
                rs.UnregisterTypeForComClients(cookie);
            }

        }
    }
}

编译成一个可执行文件ConsoleApplication1.exe(也要strong-named的)。
如何注册这个ConsoleApplication1.exe呢?还是用RegAsm.exe吧;但是注册完之后,要手工修改一下。
因为RegAsm.exe会把它注册成InprocServer32,而我们的EXE应该是LocalServer32。

你可以用下面的结构注册,相应修改路径和CLSID值。
REGEDIT4

[HKEY_CLASSES_ROOT/qguai.qgApplication2]
@="qguai.qgApplication2"

[HKEY_CLASSES_ROOT/qguai.qgApplication2/CLSID]
@="{7690C214-8891-3DF8-BA1E-609A4754F9C1}"

[HKEY_CLASSES_ROOT/CLSID/{7690C214-8891-3DF8-BA1E-609A4754F9C1}]
@="qguai.qgApplication2"

[HKEY_CLASSES_ROOT/CLSID/{7690C214-8891-3DF8-BA1E-609A4754F9C1}/LocalServer32]
@="C:/tmp/ConsoleApplication1/bin/Debug/ConsoleApplication1.exe"

[HKEY_CLASSES_ROOT/CLSID/{7690C214-8891-3DF8-BA1E-609A4754F9C1}/ProgId]
@="qguai.qgApplication2"

测试一下test2.vbs:
set app = createobject("qguai.qgApplication2")
str = app.GetText()
msgbox str
set app = nothing
当你运行test2.vbs时,你会看到ConsoleApplication1.exe被系统启动,你也将得到你想要的COM对象。

当然了,一个完整的COM服务器,还要照顾到很多其它方面。
例如,如何让VB的GetObject得到我们的对象,等等。

有什么想法要交流的,欢迎给qguai@live.ca留言。

5、COM的类型库(Type Library)与.NET反射

 

 

 

将COM签发成具有强名称(Strong Name)的结果程序集(Assembly)
  
  将COM签发成具有强名称(Strong Name)的结果程序集(Assembly) 现有COM文件d:/sample/MyCom.dll,将其添加引用(Add Reference)至.Net应用程序中编译时出现错误:
  引用的程序集“MyCom”没有强名称
  Referenced assembly ‘MyCom ' does not have a strong name
  解决方法:
  1. 首先生成强名称公/私密钥对文件(Assembly Key File)
  在命令行下键入:
  sn –k d:/sample/myAssemblyKey.snk
  2. 签发COM成具有强名称的程序集
  在命令行下键入:
  tlbimp d:/sample/MyCom.dll /keyfile: d:/sample/myAssemblyKey.snk /out:d:/sample/myStrongNameCom.dll
  注:
   如有公用密钥对文件,则应使用公用文件而无须再自行生成

使用C#和HttpClient库编写的爬虫程序 这个程序使用HttpClient库发送一个HTTP GET请求到https://www.vip.com/。它还使用了一个代理服务器(在这个例子中是www.duoip.cn,端口8000)来连接到网站。如果请求成功,它将获取响应的内容并将其写入到一个名为"output.txt"的文件中。请注意,这只是一个基本的示例,实际的爬虫程序可能会更复杂,需要处理更多的细节和异常情况。以下是一个使用C#和HttpClient库编写的爬虫程序的示例,用于爬取https://www.vip.com/的图片。 阅读详情

相关推荐

26、遥感图像无监督变化检测方法解析

本文详细解析了遥感图像中的无监督变化检测方法,重点介绍了基于快速模糊 C-均值聚类的无监督变化检测方法(CDFFCM)在滑坡映射中的应用。CDFFCM 方法利用图像结构信息和高斯金字塔技术,提高了检测的准确性,减少了参数数量,并缩短了计算时间。该方法通过与传统方法的对比,展现出在高分辨率遥感图像滑坡检测中的显著优势。此外,文章还探讨了 CDFFCM 的未来发展趋势,包括技术融合、多模态数据应用以及智能化与自动化方向。

game4的博客 134

C#与java区别

C#与Java的区别   C#最引人的地方是它与Java的区别而不是其相似性。下面主要来介绍C#区别于Java的不同的运行特点及Java完全没有的特点。   中间语言   当MSIL被编译成最终的机器码时,微软公司在如何选择上是非常灵活的。微软公司很谨慎的对外宣称说MSIL不是解释型的,而是被编译成机器码。因为开发人员都有这样一个观念:Java程序天生就比C程序运行慢,所以这暗示着基于MSIL的程序优于解释型的Java字节码。当然,既然C#和其它MSIL产品编译器还未发布,那么这一点就还未证明,但是Java

weixin_46365638的博客 1102

光伏微电网蓄电池储能控制系统设计及动态性能分析

本文构建了光伏微电网蓄电池储能控制系统的完整理论体系。在系统架构方面,提出了基于直流母线电压平衡的多时间尺度能量管理理论,建立了光伏发电单元的二阶物理模型和MPPT动态跟踪算法。针对蓄电池系统,设计了融合二阶RC等效电路与扩展卡尔曼滤波的SOC估计方法,误差控制在3%以内。双向DC-DC变换器采用状态空间平均建模方法,推导了小信号传递函数,为闭环控制提供理论基础。该体系实现了从瞬时功率平衡到长期能量调度的多层级协同控制,为微电网稳定运行提供了系统的解决方案。

AI的内部世界的博客 164

关于《用C#编写ActiveX控件》的几点说明

在我的第一篇blog发表之后,得到大家的支持,我的工作得到了认可,感到很开心。当然,很多朋友也提出了很多出色的见地。我想在这里说明一下我对这些问题的理解。         我所完成的控件是一个用来获取客户端摄像头图片并保存在特殊文件夹里,然后上传到服务器的ActiveX控件。给大家一个截图。 显示图片的就是那个控件。     之所以当初想到用C#写ActiveX控件,一是自己不会用VB/C++写

无名剑 819

2KTO2K8-跨数据库服务器的操作

前几开遇到一个需求,写入2K的时候写到2K8,当然自已写程序是容易之极。可惜写入的程序不是我写的。只能在他写入数据库的时候操作了。还好他是调用存储过程,在执行原有过程之前,执行自已的代码吧!用链接服务器是不错的选择,操作也很方便。但现实不一定是这样。要跨服务器调用又要开DTS,开了又要开一堆不是默认的选项。调用2K8的存储过程还要设定一堆配置,这些都算了。DTS还要有时候出错,什么登陆不了啊之类,

蝴蝶君的记事本 1030

关于《用C#编写ActiveX控件》的几点说明 转载

在我的第一篇blog发表之后,得到大家的支持,我的工作得到了认可,感到很开心。当然,很多朋友也提出了很多出色的见地。我想在这里说明一下我对这些问题的理解。 我所完成的控件是一个用来获取客户端摄像头图片并保存在特殊文件夹里,然后上传到服务器的ActiveX控件。给大家一个截图。 显示图片的就是那个控件。 之所以当初想到用C#写ActiveX控件,一是自...

weixin_33928137的博客 66

SQL Server 2000 汉字数据简繁转换实例 (COM,.Net)

SQL Server 2000 汉字数据简繁转换实例 (COM,.Net)首先确认在 Windows (当然只是 SQL Server 的服务器端) 上安装了 .Net Framework 1.1 !确认成功安装后:1.用 sn -k 为该类库创建一个强名密钥文件:  在安装 Microsoft .Net Framework SDK 的所在目录下的 v1.1/Bin/ 子目录下执行如下命令

playyuer㊣www.Microshaoft.com I Love UltraEditPlus 5414

udp wpf 权限_WpfUdp 自己学习写的UDP调试助手,包括客户端和服务器程序, 通过可用 CSharp C#编程 240万源代码下载- www.pudn.com...

文件名称: WpfUdp下载 收藏√ [5 4 3 2 1]开发工具: C#文件大小: 64 KB上传时间: 2015-06-17下载次数: 0详细说明:自己学习写的UDP调试助手,包括客户端和服务器程序,调试通过可用-Learning to write your own UDP debugging assistant, including client and server pr...

weixin_39579468的博客 153

C#实现实时监测文件夹,把word/excel/ppt自动转为pdf

很久没更新了,前段时间在准备软考,最近也忙着单位新系统的开发。所以也就没更新了,等后面几个项目做好了,我在项目分享出来!! 由于新开发的信息系统,原来旧版的信息系统,在查看文件时,都需要下载到本地,然后在打开查看,这样比较繁琐,所以在新版信息系统里,用了pdfjs插件进行在线查看文档,那么问题来了,怎么样才能把用户上传上来的office文档转为pdf呢??这个问题我采取了用php调用office的com组件进行转换。转换出来的pdf文件跟源文件基本一致,算是比较完美。但是com组件总会出现这个那个的问题。最

一个孤独的Coder...在需求中成长,在压力中勇进;不怕夜深人静的夜晚,不怕空无一人的街道... 6438

Asp.netC#自动化调用Word的实例与总结

摘 要:本文通过一个实用例子完整演示了如何使用C#在ASP.NET里调用Word、自动化Word,并且总结了实际应用中发现的问题,最后提出了有效的解决方案。关键词:ASP.NET; C#; Word; 自动化; VBA; COM; 死进程;1. 建立工程在ASP.NET里操作Word的第一步就是添加COM引用到你的工程里,通过右键点击“解决方案资源管理器”的“引用”,添加引用。选择COM选项卡

mapc1988的专栏 341

一个用Com管理DNS的简单类

/ TODO: 在此处添加构造函数逻辑。#region IDisposable 成员。

biyusr的专栏 801

学习C#开源报表组件Seal Report(24:Seal Report Designer界面布局-20)

学习Seal Report Designer软件左侧面板的Views节点中Tab Control View控件的属性用途及示例

gc_2299的博客 219

华证ESG评级和得分(2009-2025年).xlsx

详细介绍及样例数据:https://blog.csdn.net/T0620514/article/details/159803673

上一篇: log4j的完全使用详解
下一篇: 用C#编写ActiveX控件(一)
seraph5186
博客等级 码龄21年 5粉丝 14原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值