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 TMessage {
public:
// The message details
const HWND window;
const UINT code;
const WPARAM wParam;
const LPARAM lParam;
// The currently set result
LRESULT result;
// Whether further processing must stop
bool stop;
// Con- & destructor
TMessage(WNDPROC OriginalProc, HWND window, UINT msgCode, WPARAM wParam, LPARAM lParam);
virtual ~TMessage();
// Gets the (cached) result from the default window procedure handler
LRESULT DefaultResult();
private:
// The window's own window proc
const WNDPROC m_OriginalProc;
// Whether default processing has already been done for the message
bool m_defProcessed;
// The result of the default processing
LRESULT m_defResult;
};
class TSubclassedWindow {
public:
// Con- & destructor
TSubclassedWindow();
virtual ~TSubclassedWindow();
// Starts subclassing the given window
bool Subclass(HWND window);
// Stops subclassing the given window
void StopSubclassing(HWND window);
// Stops subclassing all windows
void StopSubclassingAll();
// Processes the given message
virtual void ProcessMessage(TMessage& msg) = 0;
private:
// The windows we handle
typedef std::set<HWND> TWindows;
TWindows m_windows;
};
#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 TSubclassController {
public:
// Con- & destructor
TSubclassController();
virtual ~TSubclassController();
// Adds the given subclasser to the list for the given window
bool Add(HWND window, TSubclassedWindow* subclasserPtr);
// Removes the given subclasser from the list for the given window
void Remove(HWND window, TSubclassedWindow* subclasserPtr);
private:
// List of all subclassers
typedef std::set<TSubclassedWindow*> TSubclasserPtrs;
// Subclass info for a window
struct TSubclassInfo {
// The window that is subclassed
HWND window;
// The window's own window proc
WNDPROC OriginalProc;
// All registered subclassers for this window
TSubclasserPtrs subclasserPtrs;
};
// Subclass info's by window handle
typedef std::map<HWND, TSubclassInfo*> TInfoPtrsByWindow;
static TInfoPtrsByWindow g_infoPtrsByWindow;
// Window procedure for subclassing
static LRESULT WINAPI WndProc(
HWND window,
UINT msgCode,
WPARAM wParam,
LPARAM lParam
);
};
// Subclass info's by window handle
TSubclassController::TInfoPtrsByWindow TSubclassController::g_infoPtrsByWindow;
// Con- & destructor
TSubclassController::TSubclassController() {
}
TSubclassController::~TSubclassController() {
// Kill all subclass info's
for (auto nextInfoPtrPtr: g_infoPtrsByWindow) {
// Delete this subclass info
delete nextInfoPtrPtr.second;
}
}
// Adds the given subclasser to the list for the given window
bool TSubclassController::Add(HWND window, TSubclassedWindow* subclasserPtr) {
// Look if the given window already occurs in our lists
TSubclassInfo* infoPtr = nullptr;
auto foundInfoPtrPtr = g_infoPtrsByWindow.find(window);
if (foundInfoPtrPtr != g_infoPtrsByWindow.end()) {
// Yes -> re-use that one
infoPtr = foundInfoPtrPtr->second;
} else {
// No -> start subclassing this window
infoPtr = new TSubclassInfo;
infoPtr->window = window;
infoPtr->OriginalProc = (WNDPROC)SetWindowLongPtr(
window,
GWLP_WNDPROC,
(LONG_PTR)WndProc
);
if (infoPtr->OriginalProc != nullptr) {
// Done -> note it in the list
g_infoPtrsByWindow[window] = infoPtr;
} else {
// Couldn't -> kill the info again
delete infoPtr;
infoPtr = nullptr;
}
}
// Look if an info could be found
if (infoPtr != nullptr) {
// Yes -> add the subclasser to it
infoPtr->subclasserPtrs.insert(subclasserPtr);
}
// And return if the subclasser could be added
return infoPtr != nullptr;
}
// Removes the given subclasser from the list for the given window
void TSubclassController::Remove(HWND window, TSubclassedWindow* subclasserPtr) {
// Look if the given window can be found in our lists
auto foundInfoPtrPtr = g_infoPtrsByWindow.find(window);
if (foundInfoPtrPtr != g_infoPtrsByWindow.end()) {
// Yes -> get the info for it
TSubclassInfo* infoPtr = foundInfoPtrPtr->second;
// Remove the subclasser from it
infoPtr->subclasserPtrs.erase(subclasserPtr);
// And look if this info is still needed
if (infoPtr->subclasserPtrs.size() == 0) {
// No -> stop subclassing the window
SetWindowLongPtr(window, GWLP_WNDPROC, (LONG_PTR)infoPtr->OriginalProc);
// And kill the info
delete infoPtr;
g_infoPtrsByWindow.erase(foundInfoPtrPtr);
}
}
}
// Window procedure for the edit control
LRESULT WINAPI TSubclassController::WndProc(
HWND window,
UINT msgCode,
WPARAM wParam,
LPARAM lParam) {
// Find the associated subclassers for this window
LRESULT result = 0;
auto foundInfoPtrPtr = g_infoPtrsByWindow.find(window);
if (foundInfoPtrPtr != g_infoPtrsByWindow.end()) {
// Done -> extract the info
TSubclassInfo* infoPtr = foundInfoPtrPtr->second;
// Create the message
TMessage msg(
infoPtr->OriginalProc,
window,
msgCode,
wParam,
lParam
);
// Let each of the subclassers process the message
auto nextSubclasserPtrPtr = infoPtr->subclasserPtrs.begin();
auto lastSubclasserPtrPtr = infoPtr->subclasserPtrs.end();
for (; nextSubclasserPtrPtr != lastSubclasserPtrPtr && !msg.stop; ++nextSubclasserPtrPtr) {
// Done -> get the subclasser
TSubclassedWindow* subclasserPtr = *nextSubclasserPtrPtr;
// And let it process the message
subclasserPtr->ProcessMessage(msg);
}
// And look if the original window procedure may still process it
if (msg.stop) {
// No -> return the result from the last processing
result = msg.result;
} else {
// Yes -> pass it on
result = msg.DefaultResult();
}
}
// And return the result
return result;
}
// The subclass controller
TSubclassController g_controller;
// Con- & destructor
TMessage::TMessage(WNDPROC OriginalProc, HWND window, UINT msgCode, WPARAM wParam, LPARAM lParam):
window(window),
code(msgCode),
wParam(wParam),
lParam(lParam),
result(0),
stop(false),
m_OriginalProc(OriginalProc),
m_defProcessed(false) {
}
TMessage::~TMessage() {
}
// Gets the (cached) result from the default window procedure handler
LRESULT TMessage::DefaultResult() {
// Look if we have already processed the message
if (!m_defProcessed) {
// No -> do so now
m_defResult = CallWindowProc(
m_OriginalProc,
window,
code,
wParam,
lParam
);
m_defProcessed = true;
}
// And return the result
return m_defResult;
}
// Con- & destructor
TSubclassedWindow::TSubclassedWindow() {
}
TSubclassedWindow::~TSubclassedWindow() {
StopSubclassingAll();
}
// Starts subclassing the given window
bool TSubclassedWindow::Subclass(HWND window) {
// Subclass the window
bool allOK = g_controller.Add(window, this);
if (allOK) {
// Done -> note the window we subclass
m_windows.insert(window);
}
// And return if successful
return allOK;
}
// Stops subclassing the given window
void TSubclassedWindow::StopSubclassing(HWND window) {
// Remove us from the controller's lists for this window
g_controller.Remove(window, this);
// And remove this window from our own lists
m_windows.erase(window);
}
// Stops subclassing all windows
void TSubclassedWindow::StopSubclassingAll() {
// Process all windows we have registered
for (HWND nextWindow: m_windows) {
// Remove us from the controller's lists for this window
g_controller.Remove(nextWindow, this);
}
// And we're not controlling any windows anymore
m_windows.clear();
}