0%

MFC OCX 开发一些注意事项

由于一些原因,居然开始 MFC OCX 的开发,这都是淘汰了多久的玩意了啊,心塞。

MFC 如何进行 OCX 开发不在本文讨论之内,可以参考一下这些内容:MFC ActiveX (ocx)控件的开发MFC .ocx控件开发mfc 开发ocx

OCX 加载失败问题

如果 OCX 莫名其妙加载失败,主要表现为,在 ie 无论调用那个函数,都会提示 没有该方法,则很有可能 OCX 加载失败了,可以检查一下两个方面:

  1. 是否注册:regsvr32 OCX路径(卸载命令:regsvr32 /u OCX路径)
  2. 依赖的 dll 是否完整。

OCX 事件回调

有些情况下,我们需要OCX 进行一些耗时间的操作,但是又不想卡死UI,这时候我们可以注册事件回调,等处理好之后,再通过事件回调进行通知UI:这里有几个地方需要注意的:
  1. 需要添加消息映射

    1
    2
    3
    4
    5
    6
    // 事件映射
    BEGIN_EVENT_MAP(CVideoShowOCXCtrl, COleControl)
    EVENT_STOCK_READYSTATECHANGE()
    // 注册 onComplete 的事件映射
    EVENT_CUSTOM_ID("onComplete", eventidComplete, Complete, VTS_BSTR)
    END_EVENT_MAP()
  2. 调用方式

    通过 FireEvent 进行触发事件映射

    1
    FireEvent(eventidComplete, EVENT_PARAM(VTS_BSTR), data);
  3. 开放端口

    如果只是进行了1、2步骤,那么事件是无法触发的,还需要对 *.idl : ActiveX 控件项目的类型库源 进行编辑,增加暴露

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //  CVideoShowOCXCtrl 的事件调度接口

    [
    uuid(*******-****-****-****-***********)
    ]
    dispinterface _DVideoShowOCXEvents
    {
    properties:
    // 事件接口没有任何属性

    methods:
    // 增加 onComplete 的暴露
    [id(1)] void onComplete(char*);
    };
  4. 在IE中的使用方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <!--回调函数-->
    <script language='javascript' for="object_id" event="onComplete(data)">
    // 参数为 data
    // Works on IE11
    Complete(data);
    </script>

    <script>
    // 使用统一的函数进行处理
    function Complete(data) {
    }
    // IE 11 以下
    try {
    object_id.attachEvent("onComplete", Complete, false);
    } catch (e) {
    }
    </script>

    需要注意的是,这玩意在 IE 不一样的版本下面还有区别;最好将两种方式都进行实现。

多线程进行事件回调

就像前面说的,通过 FireEvent 进行触发事件映射,但是问题是,多线程的情况下,这个是也是无效的,所以需要想办法使用主线程(UI线程)通过 FireEvent 进行触发事件映射,比较简单的方式就是消息通知的方式,可以通过PostMessage发送消息给主线程,让主线程进行操作,数据的话,可以通过全局变量(记得加锁读写)进行共享。

这玩意也需要进行注意:

  1. 继承函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 要使用消息推送,必须使用继承该函数
    void *Ctrl::OnSetClientSite()
    {
    // It doesn't matter who the parent window is or what the size of
    // the window is because the control's window will be reparented
    // and resized correctly later when it's in-place activated.
    if (m_pClientSite)
    VERIFY(CreateControlWindow(::GetDesktopWindow(), CRect(0, 0, 0, 0), CRect(0, 0, 0, 0)));
    COleControl::OnSetClientSite();
    }

    使用消息推送,必须继承 OnSetClientSite 函数,否则还是无效,emmmmmm。

  2. 注册消息推送处理函数

    1
    2
    3
    4
    5
    6
    7
    8
    // 消息映射
    BEGIN_MESSAGE_MAP(CVideoShowOCXCtrl, COleControl)
    ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
    // 注册消息 id = WM_USER + 100 使用 OnFinishPublic 处理
    ON_MESSAGE(WM_USER + 100, OnFinishPublic)
    END_MESSAGE_MAP()
    // OnFinishPublic 定义可以参考以下
    afx_msg LRESULT OnFinishPublic(WPARAM wParam, LPARAM lParam)
  3. PostMessage 参数

    使用消息推送的时候,第一个参数可以使用 内部变量 m_hWnd 或者使用:

    1
    2
    3
    4
    if (cWnd == nullptr) {
    cWnd = GetActiveWindow();
    hWnd = cWnd->GetSafeHwnd();
    }

    或者,看需求情况自己选择。

总结

目前发现的坑就是这些,其它的如果有的话,以后再说吧。