c#中开发ActiveX的学习笔记

1.为什么要用ActiveX?

网页本身的功能是有限的,要想实现一些网页本身不支持的功能,比如:网页上的p2p视频播放,就得靠ActiveX这种古老的技术。

2.c#能开发ActiveX吗?

严格意义上讲,c#是不能生成纯正ocx控件的,我们在vs.net中新建项目时,也找不到专门的"ActiveX项目"新建项,最多也只就能新建"类库"得到一个dll而非ocx(因此我们也无法用传统的regsvr32来注册该dll),但是c#能开发com组件,activeX控件本质上讲跟com是一类技术,所以用c#开发"能够让网页调用的com类库"还是可行的。

3.开发步骤:

(1)新建一个类库
(2)修改项目的"属性",在“生成”选项中把“输出”中的“为com互操作注册”勾中,然后再到“应用程序”选项中找到“程序集信息”按钮,点击它,在弹出的界面中勾中“使程序集COM可见(M)”

(3)修改AssemblyInfo.cs,增加[assembly: AllowPartiallyTrustedCallers()],完整内容类似下面这样:

代码

 1 using System.Reflection;
 2 using System.Runtime.CompilerServices;
 3 using System.Runtime.InteropServices;
 4 using System.Security;
 5 
 6 // General Information about an assembly is controlled through the following 
 7 // set of attributes. Change these attribute values to modify the information
 8 // associated with an assembly.
 9 [assembly: AssemblyTitle("ActiveXDemo")]
10 [assembly: AssemblyDescription("")]
11 [assembly: AssemblyConfiguration("")]
12 [assembly: AssemblyCompany("Microsoft")]
13 [assembly: AssemblyProduct("ActiveXDemo")]
14 [assembly: AssemblyCopyright("Copyright ? Microsoft 2009")]
15 [assembly: AssemblyTrademark("")]
16 [assembly: AssemblyCulture("")]
17 [assembly: AllowPartiallyTrustedCallers()]
18 
19 // Setting ComVisible to false makes the types in this assembly not visible 
20 // to COM components.  If you need to access a type in this assembly from 
21 // COM, set the ComVisible attribute to true on that type.
22 [assembly: ComVisible(true)]
23 
24 // The following GUID is for the ID of the typelib if this project is exposed to COM
25 [assembly: Guid("bd585d12-7f22-4b3f-959f-18efbfc53f94")]
26 
27 // Version information for an assembly consists of the following four values:
28 //
29 //      Major Version
30 //      Minor Version 
31 //      Build Number
32 //      Revision
33 //
34 // You can specify all the values or you can default the Build and Revision Numbers 
35 // by using the '*' as shown below:
36 // [assembly: AssemblyVersion("1.0.*")]
37 [assembly: AssemblyVersion("1.0.0.0")]
38 [assembly: AssemblyFileVersion("1.0.0.0")]

(4)新建一个IObjectSafety接口文件IObjectSafety.cs,内容如下:

代码

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Text;
 4 using System.Runtime.InteropServices;
 5 
 6 namespace ActiveXDemo
 7 {
 8     [ComImport, GuidAttribute("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
 9     [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
10     public interface IObjectSafety
11     {
12         [PreserveSig]
13         int GetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions, [MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions);
14 
15         [PreserveSig()]
16         int SetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] int dwOptionSetMask, [MarshalAs(UnmanagedType.U4)] int dwEnabledOptions);
17     }
18 
19 }

该内容除命名空间可以更改外,其它内容都是固定的,不要修改
(5)新建一个:Windows Forms-->“用户控件”,我们的主要逻辑就写在这里(还可以在它上面随便放置其它windows常用控件,跟winForm开发一样),不过首先要修改类定义,让其实现我们刚才定义的接口

代码

  1 using System;
  2 using System.Runtime.InteropServices;
  3 using System.Threading;
  4 using System.Windows.Forms;
  5 
  6 
  7 namespace ActiveXDemo
  8 {
  9     [Guid("8d7d8518-ca58-4863-b94d-3c616fda7b35")]
 10     public partial class MyActiveX : UserControl,IObjectSafety
 11     {
 12         delegate void D(object obj);
 13 
 14         public MyActiveX()
 15         {
 16             InitializeComponent();
 17         }
 18 
 19         #region IObjectSafety 成员
 20 
 21         private const string _IID_IDispatch = "{00020400-0000-0000-C000-000000000046}";
 22         private const string _IID_IDispatchEx = "{a6ef9860-c720-11d0-9337-00a0c90dcaa9}";
 23         private const string _IID_IPersistStorage = "{0000010A-0000-0000-C000-000000000046}";
 24         private const string _IID_IPersistStream = "{00000109-0000-0000-C000-000000000046}";
 25         private const string _IID_IPersistPropertyBag = "{37D84F60-42CB-11CE-8135-00AA004BB851}";
 26 
 27         private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
 28         private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
 29         private const int S_OK = 0;
 30         private const int E_FAIL = unchecked((int)0x80004005);
 31         private const int E_NOINTERFACE = unchecked((int)0x80004002);
 32 
 33         private bool _fSafeForScripting = true;
 34         private bool _fSafeForInitializing = true;
 35 
 36         public int GetInterfaceSafetyOptions(ref Guid riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions)
 37         {
 38             int Rslt = E_FAIL;
 39 
 40             string strGUID = riid.ToString("B");
 41             pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
 42             switch (strGUID)
 43             {
 44                 case _IID_IDispatch:
 45                 case _IID_IDispatchEx:
 46                     Rslt = S_OK;
 47                     pdwEnabledOptions = 0;
 48                     if (_fSafeForScripting == true)
 49                         pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
 50                     break;
 51                 case _IID_IPersistStorage:
 52                 case _IID_IPersistStream:
 53                 case _IID_IPersistPropertyBag:
 54                     Rslt = S_OK;
 55                     pdwEnabledOptions = 0;
 56                     if (_fSafeForInitializing == true)
 57                         pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
 58                     break;
 59                 default:
 60                     Rslt = E_NOINTERFACE;
 61                     break;
 62             }
 63 
 64             return Rslt;
 65         }
 66 
 67         public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
 68         {
 69             int Rslt = E_FAIL;
 70             string strGUID = riid.ToString("B");
 71             switch (strGUID)
 72             {
 73                 case _IID_IDispatch:
 74                 case _IID_IDispatchEx:
 75                     if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_CALLER) && (_fSafeForScripting == true))
 76                         Rslt = S_OK;
 77                     break;
 78                 case _IID_IPersistStorage:
 79                 case _IID_IPersistStream:
 80                 case _IID_IPersistPropertyBag:
 81                     if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_DATA) && (_fSafeForInitializing == true))
 82                         Rslt = S_OK;
 83                     break;
 84                 default:
 85                     Rslt = E_NOINTERFACE;
 86                     break;
 87             }
 88 
 89             return Rslt;
 90         }
 91 
 92         #endregion
 93 
 94         private void MyActiveX_Load(object sender, EventArgs e)
 95         {
 96             
 97         }
 98 
 99 
100         public void Start(object obj) 
101         {
102             for (int i = 0; i < 10; i++)
103             {
104                 Thread t = new Thread(new ParameterizedThreadStart(ShowTime));
105                 t.Start(obj.ToString() + ",线程:" + i.ToString());                
106             }        
107         }
108 
109         private void button1_Click(object sender, EventArgs e)
110         {
111             Start("Hello World");
112         }
113 
114         void ShowTime(object obj)
115         {
116             if (this.listBox1.InvokeRequired)
117             {
118                 D d = new D(DelegateShowTime);
119                 listBox1.Invoke(d, obj);
120             }
121             else
122             {
123                 this.listBox1.Items.Add(obj);
124             }
125 
126             
127         }
128 
129 
130         void DelegateShowTime(object obj)
131         {
132             this.listBox1.Items.Add(obj);
133         }
134 
135 
136     }
137 }

