Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / C++ Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
 Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
ъъъъъ
Member

Откуда:
Сообщений: 2114
Всем привет.
Срочно переписывается гора кода с (извините) Delphi, нужен класс, инкапсулирующий (невидимое) окно Windows.
В наследниках класса может переопределяться оконная процедура, добавляться обработчики сообщений окна.
В Delphi это было сделано с помощью AllocateHWnd(). Эта функция создает невидимое окно, и назначает ему оконную процедуру.
Фокус в том, что процедура - не простая ("статическая"), а метод. Который можно объявить виртуальным/абстрактным и реализовывать в наследниках.
+ AllocateHWnd() - осторожно, Delphi!
  TWndMethod = procedure(var Message: TMessage) of object;
...
function AllocateHWnd(Method: TWndMethod): HWND;
var
  TempClass: TWndClass;
  ClassRegistered: Boolean;
begin
  UtilWindowClass.hInstance := HInstance;
{$IFDEF PIC}
  UtilWindowClass.lpfnWndProc := @DefWindowProc;
{$ENDIF}
  ClassRegistered := GetClassInfo(HInstance, UtilWindowClass.lpszClassName,
    TempClass);
  if not ClassRegistered or (TempClass.lpfnWndProc <> @DefWindowProc) then
  begin
    if ClassRegistered then
      Windows.UnregisterClass(UtilWindowClass.lpszClassName, HInstance);
    Windows.RegisterClass(UtilWindowClass);
  end;
  Result := CreateWindowEx(WS_EX_TOOLWINDOW, UtilWindowClass.lpszClassName,
    '', WS_POPUP {+ 0}, 0, 0, 0, 0, 0, 0, HInstance, nil);
  if Assigned(Method) then
    SetWindowLong(Result, GWL_WNDPROC, Longint(MakeObjectInstance(Method)));
end;

За фокус превращения метода класса в статическую функцию отвечает хитрая процедура MakeObjectInstance():
+ MakeObjectInstance () - тут всё ещё хуже.

function MakeObjectInstance(Method: TWndMethod): Pointer;
const
  BlockCode: array[1..2] of Byte = (
    $59,       { POP ECX }
    $E9);      { JMP StdWndProc }
  PageSize = 4096;
var
  Block: PInstanceBlock;
  Instance: PObjectInstance;
begin
  if InstFreeList = nil then
  begin
    Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    Block^.Next := InstBlockList;
    Move(BlockCode, Block^.Code, SizeOf(BlockCode));
    Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2], @StdWndProc));
    Instance := @Block^.Instances;
    repeat
      Instance^.Code := $E8;  { CALL NEAR PTR Offset }
      Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code);
      Instance^.Next := InstFreeList;
      InstFreeList := Instance;
      Inc(Longint(Instance), SizeOf(TObjectInstance));
    until Longint(Instance) - Longint(Block) >= SizeOf(TInstanceBlock);
    InstBlockList := Block;
  end;
  Result := InstFreeList;
  Instance := InstFreeList;
  InstFreeList := Instance^.Next;
  Instance^.Method := Method;
end;

{ Free an object instance }

procedure FreeObjectInstance(ObjectInstance: Pointer);
begin
  if ObjectInstance <> nil then
  begin
    PObjectInstance(ObjectInstance)^.Next := InstFreeList;
    InstFreeList := ObjectInstance;
  end;
end;


- от интерпретации текста которой я несколько теряюсь. Надо бы позвать программиста, да где же его взять в час ночи?

Вопрос: как из нестатического метода объекта сделать статический? На C++, конечно.

Сообщение было отредактировано: 3 июн 21, 01:12
3 июн 21, 01:19    [22330664]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
ъъъъъ
Member

Откуда:
Сообщений: 2114
ъъъъъ
как из нестатического метода объекта сделать статический

В общем, сделал без извратов. Оставил процедуру статической.

У метода создания окна CreateWindowEx*() есть параметр LPVOID lpParam, который представляет собой ссылку на особую структуру, которую я не использую. Вместо этого я передаю ссылку на экземпляр объекта.
Далее, при создании окна эта самая оконная процедура вызывается для обработки сообщения WM_CREATE, и это значение доступно в параметре LPARAM оконной процедуры. Привожу ссылку к ссылке на экземпляр класса, и всё.

Дальше, например, вызываю виртуальный метод класса и т.д.
3 июн 21, 01:54    [22330673]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
ъъъъъ
Member

