VC++动态链接库(DLL)编程深入浅出(四)

来源:互联网 发布:海康税控开票软件 编辑:程序博客网 时间:2024/05/17 04:16
MFC扩展DLL的内涵为MFC的扩展,用户使用MFC扩展DLL就像使用MFC本身的DLL一样。除了可以在MFC扩展DLL的内部使用MFC以外,MFC扩展DLL与应用程序的接口部分也可以是MFC。我们一般使用MFC扩展DLL来包含一些MFC的增强功能,譬如扩展MFC的CStatic、CButton等类使之具备更强大的能力。

  使用Visual C++向导生产MFC扩展DLL时,MFC向导会自动增加DLL的入口函数DllMain:

 

 

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
 
// Remove this if you use lpReserved
 UNREFERENCED_PARAMETER(lpReserved);

 
if (dwReason == DLL_PROCESS_ATTACH)
 {
  TRACE0(
"MFCEXPENDDLL.DLL Initializing! ");

  
// Extension DLL one-time initialization
  if (!AfxInitExtensionModule(MfcexpenddllDLL, hInstance))
   
return 0;

  
// Insert this DLL into the resource chain
  
// NOTE: If this Extension DLL is being implicitly linked to by
  
//  an MFC Regular DLL (such as an ActiveX Control)
  
//  instead of an MFC application, then you will want to
  
//  remove this line from DllMain and put it in a separate
  
//  function exported from this Extension DLL.  The Regular DLL
  
//  that uses this Extension DLL should then explicitly call that
  
//  function to initialize this Extension DLL.  Otherwise,
  
//  the CDynLinkLibrary object will not be attached to the
  
//  Regular DLL's resource chain, and serious problems will
  
//  result.

  
new CDynLinkLibrary(MfcexpenddllDLL);
 }
 
else if (dwReason == DLL_PROCESS_DETACH)
 {
  TRACE0(
"MFCEXPENDDLL.DLL Terminating! ");
  
// Terminate the library before destructors are called
  AfxTermExtensionModule(MfcexpenddllDLL);
 }
 
return 1;   // ok
}

 上述代码完成MFC扩展DLL的初始化和终止处理。

  由于MFC扩展DLL导出函数和变量的方式与其它DLL没有什么区别,我们不再细致讲解。下面直接给出一个MFC扩展DLL的创建及在应用程序中调用它的例子。
下面我们将在MFC扩展DLL中导出一个按钮类CSXButton(扩展自MFC的CButton类),类CSXButton是一个用以取代 CButton的类,它使你能在同一个按钮上显示位图和文字,而MFC的按钮仅可显示二者之一。类CSXbutton的源代码在Internet上广泛流传,有很好的“群众基础”,因此用这个类来讲解MFC扩展DLL有其特殊的功效。

  MFC中包含一些宏,这些宏在DLL和调用DLL的应用程序中被以不同的方式展开,这使得在DLL和应用程序中,使用统一的一个宏就可以表示出输出和输入的不同意思:

 

// for data
#ifndef AFX_DATA_EXPORT
 
#define AFX_DATA_EXPORT __declspec(dllexport)
#endif
#ifndef AFX_DATA_IMPORT
 
#define AFX_DATA_IMPORT __declspec(dllimport)
#endif

// for classes
#ifndef AFX_CLASS_EXPORT
 
#define AFX_CLASS_EXPORT __declspec(dllexport)
#endif
#ifndef AFX_CLASS_IMPORT
 
#define AFX_CLASS_IMPORT __declspec(dllimport)
#endif

// for global APIs
#ifndef AFX_API_EXPORT
 
#define AFX_API_EXPORT __declspec(dllexport)
#endif
#ifndef AFX_API_IMPORT
 
#define AFX_API_IMPORT __declspec(dllimport)
#endif

#ifndef AFX_EXT_DATA
 #ifdef _AFXEXT
  
#define AFX_EXT_CLASS       AFX_CLASS_EXPORT
  
#define AFX_EXT_API         AFX_API_EXPORT
  
#define AFX_EXT_DATA        AFX_DATA_EXPORT
  
#define AFX_EXT_DATADEF
 
#else
  
#define AFX_EXT_CLASS       AFX_CLASS_IMPORT
  
#define AFX_EXT_API         AFX_API_IMPORT
  
#define AFX_EXT_DATA        AFX_DATA_IMPORT
  
#define AFX_EXT_DATADEF
 
#endif
#endif

  导出一个类,直接在类声明头文件中使用AFX_EXT_CLASS即可,以下是导出CSXButton类的例子:

 

#ifndef _SXBUTTON_H
#define _SXBUTTON_H

#define SXBUTTON_CENTER -1

