Windows核心编程系列文章仅作为实验报告和Windows编程学习参考,不作为任何技术文章,还望大佬们勿喷。
1. 实验名称
ActiveX控件编程
2. 实验环境
- VS2015
- Windows 10
3. 实验目的
- 理解Window系统COM概念,了解ActiveX控件编程基本原理、概念,以及编程流程。
- 理解容器应用程序和服务器应用程序的概念
- 采用ActiveX控件编写一个求两数最大公约数的组件,并调用组件,完成两数最大公约数。
4. 实验内容、步骤及结果
1. 实验内容
- 采用MFC ActiveX编写一个求两数最大公约数组件。如下图所示
- 然后编写一个测试程序,调用组件完成组件调用。如下图所示:
2. 实验步骤
- 首先创建一个MFC ActiveX项目
- 采用MFC ActiveX编写一个求两数最大公约数组件
- 代码如下:
// myActiveXCtrl.cpp : CmyActiveXCtrl ActiveX 控件类的实现。
#include "stdafx.h"
#include "myActiveX.h"
#include "myActiveXCtrl.h"
#include "myActiveXPropPage.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
IMPLEMENT_DYNCREATE(CmyActiveXCtrl, COleControl)
// 消息映射
BEGIN_MESSAGE_MAP(CmyActiveXCtrl, COleControl)
ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
// 调度映射
BEGIN_DISPATCH_MAP(CmyActiveXCtrl, COleControl)
DISP_FUNCTION_ID(CmyActiveXCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
DISP_FUNCTION_ID(CmyActiveXCtrl, "SetNum", dispidSetNum, SetNum, VT_EMPTY, VTS_I4 VTS_I4)
DISP_PROPERTY_NOTIFY_ID(CmyActiveXCtrl, "direction", dispiddirection, m_direction, OndirectionChanged, VT_I2)
DISP_FUNCTION_ID(CmyActiveXCtrl, "Mcd", dispidMcd, Mcd, VT_I4, VTS_I4 VTS_I4)
END_DISPATCH_MAP()
// 事件映射
BEGIN_EVENT_MAP(CmyActiveXCtrl, COleControl)
EVENT_CUSTOM_ID("FirmMsg", eventidFirmMsg, FirmMsg, VTS_NONE)
END_EVENT_MAP()
// 属性页
// TODO: 按需要添加更多属性页。 请记住增加计数!
BEGIN_PROPPAGEIDS(CmyActiveXCtrl, 1)
PROPPAGEID(CmyActiveXPropPage::guid)
END_PROPPAGEIDS(CmyActiveXCtrl)
// 初始化类工厂和 guid
IMPLEMENT_OLECREATE_EX(CmyActiveXCtrl, "MYACTIVEX.myActiveXCtrl.1",
0x85f4510, 0xb64, 0x4e28, 0x88, 0x45, 0x3c, 0x43, 0x8c, 0x22, 0xe9, 0xfe)
// 键入库 ID 和版本
IMPLEMENT_OLETYPELIB(CmyActiveXCtrl, _tlid, _wVerMajor, _wVerMinor)
// 接口 ID
const IID IID_DmyActiveX = { 0x12328EFC, 0x9968, 0x4FCE, { 0xA3, 0x26, 0x65, 0x99, 0x6E, 0xB7, 0x5A, 0x3D } };
const IID IID_DmyActiveXEvents = { 0xF481325A, 0xD7BC, 0x4389, { 0x9A, 0xA8, 0x56, 0x16, 0x3A, 0x6C, 0xEE, 0x2B } };
// 控件类型信息
static const DWORD _dwmyActiveXOleMisc =
OLEMISC_ACTIVATEWHENVISIBLE |
OLEMISC_SETCLIENTSITEFIRST |
OLEMISC_INSIDEOUT |
OLEMISC_CANTLINKINSIDE |
OLEMISC_RECOMPOSEONRESIZE;
IMPLEMENT_OLECTLTYPE(CmyActiveXCtrl, IDS_MYACTIVEX, _dwmyActiveXOleMisc)
// CmyActiveXCtrl::CmyActiveXCtrlFactory::UpdateRegistry -
// 添加或移除 CmyActiveXCtrl 的系统注册表项
BOOL CmyActiveXCtrl::CmyActiveXCtrlFactory::UpdateRegistry(BOOL bRegister)
{
// TODO: 验证您的控件是否符合单元模型线程处理规则。
// 有关更多信息,请参考 MFC 技术说明 64。
// 如果您的控件不符合单元模型规则,则
// 必须修改如下代码,将第六个参数从
// afxRegApartmentThreading 改为 0。
if (bRegister)
return AfxOleRegisterControlClass(
AfxGetInstanceHandle(),
m_clsid,
m_lpszProgID,
IDS_MYACTIVEX,
IDB_MYACTIVEX,
afxRegApartmentThreading,
_dwmyActiveXOleMisc,
_tlid,
_wVerMajor,
_wVerMinor);
else
return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}
// CmyActiveXCtrl::CmyActiveXCtrl - 构造函数
CmyActiveXCtrl::CmyActiveXCtrl()
{
InitializeIIDs(&IID_DmyActiveX, &IID_DmyActiveXEvents);
// TODO: 在此初始化控件的实例数据。
// 设置控件的初始大小
SetInitialSize(230, 230);
m_a = 16;
m_b = 12;
m_direction = FALSE;// 控制结果球的显示位置
}
// CmyActiveXCtrl::~CmyActiveXCtrl - 析构函数
CmyActiveXCtrl::~CmyActiveXCtrl()
{
// TODO: 在此清理控件的实例数据。
}
// CmyActiveXCtrl::OnDraw - 绘图函数
void CmyActiveXCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& /* rcInvalid */)
{
if (!pdc)
return;
// TODO: 用您自己的绘图代码替换下面的代码。
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH));
//pdc->Ellipse(rcBounds);
// 设置控件的背景颜色
CBrush brush(RGB(255, 255, 0));
pdc->FillRect(&rcBounds, &brush);
// 绘制3个圆
// 设置圆的背景色
CBrush brush2(RGB(0, 0, 0));
// 设置圆心位置
POINT p1 = { 40, 120 };
POINT p2 = { 190, p1.y };
POINT p3 = { (p1.x + p2.x) / 2, m_direction ? p1.y - 50 : p1.y + 50 };
// 设置圆的半径
int radious = 15;
// 画圆
CBrush *pOldBrush = pdc->SelectObject(&brush2);
pdc->Ellipse(p1.x - radious, p1.y - radious, p1.x + radious, p1.y + radious);
pdc->Ellipse(p2.x - radious, p2.y - radious, p2.x + radious, p2.y + radious);
pdc->Ellipse(p3.x - radious, p3.y - radious, p3.x + radious, p3.y + radious);
// 连接线
pdc->MoveTo(p1);
pdc->LineTo(p3);
pdc->LineTo(p2);
// 设置字体的颜色、对齐方式、背景透明
pdc->SetTextColor(RGB(255, 255, 255));
pdc->SetTextAlign(TA_CENTER);
pdc->SetBkMode(TRANSPARENT);
// 在圆中写入数值
CString strMsg;
strMsg.Format(L"%d", m_a);
pdc->TextOut(p1.x, p1.y - 9, strMsg);
strMsg.Format(L"%d", m_b);
pdc->TextOut(p2.x, p2.y - 9, strMsg);
strMsg.Format(L"%d", Mcd(m_a, m_b));
pdc->TextOut(p3.x, p3.y - 9, strMsg);
pdc->SelectObject(pOldBrush);
brush.DeleteObject();
brush2.DeleteObject();
}
// CmyActiveXCtrl::DoPropExchange - 持久性支持
void CmyActiveXCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
// TODO: 为每个持久的自定义属性调用 PX_ 函数。
}
// CmyActiveXCtrl::OnResetState - 将控件重置为默认状态
void CmyActiveXCtrl::OnResetState()
{
COleControl::OnResetState(); // 重置 DoPropExchange 中找到的默认值
// TODO: 在此重置任意其他控件状态。
}
// CmyActiveXCtrl::AboutBox - 向用户显示“关于”框
void CmyActiveXCtrl::AboutBox()
{
CDialogEx dlgAbout(IDD_ABOUTBOX_MYACTIVEX);
dlgAbout.DoModal();
}
// CmyActiveXCtrl 消息处理程序
void CmyActiveXCtrl::SetNum(LONG m_num1, LONG m_num2)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加调度处理程序代码
m_a = m_num1;
m_b = m_num2;
// 引发控件重绘调用我们的MCd函数结算结果并显示
Invalidate(TRUE);
}
void CmyActiveXCtrl::OndirectionChanged()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加属性处理程序代码
SetModifiedFlag();
}
LONG CmyActiveXCtrl::Mcd(LONG a, LONG b)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加调度处理程序代码
if (b == 0)
return a;
return Mcd(b, a%b);
}
void CmyActiveXCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
FirmMsg();
COleControl::OnLButtonDown(nFlags, point);
}
3. 实验结果(实验运行截图)
5. 实验中的问题及心得
- 理解Window系统COM概念,了解ActiveX控件编程基本原理、概念,以及编程流程。
- 学会了采用ActiveX控件编写组件。
- 一开始不会添加方法,事件,属性,后面看了附录2,跟着一步一步做,完成了操作,但是在过程中遇到了组件缺失问题,VS自动下载更新后就可以了。
- 注册ActiveX控件:管理员权限打开cmd,输入regsvr32 “组件所在的文件路径” 注册组件即可注册成功。
6. 附件
以下是实验代码