精选文章 VC++深入详解学习笔记8

VC++深入详解学习笔记8

作者:weixin_34054931 时间: 2021-02-07 02:40:36
weixin_34054931 2021-02-07 02:40:36
【摘要】Lesson18 Active控件
容器和服务器程序
容器应用程序时可以嵌入或链接对象的应用程序。Word就是容器应用程序。
服务器应用程序是创建对象并且当对象被双击时,可以被启动的应用程序。Excel就是服务器应用程序。
ActiveX控件不能独立运行,它必须被嵌入容器应用程序中,和容器应用程序一起运行。
 
Dispatch maps调度映射,主要是MFC提供让外部应用程序可以访问控件的...

Lesson18 Active控件

容器和服务器程序

容器应用程序时可以嵌入或链接对象的应用程序。Word就是容器应用程序。

服务器应用程序是创建对象并且当对象被双击时,可以被启动的应用程序。Excel就是服务器应用程序。

ActiveX控件不能独立运行,它必须被嵌入容器应用程序中,和容器应用程序一起运行。

 

Dispatch maps调度映射,主要是MFC提供让外部应用程序可以访问控件的属性和方法

Event maps事件映射,控件向包含它的容器发送事件通知

 

接口是外部程序和控件进行通信的协议,可以把接口看作是函数的集合,外部程序通过借口提供的方法,去访问控件的属性和方法。接口中所定义的所有函数都是纯虚函数

 

regsvr32 ...注册控件 regsvr32 /u....卸载控件

 

STDAPI DllRegisterServer(void)将控件信息写入注册表中

STDAPI DllUnregisterServer(void)卸载注册信息。

 

制作一个时间控件,在

void CClockCtrl::OnDraw(CDC* pdc, const CRect& rcBounds,

 const CRect& rcInvalid)中添加以下代码:

 CBrush brush(TranslateColor(GetBackColor()));

 pdc->FillRect(rcBounds,&brush);

 pdc->SetBkMode(TRANSPARENT);

 pdc->SetTextColor(TranslateColor(GetForeColor()));

 //为控件设置属性,必须在MFC ClassWizared中为控件添加属性,上面几 //行代码才有用

 

 CTime time=CTime::GetCurrentTime();

 CString str=time.Format("%H : %M : %S");

 pdc->TextOut(0,0,str);

这样就能做出一个静态的时间控件,如果我们想使控件实时显示时间,需要添加两个消息响应函数 WM_CREATE,WM_TIMER.

代码:

int CClockCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

 if (COleControl::OnCreate(lpCreateStruct) == -1)

 return -1;

 

 // TODO: 在此添加您专用的创建代码

 

 SetTimer(1,1000,NULL);

 return 0;

}

 

void CClockCtrl::OnTimer(UINT nIDEvent)

{

 // TODO: 在此添加消息处理程序代码和/或调用默认值

 Invalidate(); //立即引起窗口重绘

   //也可以使用InvalidateControl(); //强制窗口重绘,效果相同

 COleControl::OnTimer(nIDEvent);

}

要修改控件的背景色前景色和字体颜色

OnDraw中添加

 CBrush brush(TranslateColor(GetBackColor()));

 pdc->FillRect(rcBounds,&brush);

 pdc->SetBkMode(TRANSPARENT);

 pdc->SetTextColor(TranslateColor(GetForeColor()));

 

ActiveX控件的四种属性

Stock:为每个控件提供的标准属性,如字体或颜色。

Ambient:围绕控件的环境属性——已被置入容器的属性。这些属性不能被修改,但控件可以使用它们调整自己的属性。

Extended:这些是由容器处理的属性,一般包括大小和在屏幕上的位置。

Custom:由控件开发者添加的属性。

 

使控件具有多于一个属性页

首先在**ctl.cpp中找到Property Pages,代码如下:

BEGIN_PROPPAGEIDS(CClockCtrl, 2)

 PROPPAGEID(CClockPropPage::guid)

 PROPPAGEID(CLSID_CColorPropPage)

END_PROPPAGEIDS(CClockCtrl)

需要注意的是要显示几个属性页BEGIN_PROPPAGEIDS(CClockCtrl, 2)中的代码为几,如果没有修改或修改错误,会产生不可预料错误。

 

