用win32API子类化按钮,但是调用原消息处理的时候出现问题

文章讲述了在C++中使用CallWindowProc函数时遇到的问题,涉及到内存管理不当导致m_OldProc丢失。作者发现原对象为局部变量,在WM_CREATE处理完后释放,解决方法是将传入的对象改为动态分配内存。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

调用CallWindowProc函数时,显示无法访问m_OldProc,经过测试发现其存储的值,与Init内存储的不同,但是找不到是哪里更改了。

更新:问题已解决,保存文章,作为警示。

解决过程:把传入的对象由直接申请改为new分配内存,即可正常运行。

原因:原对象是局部变量,WM_CREATE消息处理完成后,函数结束变量被释放,导致函数地址丢失。所以在按钮处理WM_PAINT消息时,变量已被释放,从m_OldProc获取到的是随机值,故而导致CallWindowProc出现访存错误。果然这就是C++基础没打好的后果...

BaseButton.h

#pragma once
#include<windows.h>

class  BaseButton {
public:
    BOOL SetBackPic(HBITMAP hBitmap, BOOL bReSize);
    BOOL Init(HWND hWnd);
    BaseButton();
    virtual ~BaseButton();
    static LRESULT WINAPI newProc(HWND hWnd, UINT uMsg, UINT wParam, LONG lParam);
    LRESULT CALLBACK WndProc(UINT message, WPARAM wParam, LPARAM lParam);

private:
    LRESULT OnPaint(HDC hdc);
    HWND m_hwnd;//当前的BUTTON句柄
    HWND m_parent_hwnd;//当前BUTTON的父窗口的句柄
    LONG m_OldProc;//原来的回调函数地址
    HBITMAP hBackPic;
};
BaseButton.cpp

#include "BaseButton.h"

BaseButton::BaseButton() {}
BaseButton::~BaseButton() {}
BOOL BaseButton::Init(HWND hwnd) {
	//IsWindow函数不应被用于其他线程创建的窗口,因为在函数调用后该窗口可能被销毁,或者句柄指向不同窗口(两个线程同时运行)
	if (IsWindow(hwnd)) {
		m_hwnd = hwnd;//托管原来的BUTTON
		m_parent_hwnd = GetParent(m_hwnd);
		//setProp给m_hwnd句柄对应窗口的属性列表添加一个条目(存在则修改条目,不存在则添加条目,本处为添加),添加失败返回0,否则非0
		if (SetProp(m_hwnd, "BaseButton", (HANDLE)this) == 0) {
			return FALSE;
		}
		//改变窗口回调过程
		m_OldProc = SetWindowLongPtr(m_hwnd, GWL_WNDPROC, (LONG)newProc);
	}
	return FALSE;
}
LRESULT WINAPI BaseButton::newProc(HWND hwnd, UINT uMsg, UINT wParam, LONG lParam) {
	BaseButton* bp = (BaseButton*)GetProp(hwnd, "BaseButton");
	return bp->WndProc(uMsg, wParam, lParam);
}
//改变原BUTTON的回调过程
LRESULT CALLBACK BaseButton::WndProc(UINT message, WPARAM wParam, LPARAM lParam) {
	PAINTSTRUCT ps;
	HDC hdc;
	switch (message) {
		//调用自己设定的刷新
		case WM_PAINT: {
			hdc = BeginPaint(m_hwnd, &ps);
			OnPaint(hdc);
			EndPaint(m_hwnd, &ps);
			break;
		}
	}
	//默认回调过程
	//m_OldProc = 0x77eb7a80;
	return CallWindowProc((WNDPROC)m_OldProc, m_hwnd, message, wParam, lParam);
}
//在这里添加BUTTON的背景设置
LRESULT BaseButton::OnPaint(HDC hdc) {
	HDC hdcMem, hdcBmp;
	RECT rt;
	int width, height;
	HBITMAP bmp;
	SetBkMode(hdc, TRANSPARENT);
	GetClientRect(m_hwnd, &rt);
	width = rt.right - rt.left;
	height = rt.bottom - rt.top;
	hdcMem = CreateCompatibleDC(hdc);
	hdcBmp = CreateCompatibleDC(hdc);
	SetBkMode(hdcMem, TRANSPARENT);
	bmp = CreateCompatibleBitmap(hdc, width, height);
	SelectObject(hdcMem, bmp);
	if (hBackPic != NULL)
	{
		SelectObject(hdcBmp, hBackPic);
	}
	BitBlt(hdcMem, 0, 0, width, height, hdcBmp, 0, 0, SRCCOPY);
	BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
	DeleteDC(hdcBmp);
	DeleteDC(hdcMem);
	DeleteObject(bmp);
	return TRUE;
}
BOOL BaseButton::SetBackPic(HBITMAP hBitmap, BOOL bReSize)
{
	BITMAP bm;
	RECT rt;
	hBackPic = hBitmap;
	if (hBitmap != NULL)
	{
		if (bReSize)
		{
			GetObject(hBackPic, sizeof(bm), &bm);
			SetWindowPos(m_hwnd, 0, 0, 0, bm.bmWidth, bm.bmHeight, SWP_NOMOVE | SWP_NOREPOSITION);
		}
		GetClientRect(m_hwnd, &rt);
		InvalidateRect(m_hwnd, &rt, TRUE);
		return TRUE;
	}
	return FALSE;
}
MultifunctionalCalculator.cpp

