MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

Stella981
• 阅读 841

        小小抱怨下:也许是MFC现在用的人少的缘故。在国内和国外都基本上找不到什么全的资料。特别是ocx打包成Cab时的安装文件inf的编写方面,国内基本上是copy,抄的还一知半解。查找个资源真心的累啊。现在将编写步骤和遇到的问题以及问题的解决办法记录下来。有的资源是参考了别人的博文,有引用的地方将附上连接,再次感谢这些博主,如有侵权,请联系本人,立即删除。

一、MFC Activex 项目的构建

       此处使用的是:vs2015,操作按照图片顺序,没有特别注意的地方不做文字注释了。(不详细的地方可以参考:https://blog.csdn.net/longhuahaha/article/details/8556964

  第一:新建项目

   MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  activexdemo项目创建完成的目录结构

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  第二、先介绍四个文件

  activexdemo.cpp、activexdemoCtrl.cpp、activexdemoPropPage.cpp、activexdemo.idl

  (1) activexdemo.cpp文件中定义了DllRegisterServer和DllUnregisterServer,可以发现ActiveX的注册和反组册都与该类有关

   MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  (2)activexdemoCtrl.cpp文件

           可以发现该头文件中声明了消息映射(让ActiveX控件程序可以接受系统发送的事件通知,如窗体创建和关闭事件),调度映射(让外部调用程序(包含ActiveX的容器)可以方便地访           问ActiveX控件的属性和方法),事件映射(让ActiveX控件可以向外部调用程序(包含ActiveX的容器)发送事件通知)。也就是说对ActiveX控件的窗口操作都将在这个类中完成,包括                 ActiveX控件的创建,重绘,以及在此类中创建可视MFC窗体。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  (3)activexdemoPropPage.cpp:显示ActiveX控件的属性页的

  (4)activexdemo.idl:对外接口定义文件

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  第三、定义调度映射和事件映射方法,提供给外部调用者使用

  (1)通过类视图进行定义:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  在_Dactivexdemo上鼠标右键:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  以添加对外接口方法BSTR initDvr(BSTR info, BSTR appKey, BSTR accessToken, LONG rowNum, FLOAT height);方法为例。BSTR为MFC中的数据类型。不懂的可以去百度了解。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  点击下一步

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  填写完信息后点击完成,外部就可以调用方法initDvr,同时ActiveX控件项目代码的内部将会在三个文件中新增代码:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  第四、Release生成ocx文件。并注册。

项目上右键->属性->常规->项目默认值->MFC的使用,设置为"在静态库中使用MFC",必须。如果不设置,在其他非开发环境电脑中会出现找不到DLL的错误。

MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  在本地磁盘找到生成的activexdemo.ocx,此处路径为:E:\c++\activexdemo\Release。找到自己的项目地址中的Release文件夹。

       MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  注册activexdemo.ocx手动注册:

  以管理员权限运行cmd(不会的百度)。

  进入activexdemo.ocx的路径,使用regsvr32 xxx.ocx 对Activex进行注册。成功会弹出提示框。补充:regsvr32 xxx.ocx -u 对Activex控件进行反注册。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

      第五、Activex控件测试

  (1)第一种方法:使用ActiveX Control Test Container。ActiveX Control Test Container工具下载

  将下载的控件解压后复制到安装的visual studio的安装根目录。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  添加ActiveX Control Test Container到vs中。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  添加完成再次点击工具菜单,点击开始测试:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  给initDvr方法添加个MessageBox弹出信息:

  找到activexdemoCtrl.cpp中的initDvr方法添加:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  项目右键,点击重新生成,重新生成ocx文件。然后使用regsvr32 activedemo.ocx -u反注册下,再使用regsvr32 activedemo.ocx再次注册下。

  此处可能会出现此种错误,可按照红色字体提示解决:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  然后再次点击active container control,进行initDvr方法接口的测试。测试结果如下:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  (2)html测试:使用标签。在测试之前需要对activexdemo.ocx进行打包。此处打包成cab包。cab包需要下载cabarc.exe工具以及编写inf安装文件。cabarc.exe工具下载

  2.1 cabarc.exe下载之后建议将文件夹配置到系统环境(不懂的自己去百度,此处不再讨论)中。方便在其他文件夹中使用cabarc.exe命令。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  2.2 对inf安装文件的编写。此为最重要。先以该项目为实例,之后会讲到子目录打包cab。

  使用windwos自带的记事本建立一个inf文件。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  打开activexdemo.inf文件进行编写:(inf的各节的详细解释请参考博文:https://www.cnblogs.com/xiangxinhouse/p/10392161.html)。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

   在桌面新建一个文件夹,命名为activedemo(名称自己定义)。将Release文件夹下的activexdemo.ocx,上面编写好的activexdemo.inf文件拷贝到新建的activedemo文件夹中。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  打开一个新的cmd,进入到activedemo文件夹。进行cab打包。打包命令:cabarc -r -p N xxx.cab *。此命令为打包activedemo下的所有文件,包括子文件夹。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  新建一个web项目。此处使用java web工程:

  在webcontent(web)下新建一个files目录,并将打包好的cab文件复制到下面。并在index.jsp中添加代码。注意version=1,0,0,0。1,0,0,0之间是,而不是.

  详细如下:

   MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  发布web项目后测试。此处要设置IE浏览器允许未签名的activex控件进行安装和运行。不懂的百度。

  测试结果如下:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  此处讲下多个目录打包成cab的inf文件编写。假设activexdemo.ocx控件需要依赖test.dll和depends文件夹下的test1.dll两个动态依赖DLL。文件夹结构如下

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  inf的编写:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  由于此处的DLL依赖为示例,不真实存在,故无法测试,只是以此来讲解多层目录资源打包成cab包。

  第六、添加MFC窗体。就是一个带界面的ActiveX控件

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  去掉上边的“确定”和“取消”按钮,然后修改对话框属性:Border改为None,Control改为Ture,ID改为IDD_DIALOG1,Style改为Child,System改为False,Visible改为True,然后在对话框中双击,为对话框添加一个类,如下图:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  点击完成,会自动新增了一个类:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  在对话框上新增一个按钮,用于测试:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  双击该按钮,添加一个点击事件。在CMyDlg.h和CMyDlg.cpp文件中会响应的增加该事件的代码:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  将刚创建好的MFC对话框添加到Active中。在activexdemoCtrl.h中引入MyDlg.h并添加一个属性:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

       然后在activexdemoCtrl类中定义两个消息映射:窗体创建完成消息映射(WM_CREATE)和窗体改变大小消息映射(WM_SIZE)

       在VS2015的菜单项“项目”--》“类向导”中,选择要添加到的项目和类中,选择“消息”选项卡,选择WM_CREATE后点击”添加处理程序“按钮和选择WM_SIZE

       后点击”添加处理程序“,这样在“现有的处理程序”中就有OnCreate和OnSize这两个函数,点击确定,完成消息映射函数的添加,如图:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  点击应用和完成后,在activexdemoCtrl.h和activexdemoCtrl.cpp文件中会自动添加OnCreate()和OnSize()方法。并在activexdemoCtrl.cpp中添加MFC创建的对话框。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  重新生成ocx,然后再次反注册和注册,进行测试。结果如下:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  第七、ocx更新:

  上面注册的ocx在注册表中没有InstalledVersion这个项,需要在inf文件中添加。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  inf编辑添加InstalledVersion。如果不添加,可能会出现IE浏览器刷新后一直提示需要重复安装Activex控件。

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  inf添加了AddReg后的注册表项:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

  第八、关于ocx控件已经安装成功,但出现“SCRIPT438:对象不支持"xxx"属性或者方法”。以这篇的例子来说,IE浏览器中的Console中会出现错误:“SCRIPT438:对象不支持"initDvr"属性或者方法“的解决办法。

  (1)找到IE浏览器上的设置->Internet选项->安全->找到Internet、本地Internet、受信任的站点三个项目下的->自定义级别->找到"对未标记为可安全执行脚本的Activex控件初始化并执行脚本"设置为"启用"。

  (2)1的方法太过麻烦,不可以让每台电脑都设置。故通过添加IObjectSafety接口。

  在activexdemoCtrl.h中添加:

  MFC Activex 开发、ocx打包成cab、部署、测试、自动升级

       代码可复制:

//ISafeObject Begin
    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 END

  在activexdemoCtrl.cpp中添加:

  代码可复制:

/*
* IObjectSafety BEGIN
*/
//Interface map for IObjectSafety
BEGIN_INTERFACE_MAP(CactivexdemoCtrl, COleControl)
    INTERFACE_PART(CactivexdemoCtrl, IID_IObjectSafety, ObjSafe)
END_INTERFACE_MAP()
//IObjectSafety member functions
//Delegate AddRef,Release,QueryInterface

ULONG FAR EXPORT CactivexdemoCtrl::XObjSafe::AddRef()
{
    METHOD_PROLOGUE(CactivexdemoCtrl, ObjSafe)
        return pThis->ExternalAddRef();
}

ULONG FAR EXPORT CactivexdemoCtrl::XObjSafe::Release()
{
    METHOD_PROLOGUE(CactivexdemoCtrl, ObjSafe)
        return pThis->ExternalRelease();
}

HRESULT FAR EXPORT CactivexdemoCtrl::XObjSafe::QueryInterface(
    REFIID iid, void FAR* FAR* ppvObj)
{
    METHOD_PROLOGUE(CactivexdemoCtrl, 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
CactivexdemoCtrl::XObjSafe::GetInterfaceSafetyOptions(
    REFIID riid,
    DWORD __RPC_FAR *pdwSupportedOptions,
    DWORD __RPC_FAR *pdwEnabledOptions)
{
    METHOD_PROLOGUE(CactivexdemoCtrl, 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
CactivexdemoCtrl::XObjSafe::SetInterfaceSafetyOptions(
    REFIID riid,
    DWORD dwOptionSetMask,
    DWORD dwEnabledOptions)
{
    METHOD_PROLOGUE(CactivexdemoCtrl, 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);
}
/*IObjectSafety END*/

  添加完成后重新生成即可。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这