要增加标准属性,在_DClock上点击右键,选择增加属性,选中Stock,例如选择背景色和前景色

接下来会在Clock.odl下生成以下代码:

 dispinterface _DClock

 {

 properties:

 [id(DISPID_BACKCOLOR), helpstring("属性 BackColor")] OLE_COLOR BackColor;

 [id(DISPID_FORECOLOR), helpstring("属性 ForeColor")] OLE_COLOR ForeColor;

methods:

   [id(DISPID_ABOUTBOX)] void AboutBox();

 };

 

接下来新增自定义属性,方法同上,只是需选中“成员变量”或“get/put

会自动生成成员变量:m_Interval,和成员函数OnIntervalChanged

接下来添加代码:

void CClockCtrl::OnIntervalChanged(void)

{

 AFX_MANAGE_STATE(AfxGetStaticModuleState());

 

 // TODO: 在此添加属性处理程序代码

 if(m_Interval<0 || m_interval>6000)

 {

 m_Interval=2000;

 }

 KillTimer(1);

 SetTimer(1,m_Interval/1000*1000,NULL);

 

 SetModifiedFlag();

}

测试:运行ActiveX测试器,选择control---Invoke Methodsm_Interval进行修改。

 

为编辑框增加成员变量MFC ClassWizard-->Member Variables-->Add Member Variable-->

Optional property name:

选择自定义属性的外部名,这样我们不需要增加代码就能把控件和自定义属性相关联。

void CClockPropPage::DoDataExchange(CDataExchange* pDX)中会生成下面代码:

 DDP_Text(pDX, ID_EDIT_INTERVAL, m_updateInterval, _T("Interval") );

 DDX_Text(pDX, ID_EDIT_INTERVAL, m_updateInterval);

 

.NET2003下我始终找不到“Optional property name:”在哪,所以在我属性页上的编辑框无效,我只能选择control---Invoke Methods进行修改。

 

为控件增加函数,MFC ClassWizard-->Member Variables-->Add Method

Class Name要选择CClockCtrl

输入函数名,之后就可以在CClockCtrl类中找到了

 

我们选择MFC ClassWizard-->ActiveX Events--->Add Event

之后会在DClockEvents中增加一个事件,DClockEvents接口是源接口,控件将用这个接口发送通知事件,它不是控件本身实现的接口,这个接口是通过容器来实现的

 

 

如果要将自定义的控件属性保存下来,需要在

void CClockCtrl::DoPropExchange(CPropExchange* pPX)加入如下代码:

PX_Short(pPX,"Interval",m_interval,1000);

之后再在程序中修改代码:

 PX_Short(pPX,"Interval",m_interval,1000);

如果想使自定义控件属性实时地显示在容器属性列表中,

void CClockCtrl::OnIntervalChanged() 中加入如下代码:

 BoundPropertyChanged(0x1);   //调度代码为1

如果希望用户在设计模式时时钟控件停止运行,而在用户模式下运行,可以

void CClockCtrl::OnTimer(UINT nIDEvent)下修改代码如下:

 if(AmbientUserMode())   //查询环境属性

 InvalidateControl();

 

 

 

Lesson19 动态链接库DLL

Windows API中的所有函数都包含在DLL中。其中有三个最重要的DLLKernel32.dll,它包含用于管理内存、进程和线程的各个函数;User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;GDI32.dll,它包含用于画图和显示文本的各个函数。

静态库和动态库

静态库:函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.EXE文件);

在使用动态库的时候,往往提供两个文件:一个因入库和一个DLL。因入库包含被DLL导出的函数和变量的符号名,DLL包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行时候,再去加载DLL,访问DLL中导出的函数。

使用动态链接库的好处

可以采用多种编程语言来编写。

增强产品的功能。

提供二次开发的平台。

简化项目管理

可以节省磁盘空间和内存。

有助于资源的共享。

有助于实现应用程序的本地化

建立DLL文件代码如下:

_declspec(dllexport) int Add(int x,int y)

{

 return x+y;

}

_declspec(dllexport) int Subtract(int x,int y)

{

 return x-y;

}

//必须带_declspec(dllexport)文件,以生成*.lib文件