#region IObjectSafety 成员 ... #endregion这一段的内容是固定的,不要修改,其它内容根据自己的业务要求自行修改,另外类前面要加上Guid的标识,以便网页调用时,能用CLSID="xxx"来调用

基本上这样弄完后,就可以在网页中,用类似下面这样的代码来本机调用了:

注意:c#定义的public方法,如果想直接让js调用,只能返回string,DateTime,int,double这一类基本值类型,其它返回类型比如array,object,在js中要么直接报错,要么得到null

代码

1 <object id="x" classid="clsid:8d7d8518-ca58-4863-b94d-3c616fda7b35"></object>
2 <hr />
3 <input type="button" value="调用ActiveX中的多线程方法" onclick="fnTest()" />
4 <script type="text/javascript">
5 var fnTest = function(){
6     var x = document.getElementById("x");
7     x.Start("这是js中的参数");
8 }
9 </script>

4.安装部署

前面已经提到了,c#开发的(伪)"ActiveX"控件并非纯正的ocx,所以只能用RegAsm.Exe xxx.dll来进行程序集的注册,这里要注意一点:在开发机上,项目编译后vs.net会自动将bin\debug\xxx.dll调用regasm注册,但在别人机器上就不行了,为了能在调试时模拟其它机器的运行结果,可以在编译后,手动用类似 regAsm.exe D:\MyDoc\ActiveXDemo\output\ActiveXDemo.dll /u 来反注册(在vs.net命令行模式下)

当然,如果您不勾选3.(2)中所说的“为com互操作注册”,vs编译时便不会自动注册,但是这样调试起来不太方便,另外注册/反注册时的RegAsm.exe要起开发环境中的版本一致(比如你开发时设置是64位版本,那么反注册也要用64位版本的RegAsm.exe)

另外,我们也不可能在每个客户机上手动用RegAsm.exe来帮客户注册,所以我们还得新建安装项目来做一个安装包,这个比较简单,直接新建一个"其他项目类型-->安装和部署-->安装项目"即可

然后在安装项目上,右键"添加"-->"项目输出"-->"主输出"-->在项目下拉框中选择activex所对应的项目即可.

注意:"主输出来自xxx"的属性栏中,有一个"Register"必须选择"vsdrpCOM"

另外还有一个问题,可能是我机器的个别现象,每次activex项目有修改时,建议最好手动清除安装项目debug目录下的文件,再重新生成安装项目,否则有时候会发现activex修改了,但是安装包中包含的dll还是未修改过的版本。
 

后话:c#开发的东西是运行于.net 框架之上的,就好比java开发的东西必须要java runtime才能运行一样,利用本文方法开发出来的dll也必须要安装.net框架才能跑起来,幸好最新的win7中已经集成了.net框架,当然您如果对于庞大的.net框架安装程序很敏感,仍然觉得纯正的ocx更好的话,建议还是用vb/delphi/c++这一类老牌的开发工具/语言实现。(可以参考我的另一篇重温delphi之:如何快速开发原生ActiveX控件)

 

示例源代码下载:http://files.cnblogs.com/yjmyzz/ActiveXDemo.rar

时间: 2024-09-28 05:23:11

c#中开发ActiveX的学习笔记的相关文章

Android开发艺术探索学习笔记(七)_Android

第七章 Android动画深入分析  Android的动画分为三种:View动画,帧动画,属性动画.帧动画属于View动画. 7.1 View动画 View动画的作用对象是View,共有四种动画效果:平移(Translate),缩放(Scale),旋转(Rotate),透明度(Alpha). 7.1.1 View动画的种类 View动画的保存路径:res/anim/filename.xml.XML格式语法如下: <?xml version="1.0" encoding="

Android开发艺术探索学习笔记(七)

第七章 Android动画深入分析 Android的动画分为三种:View动画,帧动画,属性动画.帧动画属于View动画. 7.1 View动画 View动画的作用对象是View,共有四种动画效果:平移(Translate),缩放(Scale),旋转(Rotate),透明度(Alpha). 7.1.1 View动画的种类 View动画的保存路径:res/anim/filename.xml.XML格式语法如下: <?xml version="1.0" encoding="

