背景

最近做的项目都和插件有关,就是在页面中调用插件的方法,然后进行操作。

插件就是ocx ActiveX控件,具体的说明可以自己去了解一下,在这里就不做赘述。

具体调用方式很简单:

1、在页面中写一个object标签,标签中定义一个classid属性,这个属性是获取到插件的关键

<object id="ocx" classid="clsid:c998ae90-5ffc-4a58-97d2-490a414bd6e5"></object>

2、直接在js中获取到这个dom,然后就可以调用插件中的方法

let ocx = document.getElementById("ocx");
let sum = ocx.Add(1,2);

在使用过程中遇到了很多问题,于是自己去摸索了一下如何制作一个ocx插件,也能够帮助自己加深一些使用方面的了解。

工具及下载准备

这里使用Visual Studio Community 2019,先下载必须的组件和依赖包。

使用C++的桌面开发Visual Studio扩展开发这两项必须要勾选。

然后桌面开发中还需要勾选与MFC相关的库,如果不勾选的话无法新建MFC项目

勾选完成后就开始下载相关的包,这个过程需要等待一会。

开始项目

下载完成后就可以开始新建一个项目,在这里选择MFC ActiveX控件,命名为MyMFCActiveXControl

然后就会生成一系列控件名,头文件等等的文件名,这里基本上不用做什么操作,直接完成就好。

唯一需要注意的是一个控件类型ID,这个ID我们后面会用到。

点击完成后,就会生成一系列与插件相关的文件,这些文件里面包含了插件的类,头文件,描述等信息。

因为不是专门学习C++的,不对里面的原理和结构进行探究,就取我需要的内容就行。

来找一找那个神奇的classid。

打开视图中的解决方案资源管理器,找到源文件中以idl结尾的文件,这个文件包含了很多与插件相关的信息,例如版本,对外接口等等。

文件中有好几个uuid都和我们用的classid长得很像,但是我们调用的classid是其中类信息下的uuid。

有兴趣了解其他几个uuid的信息可以参考这篇文章来了解:c++ ActiveX基础1:使用VS2010创建MFC ActiveX工程项目

然后我们来添加一个方法测试一下这个插件。

在类视图中找到以Lib结尾的Liberary,点开找到Control项右击后点击添加—>添加方法

在弹出的选项中添加一个最简单的加法方法,点击确定。

编译器会帮我们在三个文件里面都做一些修改。

在.idl中定义了我们刚才添加的方法

在.h文件中声明了这个方法

在同名的.cpp文件中对方法进行实现,我们将返回值改成p1 + p2;

生成项目,在生成的目录下找到.ocx结尾的文件,这个就是我们生成的插件啦。

现在还没有办法使用这个插件,要在注册表中进行注册。可以用以下两种方式:

1、右击ocx文件选择打开方式,选择C:\Windows\System32文件夹下的regsvr32.exe打开

2、直接运行regsvr32+ocx路径

注册成功后都有以下提示

然后就可以在页面中编写我们的代码进行测试,

但是在调用时意外地报了找不到成员这个错。

查阅资料发现是需要修改浏览器的安全设置。

点击浏览器的设置,找到Internet选项—>安全,因为是在本地测试就选择本地Internet。

选择自定义级别—>对未标记为可安全的ActiveX控件初始化...—>点击启用

其实启用这个选项不是特别安全,特别是让用户多了这一步操作,会增加使用难度,如何绕开这个安全模式可以参考下面的解决方式。

在Ctrl.h头文件中添加如下代码

// for IObjectSafety;不要忘了这个头
#include <objsafe.h>    //////////////////////////////////////////////////////////////////////////
// ActiveX控件安全初始化:实现ISafeObject接口
//////////////////////////////////////////////////////////////////////////
//ISafeObject
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(ObjSafe, IObjectSafety)
STDMETHOD_(HRESULT, GetInterfaceSafetyOptions) (
/* [in] */ REFIID riid,
/* [out] */ DWORD __RPC_FAR* pdwSupportedOptions,
/* [out] */ DWORD __RPC_FAR* pdwEnabledOptions
); STDMETHOD_(HRESULT, SetInterfaceSafetyOptions) (
/* [in] */ REFIID riid,
/* [in] */ DWORD dwOptionSetMask,
/* [in] */ DWORD dwEnabledOptions
);
END_INTERFACE_PART(ObjSafe);
//ISafeObject

