[Win32/ATL]_[初级]_[处理WM_PAINT消息注意事项]

场景

  1. 在开发Win32/WTL程序时,遇到了使用CFolderDialog(atldlgs.h)打不开目录选择对话框的情况。具体表现是执行了窗口的DoModal,却没有窗口弹出来。 可以确定执行操作是在主线程,并不是工作线程。调试时暂停看堆栈,知道到DoModal方法里的SHBrowseForFolder就会停止,没有继续执行下去。主窗口也是卡住,但是不调用DoModal前主窗口不会卡的。偶尔情况下还是能打开CFolderDialog的,但是大部分测试都不行。什么情况?

说明

  1. CFolderDialogDoModal实现,SHBrowseForFolder^<2>^是打开一个选择文件的对话框。在之前的文章和课程说过,模态对话框在关闭前DoModal不会返回。主线程开启一个新的消息循环来处理模态对话框的消息。 当模态对话框没弹出来时,主界面也没有消息循环处理,自然不响应任何鼠标消息,所以看起来就是卡住了。
cpp 复制代码
INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow())
{
	if(m_bi.hwndOwner == NULL)   // set only if not specified before
		m_bi.hwndOwner = hWndParent;

	// Clear out any previous results
	m_szFolderPath[0] = 0;
	m_szFolderDisplayName[0] = 0;
	::CoTaskMemFree(m_pidlSelected);

	INT_PTR nRet = IDCANCEL;
	m_pidlSelected = ::SHBrowseForFolder(&m_bi);

	if(m_pidlSelected != NULL)
	{
		nRet = IDOK;

		// If BIF_RETURNONLYFSDIRS is set, we try to get the filesystem path.
		// Otherwise, the caller must handle the ID-list directly.
		if((m_bi.ulFlags & BIF_RETURNONLYFSDIRS) != 0)
		{
			if(::SHGetPathFromIDList(m_pidlSelected, m_szFolderPath) == FALSE)
				nRet = IDCANCEL;
		}
	}

	return nRet;
}
  1. 暂停看堆栈时,发现某个界面的OnPaint方法和SHBrowseForFolder竟然在同一个线程执行,正常情况下是不可能。看OnPaint方法,在方法内打断点,发现它一直循环执行。正常情况下只有绘制区域变更时,比如调用InvalidateRect或窗口移动时才会调用一次。所以这个OnPaint方法循环调用是有问题。 这个方法和其他窗口的绘图方法不一样的地方就是没有调用CPaintDC dc(m_hWnd);,只调用了Gdiplus::Graphics g(dc);来绘制背景。 增加CPaintDC dc(m_hWnd);后问题解决。

图1

  1. 看下CPaintDC的构造构造函数实现,发现调用了BeginPaint^[1]^。 在Win32应用程序中,处理WM_PAINT消息通常涉及调用BeginPaint来获取绘图相关的设备上下文。BeginPaint会标记缓冲区为有效,并清除重绘区域,这样在绘制时不会覆盖其他的绘图。
cpp 复制代码
CPaintDC(HWND hWnd)
{
	ATLASSERT(::IsWindow(hWnd));
	m_hWnd = hWnd;
	m_hDC = ::BeginPaint(hWnd, &m_ps);
}

~CPaintDC()
{
	ATLASSERT(m_hDC != NULL);
	ATLASSERT(::IsWindow(m_hWnd));
	::EndPaint(m_hWnd, &m_ps);
	Detach();
}
  1. 如果不调用BeginPaint,那么从InvalidateRect InvalidateRgn设置的更新区域就不会被标记,之后WM_PAINT消息就会被反复调用造成死循环。

例子

  1. 这里实现了一个错误处理WM_PAINT消息,只使用Gdiplus绘制文字的例子。

view.h

cpp 复制代码
// View.h : interface of the CView class
//
/

#pragma once

#include "atlcrack.h"
#include <gdiplus.h>

class CView : public CWindowImpl<CView>
{
public:
	DECLARE_WND_CLASS(NULL)

	BOOL PreTranslateMessage(MSG* pMsg);

	BEGIN_MSG_MAP_EX(CView)
		MESSAGE_HANDLER(WM_PAINT, OnPaint)
		MSG_WM_CREATE(OnCreate)
	END_MSG_MAP()

// Handler prototypes (uncomment arguments if needed):
//	LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
//	LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
//	LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)

	LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

	int OnCreate(LPCREATESTRUCT lpCreateStruct);


protected:

	Gdiplus::Font* font_ = nullptr;
};

view.cpp

cpp 复制代码
// View.cpp : implementation of the CView class
//
/

#include "stdafx.h"
#include "resource.h"

#include <gdiplus.h>
#include "View.h"

BOOL CView::PreTranslateMessage(MSG* pMsg)
{
	pMsg;
	return FALSE;
}

int CView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	Gdiplus::FontFamily fontFamily(L"Arial");
	font_ = new Gdiplus::Font(&fontFamily,16,
			(false)?Gdiplus::FontStyleBold:Gdiplus::FontStyleRegular,Gdiplus::UnitPixel);

	return 0;
}

LRESULT CView::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	//CPaintDC dc(m_hWnd);
	Gdiplus::Graphics g(m_hWnd);
	//Gdiplus::Graphics g(dc);
	static auto kMessage = L"OnPaint\n";
	Gdiplus::SolidBrush brush(Gdiplus::Color::Black);
	g.DrawString(kMessage, wcslen(L"OnPaint\n"), font_, Gdiplus::PointF(0, 0), nullptr, &brush);
	OutputDebugString(L"OnPaint\n");
	//TODO: Add your drawing code here

	bHandled = true;
	return 0;
}
cpp 复制代码
LRESULT CMainFrame::OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
	CFolderDialog ff(NULL, NULL, BIF_RETURNONLYFSDIRS | BIF_USENEWUI);
	if (IDOK == ff.DoModal()) {
		
	}
	return 0;
}

项目

下载地址: https://download.csdn.net/download/infoworld/90213937

参考

  1. beginPaint 函数

  2. SHBrowseForFolderW 函数 shlobj_core.h

相关推荐
嵌入式@hxydj24 天前
STM32F103单片机HAL库串口通信卡死问题解决方法
stm32·单片机·嵌入式硬件·串口·uart·hal·卡死
qiufeng_xinqing1 个月前
win32 解析、显示webp图片,及位图透明
webp·win32
Flame_Cyclone3 个月前
Win32获取系统版本信息
c++·win32·windows版本信息
Flame_Cyclone3 个月前
WinHttp异步请求封装
c++·win32·winhttp
△曉風殘月〆3 个月前
Win32打开UWP应用
vc++·uwp·win32
△曉風殘月〆4 个月前
WPF颜色(SolidColorBrush)和Win32颜色(COLOREF)互转的方法
wpf·win32·solidcolorbrush·colorref
Flame_Cyclone4 个月前
FakerInput 键盘鼠标输入封装
c++·windows·win32·fakerinput
Flame_Cyclone4 个月前
编写XBOX控制器实现鼠标键盘输入
c++·windows·win32·xbox·控制器模拟键盘鼠标
Flame_Cyclone4 个月前
原始输入解析XBOX手柄
c++·windows·win32·xbox