// MultifunctionalCalculator.cpp : 定义应用程序的入口点。
//

#include <windows.h>
#include "framework.h"
#include "MultifunctionalCalculator.h"
#include "layoutSave.h"
#include "BaseButton.h"

int APIENTRY WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
	_In_ PSTR szCmdLine, _In_ int iCmdShow);
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
LayoutSave* initLayoutSave();



int APIENTRY WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
	_In_ PSTR szCmdLine, _In_ int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("计算器");
	HWND hwnd;
	MSG msg;
	WNDCLASS wndclass;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	//wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MULTIFUNCTIONALCALCULATOR));
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("创建窗口失败!"),
			szAppName, MB_ICONERROR);
		return 0;
	}
	int iDefaultX = 510, iDefaulty = 220;
	int iMinWindowWidth = 340, iMinWindowHeight = 510;
	hwnd = CreateWindow(szAppName, TEXT("计算器"),
		WS_OVERLAPPEDWINDOW,
		iDefaultX, iDefaulty, iMinWindowWidth, iMinWindowHeight,
		NULL, NULL, hInstance, NULL);
	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
//窗口过程
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	LayoutSave* standardLayout = initLayoutSave();
	HDC hdc;
	PAINTSTRUCT ps;
	HWND hwndButtonList[100];
	//BaseButton changeButton;错误代码
    static BaseButton *changeButton = new BaseButton;
	switch (message) {
		case WM_CREATE: {
			hwndButtonList[0] = CreateWindow(TEXT("BUTTON"), TEXT("BUTTON1"), WS_CHILD | WS_VISIBLE, 0, 0, 100, 100, hwnd, 0, (HINSTANCE)lParam, NULL);
			changeButton.Init(hwndButtonList[0]);
			return 0;
		}
		case WM_PAINT: {
			hdc = BeginPaint(hwnd,&ps);
			EndPaint(hwnd, &ps);
			break;
		}
		case WM_DESTROY: {
			PostQuitMessage(0);
			return 0;
		}
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
LayoutSave* initLayoutSave() {
	LayoutSave* standardLayout = nullptr;
	int* standardWidth = new int[7]{ 50,77,77,77,77,77,77 };
	int* standardHeight = new int[7]{ 30,47,47,47,47,47,47 };
	standardLayout = new LayoutSave(10,175,7,standardWidth, standardHeight);
	return standardLayout;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值