Windows核心编程系列文章仅作为实验报告和Windows编程学习参考,不作为任何技术文章,还望大佬们勿喷。

1. 实验名称

ActiveX控件编程

2. 实验环境

  1. VS2015
  2. Windows 10

3. 实验目的

  1. 理解Window系统COM概念,了解ActiveX控件编程基本原理、概念,以及编程流程。
  2. 理解容器应用程序和服务器应用程序的概念
  3. 采用ActiveX控件编写一个求两数最大公约数的组件,并调用组件,完成两数最大公约数。

4. 实验内容、步骤及结果

1. 实验内容

  1. 采用MFC ActiveX编写一个求两数最大公约数组件。如下图所示

【实验六】ActiveX控件编程 - 图1

  1. 然后编写一个测试程序,调用组件完成组件调用。如下图所示:

【实验六】ActiveX控件编程 - 图2

2. 实验步骤

  1. 首先创建一个MFC ActiveX项目
  2. 采用MFC ActiveX编写一个求两数最大公约数组件
  3. 代码如下:
  1. // myActiveXCtrl.cpp : CmyActiveXCtrl ActiveX 控件类的实现。
  2. #include "stdafx.h"
  3. #include "myActiveX.h"
  4. #include "myActiveXCtrl.h"
  5. #include "myActiveXPropPage.h"
  6. #include "afxdialogex.h"
  7. #ifdef _DEBUG
  8. #define new DEBUG_NEW
  9. #endif
  10. IMPLEMENT_DYNCREATE(CmyActiveXCtrl, COleControl)
  11. // 消息映射
  12. BEGIN_MESSAGE_MAP(CmyActiveXCtrl, COleControl)
  13. ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
  14. ON_WM_LBUTTONDOWN()
  15. END_MESSAGE_MAP()
  16. // 调度映射
  17. BEGIN_DISPATCH_MAP(CmyActiveXCtrl, COleControl)
  18. DISP_FUNCTION_ID(CmyActiveXCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
  19. DISP_FUNCTION_ID(CmyActiveXCtrl, "SetNum", dispidSetNum, SetNum, VT_EMPTY, VTS_I4 VTS_I4)
  20. DISP_PROPERTY_NOTIFY_ID(CmyActiveXCtrl, "direction", dispiddirection, m_direction, OndirectionChanged, VT_I2)
  21. DISP_FUNCTION_ID(CmyActiveXCtrl, "Mcd", dispidMcd, Mcd, VT_I4, VTS_I4 VTS_I4)
  22. END_DISPATCH_MAP()
  23. // 事件映射
  24. BEGIN_EVENT_MAP(CmyActiveXCtrl, COleControl)
  25. EVENT_CUSTOM_ID("FirmMsg", eventidFirmMsg, FirmMsg, VTS_NONE)
  26. END_EVENT_MAP()
  27. // 属性页
  28. // TODO: 按需要添加更多属性页。 请记住增加计数!
  29. BEGIN_PROPPAGEIDS(CmyActiveXCtrl, 1)
  30. PROPPAGEID(CmyActiveXPropPage::guid)
  31. END_PROPPAGEIDS(CmyActiveXCtrl)
  32. // 初始化类工厂和 guid
  33. IMPLEMENT_OLECREATE_EX(CmyActiveXCtrl, "MYACTIVEX.myActiveXCtrl.1",
  34. 0x85f4510, 0xb64, 0x4e28, 0x88, 0x45, 0x3c, 0x43, 0x8c, 0x22, 0xe9, 0xfe)
  35. // 键入库 ID 和版本
  36. IMPLEMENT_OLETYPELIB(CmyActiveXCtrl, _tlid, _wVerMajor, _wVerMinor)
  37. // 接口 ID
  38. const IID IID_DmyActiveX = { 0x12328EFC, 0x9968, 0x4FCE, { 0xA3, 0x26, 0x65, 0x99, 0x6E, 0xB7, 0x5A, 0x3D } };
  39. const IID IID_DmyActiveXEvents = { 0xF481325A, 0xD7BC, 0x4389, { 0x9A, 0xA8, 0x56, 0x16, 0x3A, 0x6C, 0xEE, 0x2B } };
  40. // 控件类型信息
  41. static const DWORD _dwmyActiveXOleMisc =
  42. OLEMISC_ACTIVATEWHENVISIBLE |
  43. OLEMISC_SETCLIENTSITEFIRST |
  44. OLEMISC_INSIDEOUT |
  45. OLEMISC_CANTLINKINSIDE |
  46. OLEMISC_RECOMPOSEONRESIZE;
  47. IMPLEMENT_OLECTLTYPE(CmyActiveXCtrl, IDS_MYACTIVEX, _dwmyActiveXOleMisc)
  48. // CmyActiveXCtrl::CmyActiveXCtrlFactory::UpdateRegistry -
  49. // 添加或移除 CmyActiveXCtrl 的系统注册表项
  50. BOOL CmyActiveXCtrl::CmyActiveXCtrlFactory::UpdateRegistry(BOOL bRegister)
  51. {
  52. // TODO: 验证您的控件是否符合单元模型线程处理规则。
  53. // 有关更多信息,请参考 MFC 技术说明 64。
  54. // 如果您的控件不符合单元模型规则,则
  55. // 必须修改如下代码,将第六个参数从
  56. // afxRegApartmentThreading 改为 0。
  57. if (bRegister)
  58. return AfxOleRegisterControlClass(
  59. AfxGetInstanceHandle(),
  60. m_clsid,
  61. m_lpszProgID,
  62. IDS_MYACTIVEX,
  63. IDB_MYACTIVEX,
  64. afxRegApartmentThreading,
  65. _dwmyActiveXOleMisc,
  66. _tlid,
  67. _wVerMajor,
  68. _wVerMinor);
  69. else
  70. return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
  71. }
  72. // CmyActiveXCtrl::CmyActiveXCtrl - 构造函数
  73. CmyActiveXCtrl::CmyActiveXCtrl()
  74. {
  75. InitializeIIDs(&IID_DmyActiveX, &IID_DmyActiveXEvents);
  76. // TODO: 在此初始化控件的实例数据。
  77. // 设置控件的初始大小
  78. SetInitialSize(230, 230);
  79. m_a = 16;
  80. m_b = 12;
  81. m_direction = FALSE;// 控制结果球的显示位置
  82. }
  83. // CmyActiveXCtrl::~CmyActiveXCtrl - 析构函数
  84. CmyActiveXCtrl::~CmyActiveXCtrl()
  85. {
  86. // TODO: 在此清理控件的实例数据。
  87. }
  88. // CmyActiveXCtrl::OnDraw - 绘图函数
  89. void CmyActiveXCtrl::OnDraw(
  90. CDC* pdc, const CRect& rcBounds, const CRect& /* rcInvalid */)
  91. {
  92. if (!pdc)
  93. return;
  94. // TODO: 用您自己的绘图代码替换下面的代码。
  95. CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH));
  96. //pdc->Ellipse(rcBounds);
  97. // 设置控件的背景颜色
  98. CBrush brush(RGB(255, 255, 0));
  99. pdc->FillRect(&rcBounds, &brush);
  100. // 绘制3个圆
  101. // 设置圆的背景色
  102. CBrush brush2(RGB(0, 0, 0));
  103. // 设置圆心位置
  104. POINT p1 = { 40, 120 };
  105. POINT p2 = { 190, p1.y };
  106. POINT p3 = { (p1.x + p2.x) / 2, m_direction ? p1.y - 50 : p1.y + 50 };
  107. // 设置圆的半径
  108. int radious = 15;
  109. // 画圆
  110. CBrush *pOldBrush = pdc->SelectObject(&brush2);
  111. pdc->Ellipse(p1.x - radious, p1.y - radious, p1.x + radious, p1.y + radious);
  112. pdc->Ellipse(p2.x - radious, p2.y - radious, p2.x + radious, p2.y + radious);
  113. pdc->Ellipse(p3.x - radious, p3.y - radious, p3.x + radious, p3.y + radious);
  114. // 连接线
  115. pdc->MoveTo(p1);
  116. pdc->LineTo(p3);
  117. pdc->LineTo(p2);
  118. // 设置字体的颜色、对齐方式、背景透明
  119. pdc->SetTextColor(RGB(255, 255, 255));
  120. pdc->SetTextAlign(TA_CENTER);
  121. pdc->SetBkMode(TRANSPARENT);
  122. // 在圆中写入数值
  123. CString strMsg;
  124. strMsg.Format(L"%d", m_a);
  125. pdc->TextOut(p1.x, p1.y - 9, strMsg);
  126. strMsg.Format(L"%d", m_b);
  127. pdc->TextOut(p2.x, p2.y - 9, strMsg);
  128. strMsg.Format(L"%d", Mcd(m_a, m_b));
  129. pdc->TextOut(p3.x, p3.y - 9, strMsg);
  130. pdc->SelectObject(pOldBrush);
  131. brush.DeleteObject();
  132. brush2.DeleteObject();
  133. }
  134. // CmyActiveXCtrl::DoPropExchange - 持久性支持
  135. void CmyActiveXCtrl::DoPropExchange(CPropExchange* pPX)
  136. {
  137. ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
  138. COleControl::DoPropExchange(pPX);
  139. // TODO: 为每个持久的自定义属性调用 PX_ 函数。
  140. }
  141. // CmyActiveXCtrl::OnResetState - 将控件重置为默认状态
  142. void CmyActiveXCtrl::OnResetState()
  143. {
  144. COleControl::OnResetState(); // 重置 DoPropExchange 中找到的默认值
  145. // TODO: 在此重置任意其他控件状态。
  146. }
  147. // CmyActiveXCtrl::AboutBox - 向用户显示“关于”框
  148. void CmyActiveXCtrl::AboutBox()
  149. {
  150. CDialogEx dlgAbout(IDD_ABOUTBOX_MYACTIVEX);
  151. dlgAbout.DoModal();
  152. }
  153. // CmyActiveXCtrl 消息处理程序
  154. void CmyActiveXCtrl::SetNum(LONG m_num1, LONG m_num2)
  155. {
  156. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  157. // TODO: 在此添加调度处理程序代码
  158. m_a = m_num1;
  159. m_b = m_num2;
  160. // 引发控件重绘调用我们的MCd函数结算结果并显示
  161. Invalidate(TRUE);
  162. }
  163. void CmyActiveXCtrl::OndirectionChanged()
  164. {
  165. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  166. // TODO: 在此添加属性处理程序代码
  167. SetModifiedFlag();
  168. }
  169. LONG CmyActiveXCtrl::Mcd(LONG a, LONG b)
  170. {
  171. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  172. // TODO: 在此添加调度处理程序代码
  173. if (b == 0)
  174. return a;
  175. return Mcd(b, a%b);
  176. }
  177. void CmyActiveXCtrl::OnLButtonDown(UINT nFlags, CPoint point)
  178. {
  179. // TODO: 在此添加消息处理程序代码和/或调用默认值
  180. FirmMsg();
  181. COleControl::OnLButtonDown(nFlags, point);
  182. }

3. 实验结果(实验运行截图)

【实验六】ActiveX控件编程 - 图3

5. 实验中的问题及心得

  • 理解Window系统COM概念,了解ActiveX控件编程基本原理、概念,以及编程流程。
  • 学会了采用ActiveX控件编写组件。
  • 一开始不会添加方法,事件,属性,后面看了附录2,跟着一步一步做,完成了操作,但是在过程中遇到了组件缺失问题,VS自动下载更新后就可以了。
  • 注册ActiveX控件:管理员权限打开cmd,输入regsvr32 “组件所在的文件路径” 注册组件即可注册成功。

6. 附件

以下是实验代码

链接:https://pan.baidu.com/s/118VXyNd0dc9QONzfKNcekA
提取码:8pct