class AFX_EXT_CLASS CSXButton : public CButton
{
// Construction
public:
 CSXButton();

// Attributes
private:
 
// Positioning
 BOOL  m_bUseOffset;    
 CPoint  m_pointImage;
 CPoint  m_pointText;
 
int   m_nImageOffsetFromBorder;
 
int   m_nTextOffsetFromImage;

 
// Image
 HICON  m_hIcon;     
 HBITMAP  m_hBitmap;
 HBITMAP  m_hBitmapDisabled;
 
int   m_nImageWidth, m_nImageHeight;

 
// Color Tab
 char  m_bColorTab;    
 COLORREF m_crColorTab;

 
// State
 BOOL  m_bDefault;
 UINT  m_nOldAction;
 UINT  m_nOldState;
 
// Operations
public:
 
// Positioning
 int  SetImageOffset( int nPixels ); 
 
int  SetTextOffset( int nPixels );
 CPoint SetImagePos( CPoint p );
 CPoint SetTextPos( CPoint p );

 
// Image
 BOOL SetIcon( UINT nID, int nWidth, int nHeight );
 BOOL SetBitmap( UINT nID, 
int nWidth, int nHeight );
 BOOL SetMaskedBitmap( UINT nID, 
int nWidth, int nHeight, COLORREF crTransparentMask );
 BOOL HasImage() { 
return (BOOL)( m_hIcon != 0  | m_hBitmap != 0 ); }

 
// Color Tab
 void SetColorTab(COLORREF crTab);

 
// State
 BOOL SetDefaultButton( BOOL bState = TRUE );
private:
 BOOL SetBitmapCommon( UINT nID, 
int nWidth, int nHeight, COLORREF crTransparentMask, BOOL bUseMask );
 
void CheckPointForCentering( CPoint &p, int nWidth, int nHeight );
 
void Redraw();

// Overrides
 
// ClassWizard generated virtual function overrides
 
//{{AFX_VIRTUAL(CSXButton)
 public:
 
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
 
//}}AFX_VIRTUAL

// Implementation
public:
 
virtual ~CSXButton();

 
// Generated message map functions
protected:
 
//{{AFX_MSG(CSXButton)
 afx_msg LRESULT OnGetText(WPARAM wParam, LPARAM lParam);
 
//}}AFX_MSG

 DECLARE_MESSAGE_MAP()
};

#endif

  把SXBUTTON.CPP文件直接添加到工程,编译工程,得到“mfcexpenddll.lib”和“mfcexpenddll.dll”两个文件。我们用Visual Studio自带的Depends工具可以查看这个.dll,发现其导出了众多符号(见图15)。


 
图15 导出类时导出的大量符号 (+放大该图片)

  这些都是类的构造函数、析构函数及其它成员函数和变量经编译器处理过的符号,我们直接用__declspec(dllexport)语句声明类就导出了这些符号。

  如果我们想用.lib文件导出这些符号,是非常困难的,我们需要在工程中生成.map文件,查询.map文件的符号,然后将其一一导出。如图16,打开DLL工程的settings选项,再选择Link,勾选其中的产生MAP文件(Generate mapfile)就可以产生.map文件了。

   打开mfcexpenddll工程生成的.map文件,我们发现其中包含了图15中所示的符号(symbol)

 0001:00000380  ?HasImage@CSXButton@@QAEHXZ 10001380 f i SXBUTTON.OBJ
 0001:000003d0  ??0CSXButton@@QAE@XZ       100013d0 f   SXBUTTON.OBJ
 0001:00000500  ??_GCSXButton@@UAEPAXI@Z   10001500 f i SXBUTTON.OBJ
 0001:00000570  ??_ECSXButton@@UAEPAXI@Z   10001570 f i SXBUTTON.OBJ
 0001:00000630  ??1CSXButton@@UAE@XZ       10001630 f   SXBUTTON.OBJ
0001:00000700 ?_GetBaseMessageMap@CSXButton@@KGPBUAFX_MSGMAP@@XZ 10001700 f   SXBUTTON.OBJ
 0001:00000730 ?GetMessageMap@CSXButton@@MBEPBUAFX_MSGMAP@@XZ 10001730 f   SXBUTTON.OBJ
 0001:00000770    ?Redraw@CSXButton@@AAEXXZ  10001770 f i SXBUTTON.OBJ
 0001:000007d0    ?SetIcon@CSXButton@@QAEHIHH@Z 100017d0 f   SXBUTTON.OBJ
……………………………………………………………………..//省略
 

图16 产生.map文件 (+放大该图片)

  所以,对于MFC扩展DLL,我们不宜以.lib文件导出类。

 

 

