1.窗口句柄获取
通过窗口句柄函数获取窗口句柄,主要为了某些情况下,需要控制窗体的展现等级
GetForegroundWindow() 返回 Windows 系统的当前激活的窗口句柄。
GetActiveWindow() 返回自己程序的当前激活窗口的句柄。
GetTopWindow()函数成功,返回值为在Z序顶部的子窗口句柄
WindowsAPI-获得当前活动窗口(运行自身消息循环的窗体)的句柄_iswindow winapi-CSDN博客
(1)FindWindowA函数
今天改一个bug,是窗口层级的问题,两个窗口(一个进度条窗口A,一个MsgBoxTip提示窗口在A上层)是一个上下的层级关系(只能操作其中一个,不能操作另外一个窗口),但最后展现效果是一个并列的关系(两个窗口都可以操作)。初步就是怀疑MsgBoxTip的父窗口句柄设置错误,但又获取不到窗口A句柄,最终使用FindWindowA函数获取到了A窗口句柄。
Sleep(100);
HWND hWnd = FindWindowA(NULL, "操作进行中,请稍候...");//查找进度条弹窗A的句柄
UI::MsgBoxTip(hWnd, L"操作失败!");//提示弹窗并设置父句柄
其中使用Sleep,是该弹窗A与MsgBoxTip不在一个线程,会出现MsgBoxTip先于弹窗A的情况,导致FindWindowA失败。
(2)线程窗口导致的弹窗显示异常
对于线程窗口,中间遇见过一种情况:一个函数里,中间有一段代码开了一个线程窗口,线程窗口结束,继续执行后续代码,后续代码给了一个MsgBoxTip,尽管设置了主窗口句柄为父句柄,但仍旧没有实现模态框的效果。后面调试,加上了一个SetActiveWindow函数,效果正常。猜测线程窗口结束后,程序的线程间资源切换有点异常,设置父窗口失效,导致该现象。
ProgressDlgParams& dlgParams = getProgressDlgParams();
dlgParams.m_nStep = 1;
dlgParams.m_strCaption = "操作进行中,请稍候...";
dlgParams.m_strMessage = "请稍候...";
dlgParams.m_bBottomMessage = FALSE;
dlgParams.m_bInfinite = TRUE;
dlgParams.m_bShowCancel = TRUE;
startProgress("线程启动", 0, 100, true);
endProgress();
SetActiveWindow();//不进行当前窗口激活,下面MsgBoxSucceed不会形成模态的效果
UI::MsgBoxSucceed(AfxGetApp()->m_pMainWnd->GetSafeHwnd(), L"数据导出完成!");
2.SendMessage附带消息
可以使用如下格式附带字符串消息,给目标句柄:
CString str1 = _T("123");
CString str2 = _T("hello");
::SendMessage(this->GetSafeHwnd(), TTMSG_WARNLIST1, (WPARAM)(LPCTSTR)str1, (LPARAM)(LPCTSTR)str2); //发送消息方式1
消息附带格式详细链接:MFC实例:SendMessage 发送字符串,结构体,数字_sendmessage发送字符串-CSDN博客
3.CString和数字互转
CString strTmp = "",str1="";
str1.Format("%I64u", m_iID);//int64
strTmp = strTmp + str1 + "-";
str1.Format("%f", m_dMoveToward);//f
4.mfc界面展示上角标、下角标
Unicode的上标与下标有相应编码对应,然后使用长字符进行一个对应转化。
wchar_t OptionText=‘\U00B3’;
CString cstring(OptionText); //然后再把该string赋值给需要显示的界面要素字段
5.OnCommand消息,实现界面不同控件事件响应的集中化处理
给mfc界面增加了三个按钮,但这三个按钮的实现函数是相似的,不想呆呆的写3个click响应消息去处理,想进行集中处理,网上搜了下,找到了OnCommand方式:
//一个重载虚函数,可进行相应的消息重写
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
wParam :低位表示菜单项、控件、或者快捷键的 ID 值,如果表示的是控件的话,高位值代表相应的通知消息值,如果地位表示快捷键的话,高位值为 1,如果低位值是菜单的话,高位值就为 0 。
lParam:如果 WM_COMMAND 来自控件的话 lParam 就是发送这个 WM_COMMAND 消息的控件的句柄,否则为 0 。
根据此,写出如下函数,完成了集中处理。
BOOL XXXXDlg::OnCommand(WPARAM wParam, LPARAM lParam) {
//获取触发的控件ID
UINT ctrlId = LOWORD(wParam);
//判断是否要进行处理
if (HIWORD(wParam)==BN_CLICKED) {
if (ctrlId == IDC_BUTTON3) {
OnBnClickedLayerListDlg(3);
}
else if (ctrlId == IDC_BUTTON2) {
OnBnClickedLayerListDlg(2);
}
else if (ctrlId == IDC_BUTTON1) {
OnBnClickedLayerListDlg(1);
}
}
return CDialogBase::OnCommand(wParam, lParam);
}
附上一个mfc消息过程的链接:MFC消息详解 (WindowProc|OnCommand|OnNotify)_mfc 子窗口windowproc未被调用-CSDN博客
6.dlg.DoModal()失败的问题
今天遇见一个bug,界面点击按钮DoModal()出一个窗口时,发现只有第一次窗口能够弹出成功,后续点击该按钮未有反应(后续DoModal()返回值=-1),经排查是窗口资源异常导致的,致使DoModal窗口时未能正确找到窗口ID进行映射,表现效果就是弹窗显示失败。
这里面主要牵涉资源模块的知识点:
1.HINSTANCE hDll = GetModuleHandleA("useDll.dll");AfxSetResourceHandle(hDll);获取相应dll的资源模块句柄,并设置目前使用句柄。
2.HINSTANCE hDllTst = AfxGetResourceHandle();获取当前的使用资源模块句柄。
3.DoModal()调用显示窗口资源后,会修改当前的使用资源模块,第一次窗口能显示成功而后续失败,也是该原因导致的。所以在DoModal调用前设置当前所需的资源句柄,即可解决。
调试过程代码如下:
void OnBnClickedBtnEditAdd()
{
//获取当前dll的资源模块句柄
HINSTANCE hDll = GetModuleHandleA("curdll.dll");
//获取当前使用的资源模块句柄
//第一次调用:hDll =curdll.dll句柄
//第二次调用:hDll =start.exe句柄
HINSTANCE hDllTst = AfxGetResourceHandle();
//设置当前所使用的资源模块句柄
//AfxSetResourceHandle(hDll);
CDlg dlg( this);
//第一次调用:当前句柄hDll =curdll.dll,窗口ID加载正常,窗口弹出
//第二次调用:当前句柄hDll =start.exe,窗口ID加载失败,窗口异常返回-1
int resDlg=dlg.DoModal();
//获取当前dll的资源模块句柄
//第一次调用:hDllTst1 =start.exe句柄,这里改变了当前资源句柄值,导致第二次使用窗口资源加载失败
HINSTANCE hDllTst1 = AfxGetResourceHandle();
if (resDlg != IDOK)
{
return;
}
}