如果要查找*dll中包含信息,可在命令行下进入Debug所在目录,输入以下命令

dumpbin -exports dll.dll 

有些时候由于某种安装原因,dumpbin被认为是无效命令,接下来在

C:\Program Files\Microsoft Visual Studio\VC98\Bin\下找到VCVARS32.bat并在命令行运行,之后就能执行dumpbin命令了。

 

新建MFC程序,新建两个按钮,代码如下:

void CDllTestDlg::OnBtnAdd()

{

 // TODO: Add your control notification handler code here

 CString str;

 str.Format("3+5=&d",Add(3,5));

 MessageBox(str);

}

 

void CDllTestDlg::OnBtnSubtract()

{

 // TODO: Add your control notification handler code here

 CString str;

 str.Format("5-3=%d",Subtract(5,3));

 MessageBox(str);

}

为使编译器认识AddSubtract,必须在之前使用两个声明:

extern int Add(int x,int y);

extern int Subtract(int x,int y);

可以使用标示符表示这两个函数是从动态链接库的.lib文件引用的,以生成效率更高的代码

_declspec(dllimport) int Add(int x,int y);

_declspec(dllimport) int Subtract(int x,int y);

这两段代码我们也可以在DLL中新建一个头文件放进去,并在MFC程序中添加头文件

#include "..\Dll\Dll.h"

 

从原先Dll文件下Debug目录中复制*.libMFC程序文件夹下,并添加库函数

project--->setting--->link--->Object/Library Modules写下所复制的文件名

如果要查看DllTest.exe文件信息,使用命令行dumpbin -imports dlltest.exe

 

 

修改动态链接库Dll.h

#ifdef DLL_API

#else

#define DLL_API _declspec(dllimport)

#endif

 

DLL_API int Add(int x,int y);

DLL_API int Subtract(int x,int y);

 

修改Dll.cpp文件

#define DLL_API _declspec(dllexport)

#include "Dll.h"

 

int Add(int x,int y)

{

 return x+y;

}

int Subtract(int x,int y)

{

 return x-y;

}

这样做是为了方便外部程序调用同时方便内部程序使用,因为动态链接库中只有导出的函数才可以被使用,没有导出的函数在外部是看不到的,是不能被访问的

 

接下来导出整个类,代码:

class DLL_API point

{

public:

 void output(int x,int y); //如果只想导出一个函数,可把上边的DLL_API剪切 //然后放到void output(int x,int y);前边,虽然累没有被 //导出,但访问仍没有区别

};仍然受制于访问权限

实现:

void Point::output(int x,int y)

{

 HWND hwnd=GetForegroundWindow();

 HDC hdc=GetDC(hwnd);

 char buf[20];

 memset(buf,0,20);

 sprintf(buf,"x=%d,y=%d",x,y);

 TextOut(hdc,x,y,buf,strlen(buf));

 ReleaseDC(hwnd,hdc);

}

接下来在MFC程序新建一个按钮,调用动态链接库函数,代码如下:

 Point pt;

 pt.output(100,200);

 

因为C++导出或导入动态链接库会发生名字的改编,如果不想发生名字改编,我们可以使用如下代码:

#define DLL_API extern "c" _declspec(dllexport)

这样编译器就不会进行名字改编,一个用C语言编写的客户端程序就可以调用这个用C++编写的动态链接库。其缺点是,不能导入类中的函数

如果函数使用标准调用约定_stdcall,即使使用了extern "c",此函数仍会发生改编

 

 

接下来新建一个动态链接库文件,文件名为Dll2cpp文件代码为:

int Add(int x,int y)

{

 return x+y;

}

int Subtract(int x,int y)

{

 return x-y;

}

为了最终解决问题,我们可以新建一个模块文件Dll.def,以使得其他语言编制的程序也能使用我们的动态链接库。

添加代码

LIBRARY Dll2

 

EXPORTS   //即使调用_stdcall约定,也不会发生改编,而只会调用这里显示的Add    //字符串

Subtract

 

EXPORTS 语句引入了一个由一个或多个 definitions(导出的函数或数据)组成的节。每个定义必须在单独一行上。EXPORTS 关键字可以在第一个定义所在的同一行上或在前一行上。.def 文件可以包含一个或多个 EXPORTS 语句。

 

