ActiveX控件的MFC设计之旅-第7步 .

在上一步中我们实际是通过Idispatch接口的Invoke方法来访问控件的属性方法的,虽然有COledispatchDriver和其它一些辅助函数,但还是有些繁杂,能不能直接得到控件对象的指针,这样就可以直接访问控件了,甚至于控件的内部变量了?答案是肯定了。

我们的控件派生自COleControl,COleControl派生自CWnd,CWnd又派生自CCmdTarget,我们的Idispatch接口就是在CCmdTarget中实现的(其实应该是实现在COledispatchImpl类中),实现的方法比较特殊,这里不细细分析了,有兴趣的朋友可以自己看看MFC源码或者网上搜搜资料,应该能知道的。
我们能从CCmdTarget中获得Idispatch接口指针(GetIdispatch函数),同样的,我们也能从Idispatch接口指针中反向导出CCmdTarget对象指针,用到的是CCmdTarget的静态成员函数
static CCmdTarget* FromIdispatch( LPdisPATCH lpdispatch );
前面我们已经知道可以通过属性页的GetobjectArray来获得控件的Idispatch接口指针,而有了上面的FromIdispatch这个函数,我们又可以从Idispatch接口指针中来获得控件的指针了,那么问题就解决了。

另外,这里我们还用到COlePropertyPage的一个虚拟函数OnObjectsChanged,在控件变化(包括控件加载上和卸载掉时)时,都会调用到这个函数。(其实,并不是一定要用这个函数的,只是想介绍一下,就用上了)