6.2 MFC扩展DLL的调用

  在DLL所在工作区新增一个dllcall工程,它是一个基于对话框的MFC EXE程序。在其中增加两个按钮SXBUTTON1、SXBUTTON2,并设置其属性为“Owner draw”,如图17。

图17 设置按钮属性为“Owner draw”

  在工程中添加两个ICON资源:IDI_MSN_ICON(MSN的图标)、IDI_REFBAR_ICON(Windows的系统图标)。

  修改工程的“calldllDlg.h”头文件为:

#  同时,修改“calldllDlg.cpp”文件,使得m_button1、m_button2成员变量与对话框上的按钮控件建立关联:

 

void CCalldllDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 
//{{AFX_DATA_MAP(CCalldllDlg)
 DDX_Control(pDX, IDC_BUTTON2, m_button2);
 DDX_Control(pDX, IDC_BUTTON1, m_button1);
 
//}}AFX_DATA_MAP
}

 

  修改BOOL CCalldllDlg::OnInitDialog()函数,在其中增加对两个按钮设置ICON的代码:

 

BOOL CCalldllDlg::OnInitDialog()
{
 CDialog::OnInitDialog();

 
// Add "About..." menu item to system menu.

 
// IDM_ABOUTBOX must be in the system command range.
 ASSERT((IDM_ABOUTBOX & 0xFFF0== IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX 
< 0xF000);

 CMenu
* pSysMenu = GetSystemMenu(FALSE);
 
if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  
if (!strAboutMenu.IsEmpty())
  {
   pSysMenu
->AppendMenu(MF_SEPARATOR);
   pSysMenu
->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }

 
// Set the icon for this dialog.  The framework does this automatically
 
//  when the application's main window is not a dialog
 SetIcon(m_hIcon, TRUE);   // Set big icon
 SetIcon(m_hIcon, FALSE);  // Set small icon

 
// TODO: Add extra initialization here
 m_button1.SetIcon(IDI_MSN_ICON,16,16);
 m_button2.SetIcon(IDI_REFBAR_ICON,
16,16);

 
return TRUE;  // return TRUE  unless you set the focus to a control
}

  运行程序,将出现如图18的对话框,图形和文字同时出现在按钮上,这说明我们正确地调用了MFC扩展DLL。

图18 DLL扩展的按钮被显示

  如果我们不修改void CCalldllDlg::DoDataExchange(CDataExchange* pDX),即不增加下列代码:

DDX_Control(pDX, IDC_BUTTON2,  m_button2);
DDX_Control(pDX, IDC_BUTTON1,  m_button1);

  我们也可以在BOOL CCalldllDlg::OnInitDialog()函数中添加如下代码实现m_button1、m_button2与IDC_BUTTON1、IDC_BUTTON2的关联:

m_button1.SubclassDlgItem(IDC_BUTTON1, this);
m_button2.SubclassDlgItem(IDC_BUTTON2, this);

  但是,DDX_Control与按钮类的SubclassDlgItem成员函数不能同时存在,否则程序会出错。

6.3 总结

  由以上分析可知,MFC扩展DLL的导出与引用方式与前几节所讲述的方式没有太大的差别,MFC扩展DLL主要强调对MFC进行功能扩展。因此,如果DLL的目标不是增强MFC的功能,其与应用程序的接口也不是MFC,请不要将DLL建立为MFC扩展DLL。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 房贷银行卡丢了怎么办? 行驶证丢了怎么办补办 行驶证丢了怎么办异地 浦发信用卡盗刷怎么办 苹果id被盗变砖怎么办 信用卡丢了被刷怎么办 ins注册不了怎么办安卓 偷玩电脑被发现怎么办 做作业不认真的怎么办 老人脑供血不足怎么办 哺乳期吃了辣的怎么办 孕32周胎位臀位怎么办 怀孕32周胎位不正怎么办 7个月胎位不正怎么办 胎心监护老不过怎么办 8个月了胎位不正怎么办 怀孕八个月了胎位不正怎么办 怀孕八个月胎位不正怎么办 欠债的人跑了怎么办 赌博输了100万怎么办 我赌博输了4万怎么办 办80张信用卡怎么办的 19岁负债十几万怎么办 欠了十几万该怎么办 网贷负债十几万怎么办 赌博欠了十几万怎么办 欠了网贷跑了会怎么办 欠银行钱还不起怎么办 欠小额贷款公司的钱还不上怎么办 负债30万没工作怎么办 华为p9耗电太快怎么办 酷派手机反应慢怎么办 网上买手机被骗了怎么办 买手机贵了怎么办12315 在手机店被骗了怎么办 在转转上被骗了怎么办 微信被骗了800怎么办 苹果6被偷走该怎么办 苹果7被偷走该怎么办 月经量突然少了怎么办 我月经老是不来怎么办