导出 definitions 的语法为:

 

entryname[=internalname] [@ordinal [NONAME]] [PRIVATE] [DATA]

entryname 是要导出的函数名或变量名。这是必选项。如果导出的名称与 DLL 中的名称不同,则通过 internalname 指定 DLL 中导出的名称。例如,如果 DLL 导出函数 func1(),要将它用作 func2(),则应指定:

 

EXPORTS

func2=func1

@ordinal 允许指定是序号而不是函数名将进入 DLL 的导出表。这有助于最小化 DLL 的大小。.LIB 文件将包含序号与函数之间的映射,这使您得以像通常在使用 DLL 的项目中那样使用函数名。

 

可选的 NONAME 关键字允许只按序号导出,并减小结果 DLL 中导出表的大小。但是,如果要在 DLL 上使用 GetProcAddress,则必须知道序号,因为名称将无效。

 

可选的 PRIVATE 关键字禁止将 entryname 放到由 LINK 生成的导入库中。它对同样是由 LINK 生成的图像中的导出无效。

 

可选的 DATA 关键字指定导出的是数据,而不是代码。例如,可以导出数据变量,如下所示:

 

EXPORTS

i DATA

 

接下来在MFC文件中改写按钮void CDllTestDlg::OnBtnAdd() 代码:

 HINSTANCE hInst;

 hInst=LoadLibrary("Dll2.dll"); //使用动态加载

 

 

 typedef int (*ADDPROC)(int a,int b);

//如果在DLL的函数中调用_stdcall,相应的应该把代码改为

//typedef int (_stdcall *ADDPROC)(int a,int b); //注意到处函数的调用约定

 ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"Add");//构造一个函数指针

 

 

 if(!Add)

 {

 MessageBox("获取函数地址失败!");

 return ;

 }

 CString str;

 str.Format("3+5=%d",Add(3,5));

 MessageBox(str);

 

因为调用LoadLibrary时动态加载动态链接库,所以不需要头文件和.lib文件

 

 

如果我们在动态链接库中使用标准调用约定_stdcall,而在可执行程序中使用动态加载DLL,会发生名字重编,如果知道DLL中函数的序号,这时可以使用宏MAKEINTRESOURCE把序号转变成名字,如:

ADDPROC Add=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE1)

 

 

DllMain

 

The DllMain function is an optional entry point into a dynamic-link library (DLL). If the function is used, it is called by the system when processes and threads are initialized and terminated, or upon calls to the LoadLibrary and FreeLibrary functions.

 

DllMain is a placeholder for the library-defined function name. You must specify the actual name you use when you build your DLL. For more information, see the documentation included with your development tools.

 

 

BOOL WINAPI DllMain(

 HINSTANCE hinstDLL,

 DWORD fdwReason,

 LPVOID lpvReserved

);

 

当我们的动态链接库不再使用时可以调用FreeLibrary使动态链接库使用计数减1,当使用计数为零时,系统会收回模块资源

FreeLibrary

The FreeLibrary function decrements the reference count of the loaded dynamic-link library (DLL). When the reference count reaches zero, the module is unmapped from the address space of the calling process and the handle is no longer valid.

 

BOOL FreeLibrary(

 HMODULE hModule

);

 

 

 

 

转载于:https://blog.51cto.com/thgenius/830573

您找到想要的结果了吗?
VC++深入详解学习笔记8
提交成功!非常感谢您的反馈,我们会继续努力做到更好
分享文章到微博
分享文章到朋友圈

上一篇:VC++深入详解学习笔记6

下一篇:Windows Server 2012中的DirectAccess部署

CSDN

CSDN

中国开发者社区CSDN (Chinese Software Developer Network) 创立于1999年,致力为中国开发者提供知识传播、在线学习、职业发展等全生命周期服务。
VC++深入详解学习笔记8介绍:华为云为您免费提供VC++深入详解学习笔记8在博客、论坛、帮助中心等栏目的相关文章,同时还可以通过 站内搜索 查询更多VC++深入详解学习笔记8的相关内容。| 移动地址: VC++深入详解学习笔记8 | 写博客