还是用上一步中的例子topp
1.添加控件类指针为属性页类的成员变量(#include "ToppCtl.h",应该不用说的吧)
CToppCtrl* m_pctrl;
2.添加获得控件类指针的函数CToppCtrl* GetCtrlPtr();
CToppCtrl* CToppPropPage::GetCtrlPtr()
{
ULONG ulObjects;
LPdisPATCH* lpObjectArray = GetobjectArray( &ulObjects );
ASSERT( lpObjectArray != NULL );

LPdisPATCH lpdisp = NULL;
if(ulObjects > 0){//在这函数中,并不一定能保证就有控件.
lpdisp = lpObjectArray[0];//这里省事了,就假设只有一个控件了
}
CToppCtrl* pctrl = NULL;
if(lpdisp){
pctrl = (CToppCtrl*)CCmdTarget::FromIdispatch(lpdisp);
}
return pctrl;
}

3.重载OnObjectsChanged,设置m_pctrl(在其它地方,如OnInitDialog中设置也无所谓,甚至即用即获得也无所谓)
void CToppPropPage::OnObjectsChanged()
{
ULONG ulObjects;
LPdisPATCH* lpObjectArray = GetobjectArray( &ulObjects );
ASSERT( lpObjectArray != NULL );

LPdisPATCH lpctl = NULL;
if(ulObjects > 0){//在这函数中,并不一定能保证就有控件.
lpctl = lpObjectArray[0];//这里省事了,就假设只有一个控件了
}
if(lpctl){
m_pctrl = (CToppCtrl*)CCmdTarget::FromIdispatch(lpctl);
}
else{
m_pctrl = NULL;
}
}

3.修改上一步中的几个成员函数

BOOL CToppPropPage::AddItem(LPCTSTR lpszItem)
{
if(m_pctrl) m_pctrl->m_saItems.Add(lpszItem);
return m_pctrl != NULL;
}

CString CToppPropPage::GetItem(long lItem)
{
if(m_pctrl) {
if(lItem >= 0 && lItem < m_pctrl->m_saItems.GetSize()){
return m_pctrl->m_saItems[lItem];
}
else{
return _T("");
}
}
else{
return _T("");
}
}

//在获得m_color时,是直接将m_color从protected拉到了public下的,可恶的MFC,再添加新的属性方法时可能会重新再添加一个,会给你带来一个小小的麻烦。
COLORREF CToppPropPage::GetColor()
{
if(m_pctrl) return m_pctrl->TranslateColor(m_pctrl->m_color);
return RGB(0,0);
}

void CToppPropPage::SetColor(COLORREF cr)
{
if(m_pctrl) m_pctrl->m_color = cr;
}

long CToppPropPage::GetCount()
{
if(m_pctrl) return m_pctrl->m_saItems.GetSize();
return 0;
}
4.编译,新建一VB工程,在VB下测试,可以发现没有问题
5.在ActiveX Control Container下测试,咦,出问题了,没有反应,为什么?
根据最后给出的资料上中的意思,大致是因为容器聚合了控件的原因(呵呵,懒得去想了,有兴趣的自己思考吧)
6.为控件添加一只读属性long CtrlPtr
long CToppCtrl::GetCtrlPtr()
{
// Todo: Add your property handler here
return reinterpret_cast<long>(this);
return 0;
}
7.编译,在ClassWizard的Automation下,点击Add Class,然后选择 From a type library,找到编译时为控件生成的.tlb文件,这里应该是在/Debug目录下,选择这个topp.tlb,然后只选择其中的_DTopp,点确定,这样将生成一个_DTopp类,和我们上一步中的介绍比较一下,可以发现,其实就是派生自一个COledispatchDriver类,也就是说,只是一个帮助我们调用控件属性方法的类(也就是说,我们上一步中其实完全不用自己来编代码的,ClassWizard可以为我们生成一个方便的可操作的类:))。 8.修改属性页的GetCtrlPtr函数如下: CToppCtrl* CToppPropPage::GetCtrlPtr() { ULONG ulObjects; LPdisPATCH* lpObjectArray = GetobjectArray( &ulObjects ); ASSERT( lpObjectArray != NULL ); LPdisPATCH lpdisp = NULL; if(ulObjects > 0){//在这函数中,并不一定能保证就有控件. lpdisp = lpObjectArray[0];//这里省事了,就假设只有一个控件了 } CToppCtrl* pctrl = NULL; if(lpdisp){ pctrl = (CToppCtrl*)CCmdTarget::FromIdispatch(lpdisp); if(!pctrl){ long ControlPointer; _DTopp control(lpdisp); // GetobjectArray() docs state must not release pointer. control.m_bAutoRelease = FALSE; ControlPointer = control.GetCtrlPtr(); pctrl = reinterpret_cast<CToppCtrl*>(ControlPointer); //这里其实可以简单用如下的方法调用,之所以用上面的方法,只是用来介绍而已 /* long p; GetPropText("CtrlPtr",&p); pctrl = reinterpret_cast<CToppCtrl*>(p); */ } } return pctrl; } 9.编译,在ActiveX Control Container中测试,OK! 注释:这里用ClassWizard导入了一个辅助类是用来帮助从Idispatch中获得控件的CtrlPtr属性值的,其实并不一定需要这个辅助类的,见CToppCtrl* CToppPropPage::GetCtrlPtr()函数中被注释掉的代码,之所以引入这个辅助类,也是介绍起见。 参考资料: http://support.microsoft.com/kb/205670/

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Format[$] ( expr [ , fmt ] ) format 返回变体型 format$ 强制返回为文本 -------------------------------- 数字类型的格式化 --------------------------------     固定格式参数:     General Number 普通数字,如可以用来去掉千位分隔号     format$("100,1
VB6或者ASP 格式化时间为 MM/dd/yyyy 格式,竟然没有好的办法, Format 或者FormatDateTime 竟然结果和系统设置的区域语言的日期和时间格式相关。意思是尽管你用诸如 Format(Now, "MM/dd/yyyy"),如果系统的设置格式区域语言的日期和时间格式分隔符是"-",那他还会显示为 MM-dd-yyyy     只有拼凑: <%response.write
在项目中添加如下代码:新建窗口来显示异常信息。 Namespace My ‘全局错误处理,新的解决方案直接添加本ApplicationEvents.vb 到工程即可 ‘添加后还需要一个From用来显示错误。如果到这步还不会则需要先打好基础啦 ‘======================================================== ‘以下事件
转了这一篇文章,原来一直想用C#做k3的插件开发,vb没有C#用的爽呀,这篇文章写与2011年,看来我以前没有认真去找这个方法呀。 https://blog.csdn.net/chzjxgd/article/details/6176325 金蝶K3 BOS的插件官方是用VB6编写的,如果  能用.Net下的语言工具开发BOS插件是一件很愉快的事情,其中缘由不言而喻,而本文则是个人首创,实现在了用V
Sub 分列() ‘以空格为分隔符,连续空格只算1个。对所选中的单元格进行处理 Dim m As Range, tmpStr As String, s As String Dim x As Integer, y As Integer, subStr As String If MsgBox("确定要分列处理吗?请确定分列的数据会覆盖它后面的单元格!", _
  窗体代码 1 Private Sub Text1_OLEDragDrop(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single) 2 Dim path As String, hash As String 3 For Each fil
  Imports MySql.Data.MySqlClient Public Class Form1 ‘ GLOBAL DECLARATIONS Dim conString As String = "Server=localhost;Database=net2;Uid=root;Pwd=123456;" Dim con As New MySqlConnection
‘導入命名空間 Imports ADODB Imports Microsoft.Office.Interop   Private Sub A1() Dim Sql As String Dim Cnn As New ADODB.Connection Dim Rs As New ADODB.Recordset Dim S As String   S = "Provider=OraOLEDB.Oracl
Imports System.IO Imports System.Threading Imports System.Diagnostics Public Class Form1 Dim A(254) As String    Function ping(ByVal IP As Integer) As String Dim IPAddress As String IPAddress = "10.0.
VB运行EXE程序,并等待其运行结束 参考:https://blog.csdn.net/useway/article/details/5494084 Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long Pr
今天碰到一个问题,登陆的时候,如果不需要验证手机号为空,则不去验证手机号 因为登陆的时候所有的验证信息都存放在一个数组里 Dim CheckUserInfo() As String ={UserBirthday, SecEmail, UserMob, UserSex, RealNameFirst, RealName, CheckCardID, CheckCardType, Contactemail
在VB6.0中,数据访问接口有三种: 1、ActiveX数据对象(ADO) 2、远程数据对象(RDO) 3、数据访问对象(DAO) 1.使用ADO(ActiveX Data Objec,ActiveX数据对象)连接SQL Server 1)使用ADO控件连接 使用ADO控件的ConnectionString属性就可以连接SQL Server,该属性包含一个由分号分隔的argument=value语
注:大家如果没有VB6.0的安装文件,可自行百度一下下载,一般文件大小在200M左右的均为完整版的软件,可以使用。   特别提示:安装此软件的时候最好退出360杀毒软件(包括360安全卫士,电脑管家等,如果电脑上有这些软件的话),因为现如今的360杀毒软件直接会对VB6.0软件误报,这样的话就可能会在安装过程中被误报阻止而导致安装失败,或者是安装后缺乏很多必须的组件(其它的杀毒软件或安全卫士之类的
Private Sub Form_Load() Call conndb End Sub Private Function conndb() Dim cn As New ADODB.Connection Dim rs As New ADODB.Recordset Dim strCn, sql As String Dim db_host As String Dim db_user As String
  PPSM06S70:  Add  moddate  EDITSPRINTJOB:  MAX(TO_CHAR(ETRN.MODDATE, ‘yyyy/mm/dd/HH24:MI AM‘)) ACTUAL_SHIPDATE   4.Test Scenario (1) :Query SQL Test DN:8016578337 SELECT CTRN.TKCTID TRUCK_ID,        
  沒有出現CrystalReportViewer時,須安裝CRforVS_13_0. 新增1個數據集,新增1個數據表,添加二列,列名要和資料庫名一樣. 修改目標Framework 修改app.config, <startup >改成<startup useLegacyV2RuntimeActivationPolicy ="true">  CrystalReport1.rpt增加數據庫專家 在表單
Imports System.Threading Imports System Public Class Form1 Dim th1, th2 As Thread Public Sub Method1() Dim i As Integer For i = 1 To 100 If Me.Label1.BackColor =
Friend Const PROCESS_ALL_ACCESS = &H1F0FFF = 2035711 Friend Const PROCESS_VM_READ = &H10 Friend Const PROCESS_VM_WRITE = &H20 Friend Const PAGE_READONLY = &H2 Friend Const PAGE_READWRITE = &H4 Friend
以下代码随手写的 并没有大量测试 效率也有待提升 如果需要C#的请自行转换 Function SplitBytes(Data As Byte(), Delimiter As Byte()) As List(Of Byte()) Dim i = 0 Dim List As New List(Of Byte()) Dim bytes As New
Imports System.Data.SqlClient Public Class Form1 REM Public conn1 As SqlConnection = New SqlConnection("server=.; Integrated Security=False;Initial Catalog= mydatabase1; User ID= sa;password")