最近因项目需要在学习两个窗口之间的消息传递。
假设有相互独立的A、B两个对话框,现在点击A界面的Button控件之后,弹出B对话框。点击对话框B上按键之后在对话框A中响应自定义的消息。
1、工程创建完成之后资源视图会有一个对话框,更改其ID为IDD_DlgA。再加入一个对话框,style选popup,ID为IDD_DlgB。两个对话框的caption分别为A、B。
2、对话框A的头文件名和源文件名分别为:CDlgADlg.h、CDlgADlg.cpp(注意和CDlgA.h、CDlgA.cpp的区分)。手动给对话框B添加类,类名为CDlgB。
3、在对话框A中添加一个Button控件,为其添加变量为OkBtn;在对话框B中添加一个Button控件,为其添加变量为SendMessageToDlgABtn。
4、在A对话框的源文件中包含"DlgB.h"(至于为什么是在源文件中包含,而不是在头文件中包含,下文中会有解释),并在源文件任何函数之外定义一个对话框B的对象:CDlgB DlgB;。
然后在A对话框的OnInitDialog函数中添加如下代码:DlgB.Create(IDD_DlgB,this);
5、给对话框A中的Button控件添加事件处理程序,在其中添加如下代码:
void CDlgADlg::OnBnClickedOkBtn()
{
// TODO: 在此添加控件通知处理程序代码
POINT point;
::GetCursorPos(&point); //得到鼠标点击的位置
DlgB.SetWindowPos(NULL,point.x,point.y,0,0,SWP_NOSIZE);//在鼠标点击的位置弹出对话框B
DlgB.ShowWindow(SW_SHOW);//显示对话框B
}
这样就能在鼠标点击的位置弹出对话框B了
6、在对话框A中添加消息响应函数。
6.1、因为消息的实现是在对话框A中,所以我们在其头文件中添加如下宏定义:
#define TestMessage WM_USER+100
6.2、在资源视图IDD_DlgA中点击鼠标右键进入类向导
图中位置2处添加的就是宏定义的符号名:TestMessage。
6.3、添加完处理程序之后,编辑代码:
afx_msg LRESULT CDlgADlg::OnTestmessage(WPARAM wParam, LPARAM lParam)
{
MessageBox(_T("在对话框A中已处理对话框B控件的消息!"));
return 0;
}
这样消息的处理就算完成了,接下来就是如何响应到这条消息,及消息的发送,这也是难点所在。
7、消息发送
自然而然的我们就想到了使用 SendMessage()这个函数。该函数的原型为:
LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam)
参数:
hWnd:其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST:则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。
Msg:指定被发送的消息。
wParam:指定附加的消息特定信息。
IParam:指定附加的消息特定信息。
返回值:返回值指定消息处理的结果,依赖于所发送的消息。
我们先说说第二个参数,这个参数就是之前在对话框A中自定义消息的变量。而现在发送消息是在对话框B中实现的,故而,在对话框B的源文件中应该包含对话框A的头文件CDlgADlg.h。
现在我们就结合上文提到的不将"DlgB.h"包含到对话框A的头文件中来说说为什么也不将CDlgADlg.h包含于对话框B的头文件中,因为这样会导致CDlgADlg.h包含自身、CDlgB.h也包含自身。以致出现如下错误:
第三、四个参数是形参,用于传值。
下面重点说说第一个参数hWnd。它是第二个参数所在窗口的句柄,如果没有将第一个参数填写的相应的窗口句柄,消息是无法传递过去的。问题的关键所在就是如何在对话框B中得到对话框A的窗口句柄。
刚开始,我是想在对话框B中直接获取对话框A的窗口句柄,在网上搜了很多方法:
1)、this->m_hWnd或者this->GetSafeHwnd(),这个是获取当前窗口的句柄的,不适用我的程序。
2)、::FindWindow(NULL,WindowsName) ,这个需要知道窗口的名称,但是它不具一般性,虽然可能能够解决当前的问题,但是以后要是窗口没有名称怎么办(当窗口的border为none时,就不能设置名称),所以我也没有深入尝试这种方法。
3)、this->GetParent()->m_hWnd,这个是获取当前对话框的父对话框的句柄的,如果将对话框B的style设置为child,那么消息能够顺利的发送出去,处理函数也能顺利执行,但是由于实际的需要,我的对话框B不是对话框A的子对话框。故而这种方法也不适用。
无果,我开始转换思维,想着利用上面的第一种方法,在对话框A的按键处理程序中获取窗口A的句柄,并将其传到对话框B的程序中,这也不失为一种好方法。能让句柄传过去的关键就是找一个在两个源文件中的共同变量,这样才好用。
刚开始,我发现两个对话框的源文件中都包含了"DlgA.h",于是我就在"DlgA.h"文件中定义了一个全局变量 HWND DlgA_hWnd;然而这样会出现重定义:
然后我参考网上的资料,在对话框B的类中声明一个静态变量:static HWND m_DlgA_hWnd;,然后在对话框A的按键消息处理函数中修改代码:
void CDlgADlg::OnBnClickedOkBtn()
{
// TODO: 在此添加控件通知处理程序代码
//获取窗口A的句柄,并将其存至对话框B的静态成员m_DlgA_hWnd中
DlgB.m_DlgA_hWnd=this->m_hWnd;
POINT point;
::GetCursorPos(&point); //得到鼠标点击的位置
DlgB.SetWindowPos(NULL,point.x,point.y,0,0,SWP_NOSIZE);//在鼠标点击的位置弹出对话框B
DlgB.ShowWindow(SW_SHOW);//显示对话框B
}
然后在对话框B中的按钮处理程序中添加如下代码:
void CDlgB::OnBnClickedSendMessageToDlgaBtn()
{
// TODO: 在此添加控件通知处理程序代码
::SendMessage(CDlgB::m_DlgA_hWnd,TestMessage,0,0);
}
编译程序。便出现如下的错误:
在网上也查找了原因:1)、只有声明而没有实现文件,2)、链接时实现文件没有链接进去。
然而如果我声明的不是静态成员变量,则不会出现此问题(得在类外定义对象,不能使用CDlgB::m_DlgA_hWnd)。
这个问题的原因我还没有找到,希望大家不吝赐教,谢谢大家。
之后,我查到另外一种方法:在要获取窗口句柄的源文件中定义一个全局变量,在使用的源文件中的任何函数之外利用extern 声明一下这个全局变量。这样就行了,具体代码如下:
在DlgADlg.cpp中:
HWND DlgA_hWnd;
void CDlgADlg::OnBnClickedOkBtn()
{
// TODO: 在此添加控件通知处理程序代码
//获取窗口A的句柄,并将其存至DlgA_hWnd中
DlgA_hWnd=this->m_hWnd;
POINT point;
::GetCursorPos(&point); //得到鼠标点击的位置
DlgB.SetWindowPos(NULL,point.x,point.y,0,0,SWP_NOSIZE);//在鼠标点击的位置弹出对话框B
DlgB.ShowWindow(SW_SHOW);//显示对话框B
}
在DlgB.cpp中:
extern HWND DlgA_hWnd;
void CDlgB::OnBnClickedSendMessageToDlgaBtn()
{
// TODO: 在此添加控件通知处理程序代码
::SendMessage(DlgA_hWnd,TestMessage,0,0);
}
由于我也是刚接触MFC,有些地方的理解可能有些偏差,甚至是错误的,恳请大家指正,谢谢大家!
下面附上整个工程的代码,一起交流学习:
对话框A:
DlgADlg.h
// DlgADlg.h : 头文件
#pragma once
#include "afxwin.h"
#define TestMessage WM_USER+100
// CDlgADlg 对话框
class CDlgADlg : public CDialog
{
// 构造
public:
CDlgADlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
enum { IDD = IDD_DlgA };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
CButton OkBtn;
afx_msg void OnBnClickedOkBtn();
protected:
afx_msg LRESULT OnTestmessage(WPARAM wParam, LPARAM lParam);//自定义的消息响应函数
};
DlgADlg.cpp
// DlgADlg.cpp : 实现文件
#include "stdafx.h"
#include "DlgA.h"
#include "DlgADlg.h"
#include "DlgB.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CDlgB DlgB;
// CDlgADlg 对话框
CDlgADlg::CDlgADlg(CWnd* pParent /*=NULL*/)
: CDialog(CDlgADlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CDlgADlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_OkBtn, OkBtn);
}
BEGIN_MESSAGE_MAP(CDlgADlg, CDialog)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_OkBtn, &CDlgADlg::OnBnClickedOkBtn)
ON_MESSAGE(TestMessage, &CDlgADlg::OnTestmessage)
END_MESSAGE_MAP()
// CDlgADlg 消息处理程序
BOOL CDlgADlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
DlgB.Create(IDD_DlgB,this);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CDlgADlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CDlgADlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
HWND DlgA_hWnd;
void CDlgADlg::OnBnClickedOkBtn()
{
// TODO: 在此添加控件通知处理程序代码
//获取窗口A的句柄,并将其存至DlgA_hWnd中
DlgA_hWnd=this->m_hWnd;
POINT point;
::GetCursorPos(&point); //得到鼠标点击的位置
DlgB.SetWindowPos(NULL,point.x,point.y,0,0,SWP_NOSIZE);//在鼠标点击的位置弹出对话框B
DlgB.ShowWindow(SW_SHOW);//显示对话框B
}
afx_msg LRESULT CDlgADlg::OnTestmessage(WPARAM wParam, LPARAM lParam)
{
MessageBox(_T("在对话框A中已处理对话框B控件的消息!"));
return 0;
}
对话框B:
DlgB.h
#pragma once
#include "afxwin.h"
// CDlgB 对话框
class CDlgB : public CDialogEx
{
DECLARE_DYNAMIC(CDlgB)
public:
CDlgB(CWnd* pParent = NULL); // 标准构造函数
virtual ~CDlgB();
// 对话框数据
enum { IDD = IDD_DlgB };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
DECLARE_MESSAGE_MAP()
public:
CButton SendMessageToDlgABtn;
afx_msg void OnBnClickedSendMessageToDlgaBtn();
virtual BOOL OnInitDialog();
};
DlgB.cpp
// DlgB.cpp : 实现文件
//
#include "stdafx.h"
#include "DlgA.h"
#include "DlgB.h"
#include "DlgADlg.h"
#include "afxdialogex.h"
// CDlgB 对话框
IMPLEMENT_DYNAMIC(CDlgB, CDialogEx)
BOOL CDlgB::OnInitDialog()
{
CDialogEx::OnInitDialog();
// TODO: 在此添加额外的初始化
return TRUE; // return TRUE unless you set the focus to a control
}
CDlgB::CDlgB(CWnd* pParent /*=NULL*/)
: CDialogEx(CDlgB::IDD, pParent)
{
}
CDlgB::~CDlgB()
{
}
void CDlgB::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CDlgB, CDialogEx)
ON_BN_CLICKED(IDC_SendMessageToDlgABtn, &CDlgB::OnBnClickedSendMessageToDlgaBtn)
END_MESSAGE_MAP()
// CDlgB 消息处理程序
extern HWND DlgA_hWnd;
void CDlgB::OnBnClickedSendMessageToDlgaBtn()
{
// TODO: 在此添加控件通知处理程序代码
::SendMessage(DlgA_hWnd,TestMessage,0,0);
}