Откуда:
Сообщений: 2114
ъъъъъ
У метода создания окна CreateWindowEx*() есть параметр LPVOID lpParam, который представляет собой ссылку на особую структуру, которую я не использую. Вместо этого я передаю ссылку на экземпляр объекта.
Далее, при создании окна эта самая оконная процедура вызывается для обработки сообщения WM_CREATE, и это значение доступно в параметре LPARAM оконной процедуры. Привожу ссылку к ссылке на экземпляр класса, и всё.

Не всё. :(
В следующих (не WM_CREATE) вызовах оконной процедуры значение указателя уже недоступно. Надо его где-то хранить... хранить можно в окне: сохранить - SetWindowLongPtr() с индексом, равным GWL_USERDATA. Считывать GetWindowLongPtr()... вроде всё.
3 июн 21, 02:05    [22330675]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
Siemargl
Member

Откуда: 010100
Сообщений: 6493
ъъъъъ,

непонятно, зачем такое порно было сделано.
нет контекста, почему с разными методами вызывается

а так все просто
SetWindowLong(Result, GWL_WNDPROC, UsualWndProc<MyWndClass>);


template class<T> LRESULT UsualWndProc(HWND a, UINT b, WPARAM c, LPARAM d)
{
  T temp;
  return temp.WndProc(a, b, c, d);
}

Если конечно, класс меняет состояния и надо их хранить между вызовами, тогда придется temp создавать в глобальной переменной (например списке), как в оригинале.
3 июн 21, 02:12    [22330677]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
ъъъъъ
Member

Откуда:
Сообщений: 2114
Siemargl
непонятно, зачем такое порно было сделано.

Древняя VCL.
3 июн 21, 02:50    [22330681]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
ъъъъъ
Member

Откуда:
Сообщений: 2114
Siemargl
Если конечно, класс меняет состояния и надо их хранить между вызовами, тогда придется temp создавать в глобальной переменной (например списке), как в оригинале.

Я ссылку в окне (GWL_USERDATA) храню, как выше написал. А оконную процедуру не подменяю, из неё вызываю обычный виртуальный метод класса (сохраненного в окне), вот и всё.

Всем спасибо.
3 июн 21, 03:10    [22330683]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
ъъъъъ
Member

Откуда:
Сообщений: 2114
ъъъъъ
...
Всем спасибо.


Ещё вопрос.

Посмотрите, пожалуйста, насколько крив код с точки зрения человека, не испорченного годами Delphi-кодинга.
+ Напомню
Напомню, что :
ъъъъъ
...
переписывается гора кода с (извините) Delphi, нужен класс, инкапсулирующий (невидимое) окно Windows...


Интерфейс класса: конструктор/деструктор, handle хэндл невидимого окна и метод add_msg().
Метод add_msg() - для добавления обработчика конкретного сообщения windows.

Пример использования. Задача: через 3 секунды показать диалог "Привет!.
Реализация: для экземпляра объекта создаем обработчик события WM_TIMER.
	stealth_window sw;
	sw.add_msg(WM_TIMER, [&](UINT msg, WPARAM& w_param, LPARAM& l_param)
	{
    	    KillTimer(sw.handle, 999);
	    MessageBoxA(sw.handle, "Привет! Время пришло", "C++ отладка", MB_OK);
	});

Cоздаем таймер с интервалом 3000 мс.
	SetTimer(sw.handle, 999, 3000, nullptr);


Результат:

К сообщению приложен файл. Размер - 1Kb
4 июн 21, 05:21    [22331165]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
ъъъъъ
Member

Откуда:
Сообщений: 2114
ъъъъъ,

код:
+ Интерфейс класса stealth_window

Файл stealth_window.h
#pragma once
#include <Windows.h>
#include <map>
#include <functional>

using msg_proc = std::function<void(UINT msg, WPARAM&, LPARAM&)>; // Обработчик события Windows

class stealth_window
{
public:

	stealth_window();
	virtual ~stealth_window();

	void add_msg(UINT msg, msg_proc proc);
	HWND handle = nullptr; // Хэндл невидимого окна

protected:
	virtual void wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
private:
	std::multimap<UINT, msg_proc> msg_procs;
	tagWNDCLASSA uwc{ 0, WndProc,0, 0, 0, 0, 0, 0, 0, "TPUtilWindow" };
	static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
};


+ Реализация класса stealth_window

Файл stealth_window.cpp
#include "stealth_window.h"
stealth_window::stealth_window()
{
	const HMODULE hinstance = GetModuleHandleA(nullptr);
	tagWNDCLASSA tmp_class;
	this->uwc.hInstance = hinstance;
	const auto registered = GetClassInfoA(hinstance, uwc.lpszClassName, &tmp_class);

	if (!registered || (tmp_class.lpfnWndProc != DefWindowProcA)) {
		if (registered)
			UnregisterClassA(uwc.lpszClassName, hinstance);
		RegisterClassA(&uwc);
	}

	handle = CreateWindowExA(WS_EX_TOOLWINDOW, uwc.lpszClassName, "",
		WS_POPUP, 0, 0, 0, 0, 0, 0, hinstance, this);
}


stealth_window::~stealth_window()
{
	if (handle) {
		DestroyWindow(handle);
		handle = nullptr;
	}
}

void stealth_window::add_msg(UINT msg, msg_proc proc)
{
	msg_procs.insert({ msg, std::move(proc) });
}

void stealth_window::wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ // Обработка сообщений. Сообщение с одинаковым кодом может иметь несколько обработчиков. 
	auto& it = msg_procs.lower_bound(msg);
	const auto& upper_bound = msg_procs.upper_bound(msg);
	while (it != upper_bound)
		(it++)->second(msg, wParam, lParam);
}