ctivex-MFC中开发ActiveX控件问题

问题描述 MFC中开发ActiveX控件问题 在VS2010中使用VC++的MFC建立ActiveX控件时,控件默认在整个控件区域画了一个椭圆.问:如何改变控件的大小,即使控件有一个自定义的默认大小?求教... 解决方案 MoveWindow等来控制控件大小. 解决方案二: 你可以定义一个属性表示椭圆所在的区域的大小.构造函数给它们默认值画图的代码据此绘出椭圆. 解决方案三: 这个,改变初始值就可以了啊 解决方案四: onwindowsize消息里面做点手脚. 解决方案五: 大小一般是在创建控件

Windows驱动开发工具 WDK 学习笔记(1)

目标:能够把电脑当作一个集成有高性能处理器的开发板用起来,当然,还自带了一个高级的操作系统Windows(必须的).总之,就是在一个带了操作系统的高性能开发板上的驱动程序开发. 性质:纯属业余爱好 1.昨天下载了WDK 7.1.0 Free版(From MS的正版,需要注册一个Windows Live ID,下载链接http://www.microsoft.com/downloads/en/confirmation.aspx?familyId=36a2630f-5d56-43b5-b996-76

flash中starling组件Feathers学习笔记

  最近在学习starling,然后觉得不可能都自己去写组件,听说有现在的支持starling的Feathers组件,就拿来研究了一下 这个好像是新出,没什么学习资料,都是英文的.部分笔记如下: 1.popups包下有三个类一个接口,对来实现弹出层 2.CalloutPopUpContentManager 比较简单,open方法里直接使用Callout.show(content, source)进行弹出 3.Callout控件功能类似于tip.它能弹出一个带箭头的指向指定displayobjec

跨平台移动开发与Hybrid学习笔记

最近做了iOS平台上Hybrid的调研,水平有限,结合以前尝试Hybrid的一点点经验做个小小的分析.概述了跨平台开发的的各种方案,并通过阅读Cordova-iOS和WebViewJavascriptBridge的源码,分析Hybrid的实现原理和设计. 跨平台开发 这可能是移动开发领域的一个迷梦,无数人在用不同的方式想做好这一件事情,现在看起来还没有一套解决方案可以一统江湖.移动领域的跨平台开发可以从几个层面来切入(以下观点给予多年前的调研和最近的整理,出错了欢迎指出). 交叉编译 交叉编译是

JavaWeb中Session对象的学习笔记_java

一.Session简单介绍 在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下).因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务. 二.Session和Cookie的主要区别 Cookie是把用户的数据写给用户的浏览器.Session技术把用户的数据写到用户独占的sessi

JavaScript中自定义事件的学习笔记

在web前端开发中,很多人可能不会用到js的自定义事件,但如果是做一个相对来说比较大的项目,尤其是多人协同开发的时候,自定义事件就显得很重要了.那么,什么是js中的自定义事件呢?我们先来看一个例子: 前端开发员A封装了一个函数:  代码如下 复制代码 function move(){     alert(a);  //以此来代表N行代码 } 过段时间,前端开发员B要在A的基础上丰富这个函数,于是,他会这样写:  代码如下 复制代码 function move(){     alert(a); 

linux中iptables防火墙设置学习笔记

以下内容总结自鸟哥的 Linux 私房菜 -- 服务器(第三版),同时推荐喜欢Linux的同学们学习阅读. For Linux Kernel 2.6+ I. 图解防火墙 图解防火墙 上面的图示很复杂喔!不过基本上你依旧可以看出来,我们的 iptables 可以控制三种封包的流向: 封包进入 Linux 主机使用资源 (路径 A): 在路由判断后确定是向 Linux 主机要求数据的封包,主要就会透过 filter 的 INPUT 链来进行控管: 封包经由 Linux 主机的转递,没有使用主机资源,