The source files for this module are listed below. You can also download the module C_SubclassedWindow as a zip archive; this archive contains all the source files and documentation.
CSubclassedWindow allows easy window subclassing and message interception.
To start subclassing windows, you first have to derive a class from CSubclassedWindow. Also implement the virtual method ProcessMessage.
You can subclass one or more windows at the same time. To start subclassing a window, you call the Subclass method with the handle of the window you want to subclass. Call it again with another window's handle to also subclass that window. If you want to stop subclassing a particular window, call the StopSubclassing window with the window's handle, or just call StopSubclassingAll to stop subclassing all windows in one go. If your CSubclassedWindow-derived class instance goes out of scope, it will also automatically stop subclassing any window it subclasses.
Once you have started subclassing a window, your implementation of ProcessMessage will be called for each intercepted message. You can process the message yourself or just ignore it, depending on the needs in your code. The CMessage parameter contains all the message information in it's member variables. If you decide to handle the message, you can set the bStop member of the message to stop further processing of the message by the original window procedure for the window. In that case you can also specify a return value in the nResult member. You can call DefaultResult if you want to let the default message handler handle the message first (so you can e.g. modify it's result).
Multiple subclassers can subclass the same window. In this case, their ProcessMessage calls will be chained together; further processing stops when you indicate the message may not be processed any further. The result of the default message handler is cached by the DefaultResult method, so the default message processing will only occur once, even when multiple chained subclassers call this method.
Each file belonging to this source code module is listed below.
/*******************************************************************************
Version: 2
Author: Carl Colijn, TwoLogs
Contact: c.colijn@twologs.com
Source: https://www.twologs.com/sourcecode
This code is freely distributable, as long as this comment remains intact.
If you find this source useful, you may use this code in your own projects
free of charge, but some acknowledgement to the author of this code is always
appreciated :)
The source is however distributed 'as is' without waranty and/or support, and
may not be fit for each and every application. Use it at your own discretion
and at your own risk.
The source already has undergone testing. This doesn't mean however that all
bugs are removed from this piece of code. If you find one of them, please
contact me about it. I can however not guarantee when and if the bug will be
fixed.
More information about this module can be found in the accompanying HTML file.
*******************************************************************************/
#ifndef INCLUDE_TWOLOGS_COMMON_SUBCLASSEDWINDOW_H
#define INCLUDE_TWOLOGS_COMMON_SUBCLASSEDWINDOW_H
#include <windows.h>
#include <set>
// Single message
class CMessage {
public:
// The message details
const HWND hWindow;
const UINT eCode;
const WPARAM nWParam;
const LPARAM nLParam;
// The currently set result
LRESULT nResult;
// Whether further processing must stop
bool bStop;
// Con- & destructor
CMessage(WNDPROC fOriginalProc, HWND hWindow, UINT eMsg, WPARAM nWParam, LPARAM nLParam);
virtual ~CMessage();
// Gets the (cached) result from the default window procedure handler
LRESULT DefaultResult();
private:
// The window's own window proc
const WNDPROC m_fOriginalProc;
// Whether default processing has already been done for the message
bool m_bDefProcessed;
// The result of the default processing
LRESULT m_nDefResult;
};
class CSubclassedWindow {
public:
// Con- & destructor
CSubclassedWindow();
virtual ~CSubclassedWindow();
// Starts subclassing the given window
bool Subclass(HWND hWindow);
// Stops subclassing the given window
void StopSubclassing(HWND hWindow);
// Stops subclassing all windows
void StopSubclassingAll();
// Processes the given message
virtual void ProcessMessage(CMessage& oMsg) = 0;
private:
// The windows we handle
typedef std::set<HWND> CWindowSet;
CWindowSet m_ahWindows;
};
#endif // INCLUDE_TWOLOGS_COMMON_SUBCLASSEDWINDOW_H
/*******************************************************************************
Version: 2
Author: Carl Colijn, TwoLogs
Contact: c.colijn@twologs.com
Source: https://www.twologs.com/sourcecode
This code is freely distributable, as long as this comment remains intact.
If you find this source useful, you may use this code in your own projects
free of charge, but some acknowledgement to the author of this code is always
appreciated :)
The source is however distributed 'as is' without waranty and/or support, and
may not be fit for each and every application. Use it at your own discretion
and at your own risk.
The source already has undergone testing. This doesn't mean however that all
bugs are removed from this piece of code. If you find one of them, please
contact me about it. I can however not guarantee when and if the bug will be
fixed.
More information about this module can be found in the accompanying HTML file.
*******************************************************************************/
#include "SubclassedWindow.h"
#include <map>
// Controller for the subclassing
class CSubclassController {
public:
// Con- & destructor
CSubclassController();
virtual ~CSubclassController();
// Adds the given subclasser to the list for the given window
bool Add(HWND hWindow, CSubclassedWindow* poSubclasser);
// Removes the given subclasser from the list for the given window
void Remove(HWND hWindow, CSubclassedWindow* poSubclasser);
private:
// List of all subclassers
typedef std::set<CSubclassedWindow*> CSubclassers;
// Subclass info for a window
struct CSubclassInfo {
// The window that is subclassed
HWND hWindow;
// The window's own window proc
WNDPROC fOriginalProc;
// All registered subclassers for this window
CSubclassers apoSubclassers;
};
// Subclass info's by window handle
typedef std::map<HWND, CSubclassInfo*> CWindow2Info;
static CWindow2Info g_apoInfos;
// Window procedure for subclassing
static LRESULT WINAPI WndProc(
HWND hWnd,
UINT eMsg,
WPARAM nWParam,
LPARAM nLParam
);
};
// Subclass info's by window handle
CSubclassController::CWindow2Info CSubclassController::g_apoInfos;
// Con- & destructor
CSubclassController::CSubclassController() {
}
CSubclassController::~CSubclassController() {
// Kill all subclass info's
for (auto ppoNextInfo: g_apoInfos) {
// Delete this subclass info
delete ppoNextInfo.second;
}
}
// Adds the given subclasser to the list for the given window
bool CSubclassController::Add(HWND hWindow, CSubclassedWindow* poSubclasser) {
// Look if the given window already occurs in our lists
CSubclassInfo* poInfo = nullptr;
auto ppoFoundInfo = g_apoInfos.find(hWindow);
if (ppoFoundInfo != g_apoInfos.end()) {
// Yes -> re-use that one
poInfo = ppoFoundInfo->second;
} else {
// No -> start subclassing this window
poInfo = new CSubclassInfo;
poInfo->hWindow = hWindow;
poInfo->fOriginalProc = (WNDPROC)SetWindowLongPtr(
hWindow,
GWLP_WNDPROC,
(LONG_PTR)WndProc
);
if (poInfo->fOriginalProc != nullptr) {
// Done -> note it in the list
g_apoInfos[hWindow] = poInfo;
} else {
// Couldn't -> kill the info again
delete poInfo;
poInfo = nullptr;
}
}
// Look if an info could be found
if (poInfo != nullptr) {
// Yes -> add the subclasser to it
poInfo->apoSubclassers.insert(poSubclasser);
}
// And return if the subclasser could be added
return poInfo != nullptr;
}
// Removes the given subclasser from the list for the given window
void CSubclassController::Remove(HWND hWindow, CSubclassedWindow* poSubclasser) {
// Look if the given window can be found in our lists
auto ppoFoundInfo = g_apoInfos.find(hWindow);
if (ppoFoundInfo != g_apoInfos.end()) {
// Yes -> get the info for it
CSubclassInfo* poInfo = ppoFoundInfo->second;
// Remove the subclasser from it
poInfo->apoSubclassers.erase(poSubclasser);
// And look if this info is still needed
if (poInfo->apoSubclassers.size() == 0) {
// No -> stop subclassing the window
SetWindowLongPtr(hWindow, GWLP_WNDPROC, (LONG_PTR)poInfo->fOriginalProc);
// And kill the info
delete poInfo;
g_apoInfos.erase(ppoFoundInfo);
}
}
}
// Window procedure for the edit control
LRESULT WINAPI CSubclassController::WndProc(
HWND hWnd,
UINT eMsg,
WPARAM nWParam,
LPARAM nLParam) {
// Find the associated subclassers for this window
LRESULT nResult = 0;
auto ppoFoundInfo = g_apoInfos.find(hWnd);
if (ppoFoundInfo != g_apoInfos.end()) {
// Done -> extract the info
CSubclassInfo* poInfo = ppoFoundInfo->second;
// Create the message
CMessage oMsg(
poInfo->fOriginalProc,
hWnd,
eMsg,
nWParam,
nLParam
);
// Let each of the subclassers process the message
auto ppoNextSubclasser = poInfo->apoSubclassers.begin();
auto ppoLastSubclasser = poInfo->apoSubclassers.end();
for (; ppoNextSubclasser != ppoLastSubclasser && !oMsg.bStop; ++ppoNextSubclasser) {
// Done -> get the subclasser
CSubclassedWindow* poSubclasser = *ppoNextSubclasser;
// And let it process the message
poSubclasser->ProcessMessage(oMsg);
}
// And look if the original window procedure may still process it
if (oMsg.bStop) {
// No -> return the result from the last processing
nResult = oMsg.nResult;
} else {
// Yes -> pass it on
nResult = oMsg.DefaultResult();
}
}
// And return the result
return nResult;
}
// The subclass controller
CSubclassController g_oController;
// Con- & destructor
CMessage::CMessage(WNDPROC fOriginalProc, HWND hWindow, UINT eMsg, WPARAM nWParam, LPARAM nLParam):
hWindow(hWindow),
eCode(eMsg),
nWParam(nWParam),
nLParam(nLParam),
nResult(0),
bStop(false),
m_fOriginalProc(fOriginalProc),
m_bDefProcessed(false) {
}
CMessage::~CMessage() {
}
// Gets the (cached) result from the default window procedure handler
LRESULT CMessage::DefaultResult() {
// Look if we have already processed the message
if (!m_bDefProcessed) {
// No -> do so now
m_nDefResult = CallWindowProc(
m_fOriginalProc,
hWindow,
eCode,
nWParam,
nLParam
);
m_bDefProcessed = true;
}
// And return the result
return m_nDefResult;
}
// Con- & destructor
CSubclassedWindow::CSubclassedWindow() {
}
CSubclassedWindow::~CSubclassedWindow() {
StopSubclassingAll();
}
// Starts subclassing the given window
bool CSubclassedWindow::Subclass(HWND hWindow) {
// Subclass the window
bool bSuccess = g_oController.Add(hWindow, this);
if (bSuccess) {
// Done -> note the window we subclass
m_ahWindows.insert(hWindow);
}
// And return if successfull
return bSuccess;
}
// Stops subclassing the given window
void CSubclassedWindow::StopSubclassing(HWND hWindow) {
// Remove us from the controller's lists for this window
g_oController.Remove(hWindow, this);
// And remove this window from our own lists
m_ahWindows.erase(hWindow);
}
// Stops subclassing all windows
void CSubclassedWindow::StopSubclassingAll() {
// Process all windows we have registered
for (HWND hNextWindow: m_ahWindows) {
// Remove us from the controller's lists for this window
g_oController.Remove(hNextWindow, this);
}
// And we're not controlling any windows anymore
m_ahWindows.clear();
}