LRESULT stealth_window::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	stealth_window* realThis;

	if (msg == WM_NCCREATE) // Первое сообщение - при создании окна. В lParam приходит this
	{
		realThis = static_cast<stealth_window*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);

		SetLastError(0);
		if (!SetWindowLongPtr(hwnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>(realThis)))
			if (GetLastError())
				return FALSE;
	}
	else
		realThis = reinterpret_cast<stealth_window*>(GetWindowLongPtr(hwnd, GWL_USERDATA));

	if (realThis)
		realThis->wnd_proc(hwnd, msg, wParam, lParam);

	return DefWindowProcA(hwnd, msg, wParam, lParam);
}

4 июн 21, 05:25    [22331166]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 8256
ъъъъъ,
Неиспорченные в дельфи7 для сообщений делали просто TList с адресами подписчиков)
4 июн 21, 06:58    [22331168]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 8256
ъъъъъ,
Какой смысл переписывать 20 летнюю программу с одних буковок на другие не трогая архитектуру и технологии.
4 июн 21, 07:17    [22331170]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
petrav
Member

Откуда:
Сообщений: 2944
ъъъъъ,

А зачем вам невидимое окно? Для таймеров в невизуальных компонентах? Неужели такая проблема есть и в wxWidgets?
4 июн 21, 09:41    [22331205]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
ъъъъъ
Member

Откуда:
Сообщений: 2114
petrav,

таймер - это просто пример.
Это ядро диспетчера обмена сообщениями. Например, между нитями. Или, например, можно попросить ось, чтобы она тебе слала сообщения об изменении состояния группы файлов в конкретном директории. Много чего полезного с его помощью сделано. Наверняка в wxWidgets что-то свое есть. Но, как всегда, времени разбираться нет, трясти надо.
4 июн 21, 10:33    [22331220]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
petrav
Member

Откуда:
Сообщений: 2944
Что-то моё сообщение удалили без всяких комментариев. Оно культурное было и по делу.
4 июн 21, 18:14    [22331544]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
Alex_Ustinov
Member

Откуда: Nickel
Сообщений: 3797
типа wxEvtHandler
4 июн 21, 19:09    [22331588]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
ъъъъъ
Member

Откуда:
Сообщений: 2114
Alex_Ustinov
типа wxEvtHandler

Можно ловить виндосовские мессаги? Нельзя.
4 июн 21, 21:01    [22331640]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
petrav
Member

Откуда:
Сообщений: 2944
ъъъъъ
Alex_Ustinov
типа wxEvtHandler

Можно ловить виндосовские мессаги? Нельзя.

QCoreApplication::installNativeEventFilter().
4 июн 21, 21:28    [22331645]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
ъъъъъ
Member

Откуда:
Сообщений: 2114
petrav,

я не планирую менять хорошо знакомого уродца, который безответно пашет уже много лет, на слабоизученое (мной) чудовище с непонятными возможностями и перспективой.
И - при чем тут вообще Qt или wxW?
4 июн 21, 21:52    [22331657]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
petrav
Member

Откуда:
Сообщений: 2944
ъъъъъ
petrav,

я не планирую менять хорошо знакомого уродца, который безответно пашет уже много лет, на слабоизученое (мной) чудовище с непонятными возможностями и перспективой.
И - при чем тут вообще Qt или wxW?

Если я Вам мешаю, то скажите и я замолчу.

