C#开发ActiveX插件
来源:互联网 发布:淘宝折800报名要求 编辑:程序博客网 时间:2024/06/05 16:28
刚毕业来到公司主管安排个任务 就是封装一个第三方OCX包,开发个网页插件提供内部工作人员使用。经过了解OCX就是ActiveX插件,但试过网上的方法用都报错“对象没有此属性或方法”(已regsvr32注册),查看过方法发现没有构造函数,不知道是不是因为用VB编写的原因。无奈之下想到封装为WinForm下的dll再封装为ActiveX,最后委屈求全实现了需要的功能,现在ActiveX基本是被抛弃了吧?网上能找到的教程都是N年前的,而且关于C#都特别少,所以借鉴了很多博文,特此记录下来。
好像话太多了,直接上步骤:
运行环境:Windows8.1 + VS2013
1、用 AxImp.exe 将OCX转换为WinForm可承载的控件
aximp c:\systemroot\system32***.ocx /source
aximp 在路径 C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools
/source 生成 .cs文件
之后在目录下生成 .dll 、Ax.dll 、 *.cs三个文件。
2、用于测试,注册OCX包:
regsvr32 c:\windows\SysWOW64***.ocx
虽然主要是引用第一步生成的dll,但必须注册OCX包。
3、将ActiveX添加到WinForm可承载窗体,也就是继承UserControl的类。且必须BeginInit、EndInit:
UserControl ctl = new UserControl();AxyourActiveX activeX = new AxyourActiveX();activeX.BeginInit();ctl.Controls.Add(cti);activeX.EndInit();
这里有个小插曲,我原本这样写
public class CustomActiveX : UserControl{ public CustomActiveX() { AxyourActiveX activeX = new AxyourActiveX(); activeX.BeginInit(); this.Controls.Add(cti); activeX.EndInit(); }}
理论上好像没问题,但跑起来报错(COMException),可能是当前的类运行在web上面的时候本身已经变成COM组件,所以出错,一定要注意。
4、实现IObjectSafety,声明为安全ActiveX,声明后IE会提示用户是否运行,而不是拦截。
接口: [ComImport, Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IObjectSafety { [PreserveSig] int GetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions, [MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions); [PreserveSig()] int SetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] int dwOptionSetMask, [MarshalAs(UnmanagedType.U4)] int dwEnabledOptions); }
实现:private const string _IID_IDispatch = "{00020400-0000-0000-C000-000000000046}"; private const string _IID_IDispatchEx = "{a6ef9860-c720-11d0-9337-00a0c90dcaa9}"; private const string _IID_IPersistStorage = "{0000010A-0000-0000-C000-000000000046}"; private const string _IID_IPersistStream = "{00000109-0000-0000-C000-000000000046}"; private const string _IID_IPersistPropertyBag = "{37D84F60-42CB-11CE-8135-00AA004BB851}"; private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001; private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002; private const int S_OK = 0; private const int E_FAIL = unchecked((int)0x80004005); private const int E_NOINTERFACE = unchecked((int)0x80004002); private bool _fSafeForScripting = true; private bool _fSafeForInitializing = true; public int GetInterfaceSafetyOptions(ref Guid riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions) { int Rslt = E_FAIL; string strGUID = riid.ToString("B"); pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA; switch (strGUID) { case _IID_IDispatch: case _IID_IDispatchEx: Rslt = S_OK; pdwEnabledOptions = 0; if (_fSafeForScripting == true) pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER; break; case _IID_IPersistStorage: case _IID_IPersistStream: case _IID_IPersistPropertyBag: Rslt = S_OK; pdwEnabledOptions = 0; if (_fSafeForInitializing == true) pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA; break; default: Rslt = E_NOINTERFACE; break; } return Rslt; } public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions) { int Rslt = E_FAIL; string strGUID = riid.ToString("B"); switch (strGUID) { case _IID_IDispatch: case _IID_IDispatchEx: if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_CALLER) && (_fSafeForScripting == true)) Rslt = S_OK; break; case _IID_IPersistStorage: case _IID_IPersistStream: case _IID_IPersistPropertyBag: if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_DATA) && (_fSafeForInitializing == true)) Rslt = S_OK; break; default: Rslt = E_NOINTERFACE; break; } return Rslt; }
一字不漏贴上就ok,具体作用暂不探究。
5、实现注册时注册表的操作:
[ComRegisterFunction()] public static void RegisterClass(string key) { // Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it StringBuilder sb = new StringBuilder(key); sb.Replace(@"HKEY_CLASSES_ROOT\", ""); // Open the CLSID\{guid} key for write access RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true); // And create the 'Control' key - this allows it to show up in // the ActiveX control container RegistryKey ctrl = k.CreateSubKey("Control"); ctrl.Close(); // Next create the CodeBase entry - needed if not string named and GACced. RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true); inprocServer32.SetValue("CodeBase", Assembly.GetExecutingAssembly().CodeBase); inprocServer32.Close(); // Finally close the main key k.Close(); } [ComUnregisterFunction()] public static void UnregisterClass(string key) { StringBuilder sb = new StringBuilder(key); sb.Replace(@"HKEY_CLASSES_ROOT\", ""); // Open HKCR\CLSID\{guid} for write access RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true); // Delete the 'Control' key, but don't throw an exception if it does not exist k.DeleteSubKey("Control", false); // Next open up InprocServer32 RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true); // And delete the CodeBase key, again not throwing if missing k.DeleteSubKey("CodeBase", false); // Finally close the main key k.Close(); }
6、这样就差不多了,业务需要 ActiveX回调数据到JavaScript上,百度activex调用js得到的方法:
//获取html的window对象Type typeIOleObject = this.GetType().GetInterface("IOleObject", true);object oleClientSite = typeIOleObject.InvokeMember("GetClientSite",BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public, null, this, null);IOleClientSite oleClientSite2 = oleClientSite as IOleClientSite;IOleContainer pObj;oleClientSite2.GetContainer(out pObj);IHTMLDocument pDoc2 = (IHTMLDocument)pObj;HTMLWindow2Class htmlWin = (HTMLWindow2Class)pDoc2.Script;string jsCode = string.Format("{0}({1})", func, param);this._htmlWindows.execScript(jsCode, "JScript");
这里的IOleClientSite、IOleContainer需要自己生成接口:
[ComImport, Guid("0000011B-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] public interface IOleContainer { void EnumObjects([In, MarshalAs(UnmanagedType.U4)] int grfFlags, [Out, MarshalAs(UnmanagedType.LPArray)] object[] ppenum); void ParseDisplayName([In, MarshalAs(UnmanagedType.Interface)] object pbc, [In, MarshalAs(UnmanagedType.BStr)] string pszDisplayName, [Out, MarshalAs(UnmanagedType.LPArray)] int[] pchEaten, [Out, MarshalAs(UnmanagedType.LPArray)] object[] ppmkOut); void LockContainer([In, MarshalAs(UnmanagedType.I4)] int fLock); } [ComImport, Guid("00000118-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IOleClientSite { void SaveObject(); void GetMoniker(uint dwAssign, uint dwWhichMoniker, object ppmk); void GetContainer(out IOleContainer ppContainer); void ShowObject(); void OnShowWindow(bool fShow); void RequestNewObjectLayout(); }
但奇怪的事情来了,我的ActiveX并没有继承和实现这两个接口,但上面的
Type typeIOleObject = this.GetType().GetInterface(“IOleObject”, true);
并没有出错,而是获取到对象了。
7、到最后设置当前项目属性
1、 -> 应用程序 -> 程序集信息 -> 勾上 使程序集COM可见。
2、 -> 生成 -> 勾上 为COM互操作注册 。
测试成果:
方法一、手动注册我们的activex:
regasm c:/windows/SysWOW64/CustomActiveX.dll
路径: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe
注意:c#编的ActiveX一定要用regasm,不是regsvr32。
方法二:制作安装包
使用InstallShield Limited Edition for Visual Studio 2013,参考http://www.cnblogs.com/flydoos/p/3430922.html
主要部分:
Installation Requirements - 设置我们需要的 .Net框架。
Application Files - 添加主输出、将项目涉及的程序集添加进来(包括ocx)、将COM组件设置属性为“Extract COM
information”(网上的教程都说是”Self-Register”,但我安装时会出问题。)
2.Specify Application Data > Redistributables - 将.Net框架打包到安装文件
6.Prepare for Release > Release CD_ROM-Setup.exe-InstallShield Prerquisites Location设为Extract From Setup.exe 、SingleImage也一样。(这里的CD_ROM会生成.msi和exe文件,打包为cab需要用,SingleImage只会生成一个setup.exe)
调用:
<object id="obj" classid="clsid:F590031C-04A1-4100-921D-728340D7A21D" width="550" height="450"></object>clsid 为我们ActiveX的GUID,可以用VS-工具-创建GUID,快速生成。
cab打包方法:
添加install.inf
[version]signature="$CHICAGO$"AdvancedINF=2.0[Setup Hooks]hook1=hook1[hook1]run=msiexec.exe /i "%EXTRACT_DIR%\ActiveXSetup.msi" /qn
build.bat
"cabarc.exe" n test.cab ActiveXSetup.msi install.inf
因为打包是用.msi文件,而我需要将.net框架打包到客户端,所以就直接使用setup.exe了,大概68M左右。到此已可以实现我的需求,如果想签名发布,可以google一下也很多例子。
我本来不是做web开发的,只是懂一点点c# 临时分配了任务,所以总结得不太好和很多知识点都没有深究,但希望能帮助大家。第一篇blog,以后继续努力。
- C#开发ActiveX插件
- 使用c#开发activeX插件
- C# 实现ActiveX插件
- C# 实现ActiveX插件
- 浏览器插件之C#开发"ActiveX"(一)
- 浏览器插件之C#开发"ActiveX"(二)
- c#开发activex实战
- C#开发ActiveX控件
- C# ActiveX控件开发
- vs2010 c# activex 开发
- vs2010 c# activex 开发
- c# activeX开发
- C# 开发Activex
- C#开发ActiveX控件
- 内嵌Activex的Activex插件开发
- 浏览器插件之ActiveX开发
- 浏览器插件之ActiveX开发-打包插件
- 使用C#开发ActiveX控件
- Docker周报2015年1月(下)
- 翻译经典之《Cisco Lan Switching》第六章 理解生成树(二): 什么是生成树、为何要使用生成树?
- ITWorld:2014年全球最杰出的14位编程天才
- Mechanize例子
- Android NDK 学习(1) 搭建开发环境
- C#开发ActiveX插件
- windows及linux平台下安装配置memcached
- lib 和 dll 的区别、生成以及使用详解
- Android ADB 端口占用问题解决方案
- 20150209学习总结
- 【低功耗设计学习笔记】(二)By-passing & Clock Gating
- GPU虚拟化笔记
- linux环境下安装memcached及客户端常用命令
- 2015.2.8实验室日记-纪逸清