C#开发ActiveX插件
2015-02-09 22:56
148 查看
刚毕业来到公司主管安排个任务 就是封装一个第三方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:
这里有个小插曲,我原本这样写
理论上好像没问题,但跑起来报错(COMException),可能是当前的类运行在web上面的时候本身已经变成COM组件,所以出错,一定要注意。
4、实现IObjectSafety,声明为安全ActiveX,声明后IE会提示用户是否运行,而不是拦截。
一字不漏贴上就ok,具体作用暂不探究。
5、实现注册时注册表的操作:
6、这样就差不多了,业务需要 ActiveX回调数据到JavaScript上,百度activex调用js得到的方法:
这里的IOleClientSite、IOleContainer需要自己生成接口:
但奇怪的事情来了,我的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)
调用:
cab打包方法:
添加install.inf
build.bat
因为打包是用.msi文件,而我需要将.net框架打包到客户端,所以就直接使用setup.exe了,大概68M左右。到此已可以实现我的需求,如果想签名发布,可以google一下也很多例子。
我本来不是做web开发的,只是懂一点点c# 临时分配了任务,所以总结得不太好和很多知识点都没有深究,但希望能帮助大家。第一篇blog,以后继续努力。
好像话太多了,直接上步骤:
运行环境: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插件-aspx中嵌入
- c# 的插件开发
- 用C#开发MSN插件程序(比如聊天机器人)http://www.it55.com/html/xueyuan/chengxukaifa/c_jiaocheng/20070920/240668.html
- 可以让Eclipse开发C#程序的插件
- 内嵌Activex的Activex插件开发
- 【转】C#开发ActiveX可行性研究
- C#的插件开发
- C# 开发Activex
- 用Visual Studio 2005 开发VB.NET-C#语言转换插件
- C#的插件开发
- c#中开发ActiveX的学习笔记
- C# .NET 插件开发
- 在用c#开发的ActiveX中调用JavaScript方法
- C#的插件开发(转)
- C#开发Com和ActiveX资料整理
- C# .NET 插件开发
- 在用c#开发的ActiveX中调用JavaScript方法
- 开发c#插件步骤
- 开发c#插件步骤