Просто я глубоко поражён тем, что Вы решаете проблемы, которые я решал 20-ть лет назад в институте. Вот всё сложилось… и невидимое окно, и как из нестатического метода сделать статический, и свой «унифицированный» обработчик Win сообщений, и вот это вот ::KillTimer(), регистрация своего класса окна. Я даже не сразу вспомнил всё это, но когда вспомнил — прямо пазл сложился.

И всё это на фоне wxWidgets. Ну право слово…

Сообщение было отредактировано: 4 июн 21, 21:59
4 июн 21, 22:04    [22331660]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
ъъъъъ
Member

Откуда:
Сообщений: 2114
petrav,

можно конкретнее - что именно не так?
4 июн 21, 22:17    [22331662]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
petrav
Member

Откуда:
Сообщений: 2944
ъъъъъ
petrav,

можно конкретнее - что именно не так?

Всё нормально. Продолжайте работать.
4 июн 21, 22:39    [22331665]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 8256
ъъъъъ
petrav,
я не планирую менять хорошо знакомого уродца, который безответно пашет уже много лет, на слабоизученое (мной) чудовище с непонятными возможностями и перспективой.
И - при чем тут вообще Qt или wxW?
как причем?
Форум публичный. Форум читает много народу.
Зачем им кидатся писать свой менеджер сообщений рукописный?
Только если ТС через слово повторяет - "мопед не мой, проект легаси и ничего менять не могу. Сроки".
А фреймворки и решения приводят не вам а "оглашают" читающим тему.
5 июн 21, 00:05    [22331682]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
rdb_dev
Member

Откуда: с болот
Сообщений: 3654
ъъъъъ, по моему, кто-то где-то перемудрил.
Невиртуальный метод экземпляра класса, это обычная функция, которая принимает неявный параметр this и зачем приседания с виртуальной памятью - совершенно не понятно. Если у тебя есть массив с регистрацией методов окон, которые надо вызывать из обработчика сообщений - нафига весь этот огород?
typedef void (*TWndMethod) (TWndClass*, const TMessage&);
std::map<const TWndClass*, TWndMethod*> mapWndClassMethod;
...
// Регистрация
mapWndClassMethod[ inst ] = &TChildOfWndClass::method( const TMessage& );
...
void Handler( TWndClass * inst )
{
  auto it = mapWndClassMethod.find( inst );
  if ( mapWndClassMethod.cend() == it )
    throw -1;  // integrity violation

  it->second( inst );
}


Сообщение было отредактировано: 7 июн 21, 09:53
7 июн 21, 10:00    [22332103]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 8256
Кстати, ТС не огласил версию дельфи.
Но в 7ой было так что любая форма окно имела возможность подписки на сообщения от Оси из коробки. Одной строкой.
Имхо менеджер событий ГУИ надо опирать на самой ГУИ.
Невизуальные объекты без окон, надо опирать на свои механизмы.
Универсализм - зло.
7 июн 21, 10:29    [22332120]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
kealon(Ruslan)
Member

Откуда: Нижневартовск
Сообщений: 6360
PetroNotC Sharp
Кстати, ТС не огласил версию дельфи.
Но в 7ой было так что любая форма окно имела возможность подписки на сообщения от Оси из коробки. Одной строкой.
Имхо менеджер событий ГУИ надо опирать на самой ГУИ.
тут есть одна проблема: когда писалась VCL, WinAPI как-то совсем несовременный был и так эти косяки и остались для совместимости.
9 июн 21, 21:46    [22333542]     Ответить | Цитировать Сообщить модератору
 Re: Как из нестатического метода объекта сделать статический, или аналог AllocateHWnd()...  [new]
kealon(Ruslan)
Member

Откуда: Нижневартовск
Сообщений: 6360
ъъъъъ
Siemargl
Если конечно, класс меняет состояния и надо их хранить между вызовами, тогда придется temp создавать в глобальной переменной (например списке), как в оригинале.

Я ссылку в окне (GWL_USERDATA) храню, как выше написал. А оконную процедуру не подменяю, из неё вызываю обычный виртуальный метод класса (сохраненного в окне), вот и всё.

Всем спасибо.
Это работает с базовыми классами окон, но надо помнить, что в общем виде это неправильно.
По правильному, что бы получить смещение для SetWindowLong, нужно к GWL_USERDATA добавить cbWndExtra из структуры WNDCLASSA используемого вами класса окна. Так как cbWndExtra байт зарезервировано под функцию окна.

Так же и в обратку, если ваша функция окна (lpfnWndProc) использует сколько-то байт из структуры окна, то вы должны при создании класса окна указать в cbWndExtra сколько байт использует ваша функция, что бы никто их не подпортил.
10 июн 21, 08:53    [22333573]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / C++ Ответить