Напомните плиз реализацию wndproc для своего класса окна
От: Evgeniy Skvortsov Россия  
Дата: 01.12.18 16:25
Оценка:
Привет!

Вопрос не про то, как засунуть wndproc в класс, а про конкретную реализацию.
Здесь это обсуждалось, и приводили классную и очень изящную реализацию с двойной оконной процедурой.
Решение было очень короткое, This хранился в данных окна, и ни одно оконное сообщение не терялось.

То есть никаких thunk, никаких map, никаких глобальных переменных.

Найти не могу, уже извелся весь. 100% здесь было обсуждение, решение, по-моему приводилось с SO.
Re: Напомните плиз реализацию wndproc для своего класса окна
От: Дрободан Фрилич СССР  
Дата: 01.12.18 16:30
Оценка:
Evgeniy Skvortsov:

ES>Вопрос не про то, как засунуть wndproc в класс, а про конкретную реализацию.

ES>Здесь это обсуждалось, и приводили классную и очень изящную реализацию с двойной оконной процедурой.
ES>Решение было очень короткое, This хранился в данных окна, и ни одно оконное сообщение не терялось.

Пихать this в SetWindowLongPtr по индексу GWLP_USERDATA?

UPD. Не помню каким образом, при регистрации класса окна можно указать размер области под дополнительные user data...
Модератор-националист Kerk преследует оппонентов по политическим мотивам.
Отредактировано 01.12.2018 16:33 Bill Baklushi . Предыдущая версия .
Re: Напомните плиз реализацию wndproc для своего класса окна
От: Amygdala Россия  
Дата: 01.12.18 16:34
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

WTL глянь. Не помню как именно, но помню что вроде там мне более всего понравилось.
Re[2]: Напомните плиз реализацию wndproc для своего класса окна
От: Alexander G Украина  
Дата: 01.12.18 16:39
Оценка: +1
Здравствуйте, Amygdala, Вы писали:

A>WTL глянь. Не помню как именно, но помню что вроде там мне более всего понравилось.


Там thunk, доставшийся из ATL.
Корявая штука, для которой потом в DEP воркэраунд делали.
Русский военный корабль идёт ко дну!
Re[3]: Напомните плиз реализацию wndproc для своего класса окна
От: Amygdala Россия  
Дата: 01.12.18 16:44
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Там thunk, доставшийся из ATL.

AG>Корявая штука, для которой потом в DEP воркэраунд делали.

С wndproc любое решение корявым будет.
Re: Напомните плиз реализацию wndproc для своего класса окна
От: Alexander G Украина  
Дата: 01.12.18 16:48
Оценка: +1
Здравствуйте, 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 для своего класса окна
От: kov_serg Россия  
Дата: 01.12.18 17:21
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

ES>Привет!


ES>Вопрос не про то, как засунуть wndproc в класс, а про конкретную реализацию.

ES>Здесь это обсуждалось, и приводили классную и очень изящную реализацию с двойной оконной процедурой.
ES>Решение было очень короткое, This хранился в данных окна, и ни одно оконное сообщение не терялось.

ES>То есть никаких thunk, никаких map, никаких глобальных переменных.


ES>Найти не могу, уже извелся весь. 100% здесь было обсуждение, решение, по-моему приводилось с SO.


В C было принято использовать глобальную переменную.
Можно еще использовать threadvar. Но обычно делают так
https://blogs.msdn.microsoft.com/oldnewthing/20140203-00/?p=1893
до NC_CREATE всего несколько сообщений пропустите.
Re[4]: Напомните плиз реализацию wndproc для своего класса окна
От: Alexander G Украина  
Дата: 01.12.18 17:25
Оценка:
Здравствуйте, Amygdala, Вы писали:

A>С wndproc любое решение корявым будет.


Оно то да.

Но конкретно thunk, будучи более выгодным по перформансу (меньше на один переход по указателю из переменной) и по завязке на детали реализации (не требует параметров CreateWindowEx и знания, что первым приходит WM_NCCREATE), имеет все минуса решения с генерацией кода в памяти.

В Windows 10, кстати, появилась АПИ, чтобы переложить заботу об этом на систему:
https://docs.microsoft.com/en-us/windows/desktop/api/atlthunk/
Русский военный корабль идёт ко дну!
Re: Напомните плиз реализацию wndproc для своего класса окна
От: Aniskin  
Дата: 01.12.18 17:49
Оценка: 27 (2)
Если нет требования ловить все сообщения при инициализации окна, то наверное можно воспользоваться SetWindowSubclass.
Re[2]: Напомните плиз реализацию wndproc для своего класса окна
От: Conr Россия  
Дата: 01.12.18 17:49
Оценка: 5 (1)
Здравствуйте, Alexander G, Вы писали:

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 handle

            if( 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 для своего класса окна
От: Alexander G Украина  
Дата: 02.12.18 06:20
Оценка: +1
Здравствуйте, Conr, Вы писали:


C>WM_GETMINMAXINFO может прийди перед WM_NCCREATE!

C>Поэтому что-то в духе

Тогда, исходя из требования "не пропустить ни одно сообщение", я бы использовал передачу this через TLS, и доставил бы в оконный метод первое же сообщение, будь то даже преждевременный WM_GETMINMAXINFO.

(Надеюсь, Windows XP и старее уже никого не интересует? Ибо там проблемы со статическим TLS в динамических DLL)
Русский военный корабль идёт ко дну!
Re: Напомните плиз реализацию wndproc для своего класса окна
От: uuuser  
Дата: 04.12.18 02:33
Оценка:
Здравствуйте, Evgeniy Skvortsov, Вы писали:

ES>Привет!


ES>Вопрос не про то, как засунуть wndproc в класс, а про конкретную реализацию.

ES>Здесь это обсуждалось, и приводили классную и очень изящную реализацию с двойной оконной процедурой.
ES>Решение было очень короткое, This хранился в данных окна, и ни одно оконное сообщение не терялось.
ES>То есть никаких thunk, никаких map, никаких глобальных переменных.
ES>Найти не могу, уже извелся весь. 100% здесь было обсуждение, решение, по-моему приводилось с SO.

Win7 SDK \multimedia\directshow\common\BaseWindow.cpp
Win7 SDK \multimedia\directshow\common\BaseWindow.h

оно?
Re[2]: Напомните плиз реализацию wndproc для своего класса окна
От: Evgeniy Skvortsov Россия  
Дата: 04.12.18 11:33
Оценка:
Здравствуйте, uuuser, Вы писали:

U>Win7 SDK \multimedia\directshow\common\BaseWindow.cpp

U>Win7 SDK \multimedia\directshow\common\BaseWindow.h

U>оно?


Что-то не найду у себя таких файлов
Re[3]: Напомните плиз реализацию wndproc для своего класса окна
От: uuuser  
Дата: 04.12.18 21:19
Оценка: +1
Здравствуйте, 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);
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.