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

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值