在Ctrl.cpp中添加如下代码,如果创建的项目名称和我的不一样,记得修改里面的类名

//////////////////////////////////////////////////////////////////////////
// ActiveX控件安全初始化:实现ISafeObject接口
//////////////////////////////////////////////////////////////////////////
// Interface map for IObjectSafety
BEGIN_INTERFACE_MAP(CMyMFCActiveXControlCtrl, COleControl)
INTERFACE_PART(CMyMFCActiveXControlCtrl, IID_IObjectSafety, ObjSafe)
END_INTERFACE_MAP()
// IObjectSafety member functions
// Delegate AddRef, Release, QueryInterface
ULONG FAR EXPORT CMyMFCActiveXControlCtrl::XObjSafe::AddRef()
{
METHOD_PROLOGUE(CMyMFCActiveXControlCtrl, ObjSafe)
return pThis->ExternalAddRef();
}
ULONG FAR EXPORT CMyMFCActiveXControlCtrl::XObjSafe::Release()
{
METHOD_PROLOGUE(CMyMFCActiveXControlCtrl, ObjSafe)
return pThis->ExternalRelease();
}
HRESULT FAR EXPORT CMyMFCActiveXControlCtrl::XObjSafe::QueryInterface(
REFIID iid, void FAR* FAR* ppvObj)
{
METHOD_PROLOGUE(CMyMFCActiveXControlCtrl, ObjSafe)
return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}
const DWORD dwSupportedBits =
INTERFACESAFE_FOR_UNTRUSTED_CALLER |
INTERFACESAFE_FOR_UNTRUSTED_DATA;
const DWORD dwNotSupportedBits = ~dwSupportedBits;
//.............................................................................
// CStopLiteCtrl::XObjSafe::GetInterfaceSafetyOptions
// Allows container to query what interfaces are safe for what. We're
// optimizing significantly by ignoring which interface the caller is
// asking for.
HRESULT STDMETHODCALLTYPE
CMyMFCActiveXControlCtrl::XObjSafe::GetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [out] */ DWORD __RPC_FAR* pdwSupportedOptions,
/* [out] */ DWORD __RPC_FAR* pdwEnabledOptions)
{
METHOD_PROLOGUE(CMyMFCActiveXControlCtrl, ObjSafe)
HRESULT retval = ResultFromScode(S_OK);
// does interface exist?
IUnknown FAR* punkInterface;
retval = pThis->ExternalQueryInterface(&riid,
(void**)&punkInterface);
if (retval != E_NOINTERFACE) { // interface exists
punkInterface->Release(); // release it--just checking!
} // we support both kinds of safety and have always both set,
// regardless of interface
*pdwSupportedOptions = *pdwEnabledOptions = dwSupportedBits;
return retval; // E_NOINTERFACE if QI failed
}
///////////////////////////////////////////////////////////////////////////// // CStopLiteCtrl::XObjSafe::SetInterfaceSafetyOptions
// Since we're always safe, this is a no-brainer--but we do check to make
// sure the interface requested exists and that the options we're asked to
// set exist and are set on (we don't support unsafe mode).
HRESULT STDMETHODCALLTYPE
CMyMFCActiveXControlCtrl::XObjSafe::SetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [in] */ DWORD dwOptionSetMask,
/* [in] */ DWORD dwEnabledOptions)
{
METHOD_PROLOGUE(CMyMFCActiveXControlCtrl, ObjSafe) // does interface exist?
IUnknown FAR* punkInterface;
pThis->ExternalQueryInterface(&riid, (void**)&punkInterface);
if (punkInterface) { // interface exists
punkInterface->Release(); // release it--just checking!
}
else { // interface doesn't exist
return ResultFromScode(E_NOINTERFACE);
}
// can't set bits we don't support
if (dwOptionSetMask & dwNotSupportedBits) {
return ResultFromScode(E_FAIL);
} // can't set bits we do support to zero
dwEnabledOptions &= dwSupportedBits;
// (we already know there are no extra bits in mask )
if ((dwOptionSetMask & dwEnabledOptions) !=
dwOptionSetMask) {
return ResultFromScode(E_FAIL);
} // don't need to change anything since we're always safe
return ResultFromScode(S_OK);
}
//////////////////////////////////////////////////////////////////////////

