由于一些原因,居然开始 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. 需要添加消息映射

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

    通过 FireEvent 进行触发事件映射

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

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

         //  CVideoShowOCXCtrl 的事件调度接口
        
         [ 
             uuid(*******-****-****-****-***********)	
         ]
         dispinterface _DVideoShowOCXEvents
         {
             properties:
                 //  事件接口没有任何属性
        
             methods:
                 // 增加 onComplete 的暴露
                 [id(1)] void onComplete(char*);
         };
    
  4. 在IE中的使用方式

         <!--回调函数-->
         <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. 继承函数

     // 要使用消息推送,必须使用继承该函数
     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. 注册消息推送处理函数

     // 消息映射
     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 或者使用:

     if (cWnd == nullptr) {
             cWnd = GetActiveWindow();
             hWnd = cWnd->GetSafeHwnd();
         }
    

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

总结

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