zoukankan      html  css  js  c++  java
  • ActiveX控件开发总结(续二)

    开发过程中一些经验总结

    以下总结主要由李俊峰(lijunfeng 00165774/huawei,)、李伟(liwei 00165242/huawei,)、姜川(j00132245)总结

    1.自定义CListCtrl,使用自定义列表头CHeaderCtrl

    在自定义CListCtrl的列表头时,需要替换系统自带的CHeaderCtrl,网上代码的处理方式通常是在自定义的MyListCtrl中重写PreSubclassWindow,并在其中调用(其中m_MyHeaderCtrl是自定义的CHeaderCtrl,作为MyListCtrl的成员变量

           if(GetHeaderCtrl())

                  m_MyHeaderCtrl.SubclassWindow(GetHeaderCtrl()->m_hWnd);

    这种方法在MFC桌面程序中可以奏效。但是放到ActiveX中时,此处的GetHeaderCtrl()==NULL,所以无法达到替换表头的效果(具体原理未知,有兴趣的同学可以研究)。解决办法是在创建自定义的MyListCtrl之后手动调用

    m_MyHeaderCtrl.SubclassWindow(GetHeaderCtrl()->m_hWnd);

    当然需要把这个包装成一个InitMyHeader之类的public方法放到MyListCtrl中。

    完整代码请参考VAEListCtrlVideoListCtrl及其使用。

    2.ActiveX窗口上的自定义控件添加ToolTip

    为自定义控件添加ToolTip时,一般做法是在PreTranslateMessage中添加如下代码:

    if (NULL != m_pToolTipCtrl)           

                  m_pToolTipCtrl->RelayEvent(pMsg);

    希望CToolTipCtrl能够捕获鼠标移动等消息,但是结果PreTranslateMessage方法在ActiveX程序中不会执行(经验证在ProjectName+Ctrl(即整个ActiveX获得输入焦点)时PreTranslateMessage方法会执行)。

    解决办法是为控件添加OnMouseMove消息响应,在OnMouseMoveUINT nFlags, CPoint point)方法中添加代码:

    //构造一个MSG

            MSG msg;

            msg.hwnd = m_hWnd;

            msg.message = WM_MOUSEMOVE;

            msg.wParam = LOWORD(point.x);

            msg.lParam = LOWORD(point.y);

            msg.time = 0;

            msg.pt.x = LOWORD(point.y);

            msg.pt.y = HIWORD(point.y);

            m_ToolTip.RelayEvent(&msg);

    这样才会使鼠标在控件上移动时显示ToolTip,但是要求要先点选过该控件才行。如果想要更进一步去掉这个先点选控件的限制,则需要在自定义控件的父窗口中响应OnMouseMove消息,并构造MSG关联到ToolTip。此时需要注意point的坐标转换。

    完整的相关代码(PTZControlWndBitmapSlider)如下:

     

    父窗口PTZControlWnd

    //成员变量声明

    CBitmapSlider m_bsSpeedOrStepsize;

    void PTZControlWnd::OnMouseMove(UINT nFlags, CPoint point)

    {

       (void)nFlags;

    UpdateToolTipText();//更新ToolTip文字

    m_bsSpeedOrStepsize->RelayToolTipEventFromParent(point);//此处的point的坐标是相对PTZControlWnd

        VAEBaseWnd::OnMouseMove(nFlags, point);

    }

    控件CBitmapSlider

    //成员变量声明

    CToolTipCtrl m_ToolTip;

    void CBitmapSlider::RelayToolTipEventFromParent(CPoint &point)

    {

       //将相对父窗口的坐标转换成屏幕坐标

    GetParent()->ClientToScreen(&point);

     

    //从屏幕坐标转换成相对控件自身CBitmapSlider的坐标

    ScreenToClient(&point);

     

    //因为在CBitmapSlider::OnMouseMove中也要用到,所以提取成函数

        RelayToolTipEvent(point);

    }

     

    void CBitmapSlider::RelayToolTipEvent(const CPoint &point )

    {

        if (m_ToolTip.m_hWnd != NULL)

        {

            //构造一个MSG

            MSG msg;

            msg.hwnd = m_hWnd;

            msg.message = WM_MOUSEMOVE;

            msg.wParam = LOWORD(point.x);

            msg.lParam = LOWORD(point.y);

            msg.time = 0;

            msg.pt.x = LOWORD(point.y);

            msg.pt.y = HIWORD(point.y);

            m_ToolTip.RelayEvent(&msg);

        }

    }

     

    为整个ActiveX添加ToolTip的方法可以参考MSDN,同样没有使用PreTranslateMessage方法

    http://support.microsoft.com/kb/141871/zh-cn

     

    3.MFC ActiveX控件添加对外接口

    打开类视图,找到ProjectName+Lib,此处为NVS_VAELib


    _D+ProjectName(此处为_DNVS_VAE)上右键弹出菜单,选择添加方法


    弹出窗口如下所示,填写相关内容,注意字符串类型参数需要选择BSTR

    填写完毕点击『完成』,如果出现错误提示,可以关闭VisualStudio,删除解决方案文件夹下的“.ncb”文件,然后重新打开解决方案再添加方法。

    修改已经添加的对外接口签名时注意除了声明和实现外还需要在“.idl”文件中更改相应的调度接口。


    4.对外接口BSTR参数转换为char *

    生成的方法签名中的对应BSTR类型的是LPCTSTR

    javascript传进来的Unicode字符串转换为内部接口使用的char*,需要使用MFC宏,

    USES_CONVERSION;

    char * pszCameraID = W2A(bstrCameraID);

    5.关于网页的刷新

         ocx加载在网页上时,如果F5刷新,ocx控件会销毁ocx的窗口类,但是ocxapp类是不销毁的,

     只有当网页关闭时,才销毁app类。

     1 刷新引起的问题

         app类中有成员变量时,请注意刷新回来后变量的值还是刷新前的值。

     2 利用刷新app类的不析构恢复刷新前的状态

         可在控件的APP类中保存刷新前的值,刷新后恢复刷新前的状态

    6.一个网页中加载两次(或者多次)OCX控件

    同一进程加载两次控件时,app类调用一次,ocx窗口类调用两次。也就是说两个控件实例使用的是同一个app类的实例,只是有各自的窗口。这时如果app类中有成员变量,值得注意。

     

    7.当将UNICODE字符串转化为多字符集字符串时注意的问题

     unicode字符串中含有汉字时,注意转化前后的字符串长度。

     例如:TCHAR* pUnicde=_T("abc例子"); // pUnicde长度5

     char* pMutiBtye=T2A(pUnicde); // pMutiBtye长度是7,一个汉字占两个字节

    8.开发DLL等控件时,加载字符串/图片等资源失败的原因

           MFC的对话框装载资源是通过获取当前线程对应的ModuleState保存的ResourceHandler来装载资源的。

           所以,DLL里的代码,需要在函数的入口,首先把当前执行线程的ModuleState换成该DllState,这样才能装载该dll的资源

           即:使用AFX_MANAGE_STATE(AfxGetStaticModuleState());

    9.SetEventPulseEvent的区别

           SetEvent为设置事件对象为有信号状态;而PulseEvent也是将指定的事件设为有信号状态,不同的是如果是一个人工重设事件,

           正在等候事件的、被挂起的所有线程都会进入活动状态,函数随后将事件设回,并返回;如果是一个自动重设事件,

           则正在等候事件的、被挂起的单个线程会进入活动状态,事件随后设回无信号,并且函数返回。

           也就是说在自动重置模式下PulseEventSetEvent的作用没有什么区别,但在手动模式下PulseEvent就有明显的不同,

           可以比较容易的控制程序是单步走,还是连续走。如果让循环按要求执行一次就用PulseEvent,如果想让循环连续不停的运转就用SetEvent

           在要求停止的地方发个ResetEventOK了。

    男人就是责任!
  • 相关阅读:
    pip 8 安装
    zabbix server配置文件
    双代号网络图、双代号时标网络图
    logrotate
    tsql 执行存储过程
    dos 加用户
    Visual Studio (VS IDE) 你必须知道的功能和技巧
    格式化数字字符串 与C#变量
    .NET中的字符串你了解多少?
    新手如何有效地学习.NET
  • 原文地址:https://www.cnblogs.com/snben/p/2701125.html
Copyright © 2011-2022 走看看