然后就可以在页面上进行调用啦,调用成功返回了正确的值。

let ocx = document.getElementById("ocx");
let sum = ocx.Add(1, 2);
alert(sum);

拓展

刚才演示的都是正常情况下的调用,在实际使用时,最重要的场景是如何验证电脑中是否安装了我们需要使用的插件。

常见的办法有两种:

1、调用插件方法时使用异常处理,用try...catch来捕获调用不到插件的情况

我们把插件卸载掉,运行regsvr /u 插件地址来卸载插件

会提示卸载成功

这个时候我们再来调用一下上面的Add方法。

try {
let ocx = document.getElementById("ocx");
let sum = ocx.Add(1, 2);
alert(sum);
} catch (e) {
alert(e);
}

会提示对象不支持Add属性或方法,这样好像就能判断本机是否安装了插件。

但是在版本迭代中,插件的方法肯定会越来越多,不止一个方法,那么这个方法还能帮助我们判断嘛?

我们来尝试注册插件后,调用一个不存在Sub方法。

try {
let ocx = document.getElementById("ocx");
let sum = ocx.Sub(1, 2);
alert(sum);
} catch (e) {
alert(e);
}

也会得到同样的结果,所以这个方法不是判断本机是否安装插件的最佳办法。

这里我推荐第二种办法

2、通过ActiveXObject来检测是否安装插件

var findOcx = function () {
let control;
try {
//插件ProgID
control = new ActiveXObject('MFCACTIVEXCONTRO.MyMFCActiveXControlCtrl.1');
} catch (e) {
console.log(e);
return false;
}
return true;
};

这个方法new了一个ActiveXObject对象,里面的参数就是刚才我们新建项目时标注的控件类型ID。

在.idl文件中也可以找到这个ID。

在注册表中他以这样的形式存在

如果未安装插件,会提示Automation 服务器不能创建对象,这样就把是否安装插件和这个版本的插件是否有这个方法这两个问题区分开来了。

如果插件存在,通过调试,也可以查看插件暴露出来的所有方法。

插件可以做很多事情,可以绕过浏览器的安全限制在本地读写文件,也可以绘制图像,显示视频流等等。

但是插件的局限性也很大。

在实际使用中,版本的更迭提示、不同系统不同浏览器版本的安全模式等等问题处理起来更是让人头疼。

所以建议不到万不得己最好不要使用插件。

