first time asking here. I am trying to hook MessageBox in C++ to change Background color to #457B9D
and change foreground color to White color, also I want to change button text and button border to White color and background color of button to transparent. After that, I will use in my C# .NET 8.0 Windows Forms application, because I don’t want create a new form for MessageBox because our project has grown so complex that fixing the code in each Forms (I have a total of 90 forms and 75 user controls) is time consuming.
Here is my code:
framework.h
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
#include <stdlib.h>
pch.h
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#include "framework.h"
#endif //PCH_H
pch.cpp
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
dllmain.cpp
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
extern "C" __declspec(dllexport) void HookMessageBoxW();
extern "C" __declspec(dllexport) void UnhookMessageBoxW();
const COLORREF bgColor = RGB(69, 123, 157); // #457B9D
const COLORREF textColor = RGB(255, 255, 255); // White
HHOOK hHook = NULL;
WNDPROC oldButtonProc = NULL;
// Button subclass procedure
LRESULT CALLBACK ButtonSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
try {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rect;
GetClientRect(hWnd, &rect);
// Create and use a brush for background color
HBRUSH hBrush = CreateSolidBrush(bgColor);
FillRect(hdc, &rect, hBrush);
DeleteObject(hBrush); // Delete the brush to avoid resource leaks
SetTextColor(hdc, textColor);
SetBkMode(hdc, TRANSPARENT);
// Draw the text on the button
WCHAR text[512];
GetWindowText(hWnd, text, 512);
DrawText(hdc, text, -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
// Draw a white border around the button
HPEN hPen = CreatePen(PS_SOLID, 2, textColor);
HGDIOBJ oldPen = SelectObject(hdc, hPen);
Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
SelectObject(hdc, oldPen);
DeleteObject(hPen); // Delete the pen to avoid resource leaks
EndPaint(hWnd, &ps);
return 0;
}
default:
break;
}
}
catch (...) {
// Log the exception or handle it accordingly
return CallWindowProc(oldButtonProc, hWnd, uMsg, wParam, lParam);
}
// Default processing for other messages
return CallWindowProc(oldButtonProc, hWnd, uMsg, wParam, lParam);
}
// MessageBox subclass procedure
LRESULT CALLBACK MessageBoxSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
try {
HDC hdcStatic;
HBRUSH hBrush = CreateSolidBrush(bgColor);
switch (uMsg) {
case WM_CTLCOLORDLG:
case WM_CTLCOLORSTATIC:
case WM_CTLCOLORBTN:
hdcStatic = (HDC)wParam;
SetTextColor(hdcStatic, textColor);
SetBkColor(hdcStatic, bgColor);
DeleteObject(hBrush); // Make sure to delete the brush after use
return (LRESULT)hBrush;
case WM_INITDIALOG: {
HWND hButton = GetDlgItem(hWnd, IDOK);
if (hButton) {
oldButtonProc = (WNDPROC)SetWindowLongPtr(hButton, GWLP_WNDPROC, (LONG_PTR)ButtonSubclassProc);
}
hButton = GetDlgItem(hWnd, IDCANCEL);
if (hButton) {
SetWindowLongPtr(hButton, GWLP_WNDPROC, (LONG_PTR)ButtonSubclassProc);
}
break;
}
case WM_DESTROY:
SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)GetWindowLongPtr(hWnd, GWLP_USERDATA));
SetWindowLongPtr(hWnd, GWLP_USERDATA, 0);
break;
default:
break;
}
DeleteObject(hBrush); // Delete the brush before returning
}
catch (...) {
// Handle any exceptions to prevent crashes
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
// Default processing
return CallWindowProc((WNDPROC)GetWindowLongPtr(hWnd, GWLP_USERDATA), hWnd, uMsg, wParam, lParam);
}
// Hook procedure to capture MessageBox creation
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) {
try {
if (nCode == HCBT_CREATEWND) {
LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs;
// Check if it's a MessageBox
if (lpcs->lpszClass && wcscmp(lpcs->lpszClass, L"#32770") == 0) {
HWND hWnd = (HWND)wParam;
SetWindowLongPtr(hWnd, GWLP_USERDATA, SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MessageBoxSubclassProc));
}
}
}
catch (...) {
// Handle the exception gracefully
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
// Exported function to hook MessageBoxW
extern "C" __declspec(dllexport) void HookMessageBoxW() {
hHook = SetWindowsHookEx(WH_CBT, CBTProc, nullptr, GetCurrentThreadId());
}
// Exported function to unhook MessageBoxW
extern "C" __declspec(dllexport) void UnhookMessageBoxW() {
if (hHook) {
UnhookWindowsHookEx(hHook);
hHook = nullptr;
}
}
Program.cs
using OSVersionExtension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Education
{
internal static class Program
{
[DllImport("Win32.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void HookMessageBoxW();
[DllImport("Win32.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void UnhookMessageBoxW();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool SetProcessDPIAware();
static void Main()
{
HookMessageBoxW();
ApplicationConfiguration.Initialize();
//SetProcessDPIAware();
// Check Operating System section
//
// We only have rounded corners (actually is uDWM hack) on Windows 11
// On earlier OS version, we need to apply our custom rounded corner
// that defined in EllipseControl.cs
//
// To check Windows version, we use OSCheckExt from NuGet package
// manager
//
// So, we have these cases:
//
// Case 1: If users have Windows 11: Let's use native uDWM hack (inside
// dwmapi.dll) and opt in system rounded corners
//
// Case 2: If users doesn't have Windows 11: We need to create
// custom interface to enable rounded corners that defined
// in EllipseControl.cs then enable them in Form1.cs
//
// Note that on Windows Server 2022, we still doesn't have uDWM hack,
// actually uDWM hack exists only on Windows 11. So if we detected
// Windows Server Edition, we have to use our custom rounded corners
// defined in EllipseControl.cs to enable rounded corners effect
//
// 9/3/2024
OSVersionExtension.OperatingSystem osFetchData = OSVersion.GetOperatingSystem();
// Windows 11 detected
if (osFetchData == OSVersionExtension.OperatingSystem.Windows11)
{
Application.Run(new Education_MainForm(true));
}
else
{
Application.Run(new Education_MainForm(false));
}
}
}
}
When I tried to run in Debugging mode, none of useful:
Debug output in Visual Studio Enterprise 2022
I tried to figure what happening in System Informer, then I can see WerFault.exe is running because my application crashed:
System Informer check
When I tried to inspect what happening, then I get it:
System Informer inspect
It say: An unhandled exception was encountered during a user callback.
But I can’t find anything useful when debugging, that make me very confusing.
I’m running on Windows 11 x64 23H2 with Visual Studio Enterprise 2022
CyberDay is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.