Вопрос не про то, как засунуть wndproc в класс, а про конкретную реализацию.
Здесь это обсуждалось, и приводили классную и очень изящную реализацию с двойной оконной процедурой.
Решение было очень короткое, This хранился в данных окна, и ни одно оконное сообщение не терялось.
То есть никаких thunk, никаких map, никаких глобальных переменных.
Найти не могу, уже извелся весь. 100% здесь было обсуждение, решение, по-моему приводилось с SO.
Re: Напомните плиз реализацию wndproc для своего класса окна
Evgeniy Skvortsov:
ES>Вопрос не про то, как засунуть wndproc в класс, а про конкретную реализацию. ES>Здесь это обсуждалось, и приводили классную и очень изящную реализацию с двойной оконной процедурой. ES>Решение было очень короткое, This хранился в данных окна, и ни одно оконное сообщение не терялось.
Пихать this в SetWindowLongPtr по индексу GWLP_USERDATA?
UPD. Не помню каким образом, при регистрации класса окна можно указать размер области под дополнительные user data...
Модератор-националист Kerk преследует оппонентов по политическим мотивам.
Здравствуйте, Evgeniy Skvortsov, Вы писали:
ES>Привет!
ES>Вопрос не про то, как засунуть wndproc в класс, а про конкретную реализацию. ES>Здесь это обсуждалось, и приводили классную и очень изящную реализацию с двойной оконной процедурой. ES>Решение было очень короткое, This хранился в данных окна, и ни одно оконное сообщение не терялось.
Если не потерять ни одно сообщение, то:
1. Передать this как lparam параметр в CreateWindow[Ex],
2. Поймать WM_NCCREATE и сохранить его this из CREATESTRUCT, в других сообщениях уже пользоваться сохранённым
3. Хранить оконный метод в: a) GWL_USERDATA b) положительных "смещениях" GetWindowLongPtr/SetWindowLongPtr c) SetProp / GetProp
Русский военный корабль идёт ко дну!
Re: Напомните плиз реализацию wndproc для своего класса окна
Здравствуйте, Evgeniy Skvortsov, Вы писали:
ES>Привет!
ES>Вопрос не про то, как засунуть wndproc в класс, а про конкретную реализацию. ES>Здесь это обсуждалось, и приводили классную и очень изящную реализацию с двойной оконной процедурой. ES>Решение было очень короткое, This хранился в данных окна, и ни одно оконное сообщение не терялось.
ES>То есть никаких thunk, никаких map, никаких глобальных переменных.
ES>Найти не могу, уже извелся весь. 100% здесь было обсуждение, решение, по-моему приводилось с SO.
Здравствуйте, Amygdala, Вы писали:
A>С wndproc любое решение корявым будет.
Оно то да.
Но конкретно thunk, будучи более выгодным по перформансу (меньше на один переход по указателю из переменной) и по завязке на детали реализации (не требует параметров CreateWindowEx и знания, что первым приходит WM_NCCREATE), имеет все минуса решения с генерацией кода в памяти.
AG>Если не потерять ни одно сообщение, то: AG>1. Передать this как lparam параметр в CreateWindow[Ex], AG>2. Поймать WM_NCCREATE и сохранить его this из CREATESTRUCT, в других сообщениях уже пользоваться сохранённым AG>3. Хранить оконный метод в: a) GWL_USERDATA b) положительных "смещениях" GetWindowLongPtr/SetWindowLongPtr c) SetProp / GetProp
WM_GETMINMAXINFO может прийди перед WM_NCCREATE!
Поэтому что-то в духе
static NativeWindow * from_handle(HWND handle)
{
auto ptr = ::GetWindowLongPtr(handle, GWLP_USERDATA);
return reinterpret_cast<NativeWindow *>( ptr );
}
static LRESULT WINAPI window_proc(HWND const hwnd, UINT const message, WPARAM const wparam, LPARAM const lparam)
{
// During window creation WM_GETMINMAXINFO may occur before WM_NCCREATE!
// so have to be ready for a null handleif( WM_NCCREATE == message)
{
auto cs = reinterpret_cast<CREATESTRUCT *>( lparam );
auto self = reinterpret_cast<NativeWindow *>( cs->lpCreateParams );
ASSERT( self );
ASSERT( !self->m_hwnd );
::SetLastError(0);
auto res = ::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>( self ));
ASSERT( !( res == 0 && ::GetLastError() != 0 ) ); // it's safe here to call GetLastError inside ASSERT
self->m_hwnd = hwnd;
}
else if( auto self = from_handle(hwnd) )
{
return static_cast<T*>(self)->on_window_message(message, wparam, lparam);
}
return ::DefWindowProc(hwnd, message, wparam, lparam);
}
Re[3]: Напомните плиз реализацию wndproc для своего класса окна
C>WM_GETMINMAXINFO может прийди перед WM_NCCREATE! C>Поэтому что-то в духе
Тогда, исходя из требования "не пропустить ни одно сообщение", я бы использовал передачу this через TLS, и доставил бы в оконный метод первое же сообщение, будь то даже преждевременный WM_GETMINMAXINFO.
(Надеюсь, Windows XP и старее уже никого не интересует? Ибо там проблемы со статическим TLS в динамических DLL)
Русский военный корабль идёт ко дну!
Re: Напомните плиз реализацию wndproc для своего класса окна
Здравствуйте, Evgeniy Skvortsov, Вы писали:
ES>Привет!
ES>Вопрос не про то, как засунуть wndproc в класс, а про конкретную реализацию. ES>Здесь это обсуждалось, и приводили классную и очень изящную реализацию с двойной оконной процедурой. ES>Решение было очень короткое, This хранился в данных окна, и ни одно оконное сообщение не терялось. ES>То есть никаких thunk, никаких map, никаких глобальных переменных. ES>Найти не могу, уже извелся весь. 100% здесь было обсуждение, решение, по-моему приводилось с SO.
Здравствуйте, Evgeniy Skvortsov, Вы писали: U>>Win7 SDK \multimedia\directshow\common\BaseWindow.cpp U>>Win7 SDK \multimedia\directshow\common\BaseWindow.h U>>оно? ES>Что-то не найду у себя таких файлов
BaseWindow.h
//////////////////////////////////////////////////////////////////////////
// BaseWindow.h: Abstract window class.
//
// Note: This class is designed to be as minimal as possible for
// purposes of a sample Win32 application. It is not meant to be
// a complete solution along the lines of MFC or ATL.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////#pragma once
class BaseWindow
{
public:
BaseWindow();
virtual ~BaseWindow() {}
static LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
HRESULT Create(HINSTANCE hInstance);
HRESULT Show(int nCmdShow);
virtual LRESULT OnReceiveMessage(UINT msg, WPARAM wparam, LPARAM lparam);
protected:
HRESULT Register();
virtual LPCTSTR ClassName() const = 0;
virtual LPCTSTR MenuName() const { return NULL; }
virtual LPCTSTR WindowName() const = 0;
virtual void OnPaint() = 0;
HWND m_hwnd;
HINSTANCE m_hInstance;
};
BaseWindow.cpp
//////////////////////////////////////////////////////////////////////////
// BaseWindow.cpp: Abstract window class.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////#include"wincontrol.h"#include"BaseWindow.h"//--------------------------------------------------------------------------------------
// BaseWindow constructor.
//--------------------------------------------------------------------------------------
BaseWindow::BaseWindow() : m_hwnd(NULL), m_hInstance(NULL)
{
}
//--------------------------------------------------------------------------------------
// BaseWindow::Register
// Description: Registers the window class.
//--------------------------------------------------------------------------------------
HRESULT BaseWindow::Register()
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = m_hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MenuName();
wcex.lpszClassName = ClassName();
wcex.hIconSm = NULL;
ATOM atom = RegisterClassEx(&wcex);
if (atom == 0)
{
return __HRESULT_FROM_WIN32(GetLastError());
}
else
{
return S_OK;
}
}
//--------------------------------------------------------------------------------------
// BaseWindow::Create
// Description: Creates an instance of the window.
//--------------------------------------------------------------------------------------
HRESULT BaseWindow::Create(HINSTANCE hInstance)
{
m_hInstance = hInstance;
HRESULT hr = Register();
if (SUCCEEDED(hr))
{
HWND hwnd = CreateWindow(
ClassName(),
WindowName(),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
NULL,
NULL,
m_hInstance,
this);
if (hwnd == 0)
{
hr = __HRESULT_FROM_WIN32(GetLastError());
}
}
return hr;
}
//--------------------------------------------------------------------------------------
// BaseWindow::Show
// Description: Show or hide the window.
//--------------------------------------------------------------------------------------
HRESULT BaseWindow::Show(int nCmdShow)
{
ShowWindow(m_hwnd, nCmdShow);
UpdateWindow(m_hwnd);
return S_OK;
}
//--------------------------------------------------------------------------------------
// BaseWindow::WindowProc
// Description: Window procedure.
//--------------------------------------------------------------------------------------
LRESULT CALLBACK BaseWindow::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BaseWindow *pWin = NULL;
if (uMsg == WM_NCCREATE)
{
// When we create the window, we pass in a pointer to this class
// as part of the CREATESTRUCT structure.
LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
pWin = (BaseWindow*)lpcs->lpCreateParams;
// Set the window handle.
pWin->m_hwnd = hwnd;
// Set the pointer to the class as user data.
_SetWindowLongPtr(hwnd, GWLP_USERDATA, pWin);
}
else
{
// Get the pointer to the class.
pWin = _GetWindowLongPtr<BaseWindow*>(hwnd, GWLP_USERDATA);
}
if (pWin)
{
return pWin->OnReceiveMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
//--------------------------------------------------------------------------------------
// BaseWindow::OnReceiveMessage
// Description: Handle window messages other than WM_NCCREATE.
//--------------------------------------------------------------------------------------
LRESULT BaseWindow::OnReceiveMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCDESTROY:
SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0);
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
case WM_PAINT:
OnPaint();
return 0;
}
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}