调用ocx ActiveX控件详解(做一个简单的ocx控件)的更多相关文章

  1. [Pyhton] SimPy 离散事件模拟框架详解 —— 以一个简单的汽车充电排队模拟为例

    目录 一.背景知识 二.SimPy 讲解 2.1 SimPy 概述 2.2 基本概念 2.3 一个汽车开开停停的例子 2.4 在走走停停过程中增加充电过程(过程交互) 2.5 共享资源 三.后续 参考 ...

  2. IOS—UITextFiled控件详解

    IOS—UITextFiled控件详解 //初始化textfield并设置位置及大小 UITextField *text = [[UITextField alloc]initWithFrame:CGR ...

  3. ToolBar控件详解

    ToolBar控件详解 在Activity中添加ToolBar 1.添加库 dependencies { ... compile "com.android.support:appcompat ...

  4. Android开发:文本控件详解——TextView(一)基本属性

    一.简单实例: 新建的Android项目初始自带的Hello World!其实就是一个TextView. 在activity_main.xml中可以新建TextView,从左侧组件里拖拽到右侧预览界面 ...

  5. picker控件详解与使用,(实现省市的二级联动)

    picker控件详解与使用,(实现省市的二级联动) 第一步:新建一个单视图(single view)的工程, 命名为pickerTest,不要勾选下面两个选项,第一个是新版本里面的,第二个是单元测试, ...

  6. Switch控件详解

    Switch控件详解 原生效果 5.x 4.x 布局 <Switch android:id="@+id/setting_switch" android:layout_widt ...

  7. Spinner控件详解

    Spinner控件详解 效果图 修改Spinner样式 在介绍之前,先看一下系统原生的样式 6.x & 5.x系统样式 4.x系统样式 官方文档 XML属性 方法 描述 android:dro ...

  8. Android开发:文本控件详解——TextView(二)文字跑马灯效果实现

    一.需要使用的属性: 1.android:ellipsize 作用:若文字过长,控制该控件如何显示. 对于同样的文字“Android开发:文本控件详解——TextView(二)文字跑马灯效果实现”,不 ...

  9. C++ CComboBox控件详解

    转载:http://blog.sina.com.cn/s/blog_46d93f190100m395.html C++ CComboBox控件详解 (2010-09-14 14:03:44) 转载▼ ...

随机推荐

  1. SQL实战(六)

    一. 题目描述 查找排除当前最大.最小salary之后的员工的平均工资avg_salary.CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL,`s ...

  2. MATLAB 图像打开保存

    一.图片读取保存 (1)读取 clear all [filename,pathname]=uigetfile({'*.jpg';'*.bmp';'*.gif'},'选择图片'); if isequal ...

  3. 一位萌新Google冲浪的开始

    这一切的开始可能都来源于对 百度 各方面的不满吧(确实不咋滴) 于是开始对Google感冒,上必应https://cn.bing.com/去搜了下“国内如何上Google”,上面也是众说纷纭,莫衷一是 ...

  4. 9.Metasploit制作木马后门

    01木马与后门   木马?后门? 木马和后门都有害,尤其是木马,它由攻击者主动发起,稍不留心就会被利用:后门原来是留给自己方便用的,但也有可能被非法利用,这两种程序都会给用户带来损失. 木马是指潜伏在 ...

  5. 老技术新谈,Java应用监控利器JMX(2)

    各位坐稳扶好,我们要开车了.不过在开车之前,我们还是例行回顾一下上期分享的要点. 上期由于架不住来自于程序员内心的灵魂的拷问,于是我们潜心修炼,与 Java 应用监控利器 JMX 正式打了个照面. J ...

  6. 并发——抽象队列同步器AQS的实现原理

    一.前言   这段时间在研究Java并发相关的内容,一段时间下来算是小有收获了.ReentrantLock是Java并发中的重要部分,所以也是我的首要研究对象,在学习它的过程中,我发现它是基于抽象队列 ...

  7. Java 连接数据库总是报错

    mysql账号密码是正确的,但是一直报账号密码错误. 报错信息: java.sql.SQLException: Access denied for user 'root'@'localhost' (u ...

  8. 1046 Shortest Distance (20分)

    The task is really simple: given N exits on a highway which forms a simple cycle, you are supposed t ...

  9. PTA数据结构与算法题目集(中文) 7-27

    PTA数据结构与算法题目集(中文)  7-27 7-27 家谱处理 (30 分)   人类学研究对于家族很感兴趣,于是研究人员搜集了一些家族的家谱进行研究.实验中,使用计算机处理家谱.为了实现这个目的 ...

  10. 深入理解equals和hashCode关系和区别

    为什么要说equals和hashCode这两个东西,一来是因为有不少小伙伴面试时被问过这个东西,二来则是因为如果了解了这两个东西的原理,那么实际的开发过程中,对效率和容错率上还是能帮上很大的忙! 直入 ...