WinAPI Objektorientiert
-
Hat jemand für mich ein günstiges Klassenkonzept für Window und Controllclasses?
Ein Steuerelement könnte ja theoretisch von der Fensterklasse einige Funktionen erben??
Und wie kann ich es bewältigen das jede WindowFrame Instanz eine eigene WndProc bekommt, sodass ich für jedes "Hauptfenster" die Nachrichten verarbeiten kann?
-
Bei CreateWindow() kannst Du einen Parameter "lParam" uebergeben, den Du aus WM_CREATE wieder abfragen kannst. Dann nimmst Du SetWindowLongPtr() mit GWL_USERDATA und speicherst den Pointer dorthin; bei anderen Messages mit GetWindowLongPtr() und GWL_USERDATA wieder auslesen. So uebergist Du den this-Pointer.
Die Fensterprozedur kann ruhig immer dieselbe sein, sie ruft dann den Code einer pure virtual Class auf, von der alle anderen Fensterklassen abgeleitet sind. Dann ist es Kaese-einfach.
-
@ Power Off: hat dann jede klasse ihre eigene callback?
wie dem auch sei, "damals" als ich noch aktiver codete schrieb ich folgendes (es ist bissl komplizierter, aber wenn esso einfach wie bei dir wäre, power off, hätte ich es damals ja so gemacht). Ich denke bei meinem zeugs kann man für fenster derselben klasse eigene callbacks haben. aber kA ^^
Also:
CWndUnknown.cpp:/* * Description: creates a CALLBACK-thunk. * For more information see readme.txt */ #include <windows.h> #include "CWndUnknown.h" void CWndProcThunk::Init(WNDPROC WndProc, void* pThis) { thunk.m_mov = 0x042444C7; //C7 44 24 04 thunk.m_this = (DWORD)pThis; thunk.m_jmp = 0xe9; thunk.m_relproc = *((int*)&WndProc) - ((int)this + sizeof(_WndProcThunk)); FlushInstructionCache(GetCurrentProcess(), &thunk, sizeof(thunk)); } CWndUnknown::CWndUnknown() { m_hwnd = 0; } LRESULT CWndUnknown::ProcessWindowMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { return false; } LRESULT CALLBACK CWndUnknown::StartWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if(message == WM_CREATE) { CWndUnknown *pThis = (CWndUnknown*)((LPCREATESTRUCT)lParam)->lpCreateParams; pThis->m_hwnd = hwnd; pThis->m_thunk.Init(WindowProc, pThis); WNDPROC pProc = (WNDPROC)&(pThis->m_thunk.thunk); SetWindowLong(hwnd, GWL_WNDPROC, (LONG)pProc); return pProc(hwnd, message, wParam, lParam); } return DefWindowProc(hwnd, message, wParam, lParam); } LRESULT CALLBACK CWndUnknown::WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { CWndUnknown *pThis = (CWndUnknown*)hwnd; return pThis->ProcessWindowMessage(pThis->m_hwnd, message, wParam, lParam); } void* CWndUnknown::GetUnknownClassPtr() { return this; } WNDPROC CWndUnknown::GetStartWndProc() { return (WNDPROC)&StartWindowProc; }CWndUnknown.h:
#ifndef _CWNDUNKNOWN_H_ #define _CWNDUNKNOWN_H_ #pragma pack(push,1) struct _WndProcThunk { DWORD m_mov; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd) DWORD m_this; BYTE m_jmp; // jmp WndProc DWORD m_relproc; // relative jmp }; #pragma pack(pop) class CWndProcThunk { public: _WndProcThunk thunk; void Init(WNDPROC WndProc, void* pThis); }; class CWndUnknown { public: CWndUnknown(); void* GetUnknownClassPtr(); static WNDPROC GetStartWndProc(); protected: HWND m_hwnd; CWndProcThunk m_thunk; virtual LRESULT ProcessWindowMessage(HWND, UINT, WPARAM, LPARAM); static LRESULT CALLBACK StartWindowProc(HWND, UINT, WPARAM, LPARAM); static LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM); }; #endifeinmaliges Initialisieren der Fensterklasse:
wc.cbSize = sizeof(WNDCLASSEX); wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground= (HBRUSH)GetStockObject(BLACK_BRUSH); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hIcon = 0; wc.hIconSm = 0; wc.hInstance = hInstance; wc.lpfnWndProc = CWndUnknown::GetStartWndProc(); wc.lpszClassName= g_pszUnknownClass; wc.lpszMenuName = 0; wc.style = 0; if(!RegisterClassEx(&wc)) return false;(g_pszUnknownClass musst du irgendwo global definiern, eigentlich könnte man es auch als static in die klasse rein, aber kA)
willst jetzt ein fenster haben das in der Klasse CWndMain ist, sieht diese klasse so aus (ausschnitt):
#ifndef _CMAINWND_H_ #define _CMAINWND_H_ #include "CWndUnknown.h" class CMainWnd : public CWndUnknown { public: CMainWnd(); ~CMainWnd(); private: HINSTANCE m_hInstance; virtual LRESULT ProcessWindowMessage(HWND, UINT, WPARAM, LPARAM); }; #endifso erstellst du das fenster in der Klasse (CMainWnd::Create):
HWND CMainWnd::Create() { m_hInstance = GetModuleHandle(0); CreateWindowEx(0, g_pszUnknownClass, "itel", WS_VISIBLE, 100, 100, 400, 600, 0, 0, m_hInstance, GetUnknownClassPtr()); return m_hwnd; }irgendwas schlummert in meinm kopf dass man m_hwnd nicht speichern darf und auch nicht klassmember sein darf, kA. s. CWndUnknown Klasse ^^.
jo das wars.mfg
-
So tief runter (Thunking) braucht man gar nicht.
Was ich oben gesagt habe, reicht.
(Falls ich ein passendes Codebeispiel finde, poste ich es hier; EDIT: nix! war wohl doch in einer Firma gewesen, wo ich das programmiert habe, sorry!
)
-
Nee, so schwer ist das auch nicht. Ich habe das bei mir in eine Basisklasse "Widget" gepackt, die so aussieht:
// Widget.h class Widget { public: Widget(); virtual ~Widget(); const HWND getHandle(); protected: static LRESULT CALLBACK WindowProc(HWND _Handle, UINT _Message, WPARAM _wParam, LPARAM _lParam); virtual LRESULT DefWindowProc(UINT _Message, WPARAM _wParam, LPARAM _lParam); HWND Handle; WNDPROC PrevWndProc; private: };// Widget.cpp #include "Widget.h" // ----------------------------------------------------------------------------- Widget::Widget() : Handle(0), PrevWndProc(0), Text(""), Parent(0), BackgroundColor(0xFFFFFF), PaintBackground(true) { } Widget::~Widget() { } // ----------------------------------------------------------------------------- const HWND Widget::getHandle() { return reinterpret_cast<HWND>(this->Handle); } // ----------------------------------------------------------------------------- LRESULT Widget::DefWindowProc(UINT _Message, WPARAM _wParam, LPARAM _lParam) { // hier Message-Verarbeitung if (this->PrevWndProc) { return ::CallWindowProc(this->PrevWndProc, this->getHandle(), _Message, _wParam, _lParam); } return ::DefWindowProc(this->getHandle(), _Message, _wParam, _lParam); } // ----------------------------------------------------------------------------- // default windowproc for all objects based on this class LRESULT CALLBACK Widget::WindowProc(HWND _Handle, UINT _Message, WPARAM _wParam, LPARAM _lParam) { if(_Message == WM_CREATE) { // übergebenen this merken ::SetWindowLong(_Handle, GWL_USERDATA, (LONG)((CREATESTRUCT*)_lParam)->lpCreateParams); } Widget* pReceiver = reinterpret_cast<Widget*>(GetWindowLong(_Handle, GWL_USERDATA)); if (!pReceiver) { return ::DefWindowProc(_Handle, _Message, _wParam, _lParam); } return pReceiver->DefWindowProc(_Message, _wParam, _lParam); } // -----------------------------------------------------------------------------In einer weiteren Klasse (hier Window) kann man dann CreateWindow z.B. so aufrufen:
... wc.lpfnWndProc = Widget::WindowProc; ... this->Handle = ::CreateWindowEx(0, "Window", this->Text.c_str(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | (this->Parent ? WS_CHILD : 0), _x, _y, _Width, _Height, (this->Parent ? this->Parent->getHandle() : 0), 0, 0, this);Wichtig dabei ist, dass der this-Zeiger übergeben wird und vorher die WndProc der Fensterklasse auf die static-WndProc von Widget gesetzt wird. Bei Buttons, etc. muss man die halt später mit SetWindowLong(Ptr) ändern, da diese ja bereits eine registierte Fensterklasse haben. Aber das war's dann auch schon.
-
mit dieser lösung verpasst man aber ein paar nachrichten.

-
Weiß jetzt nicht genau, was Du meinst. Bei eigenen Fenstern müsstest Du alle bekommen, bei Buttons, etc., die die bereits eine registrierte Fensterklasse haben, verpasst man die CREATE-Messages, was aber klar sein dürfte, weil man da die WndProc erst ändern kann, wenn man nen Handle auf der Button hat, welches man aber erst bekommt, wenn der Button erzeugt wurde.
Wenn es hierfür noch andere Möglichkeiten gibt, nur her damit.

-
Und wie kann ich es bewältigen das jede WindowFrame Instanz eine eigene WndProc bekommt, sodass ich für jedes "Hauptfenster" die Nachrichten verarbeiten kann?
Das beste Framework das ich jemals gehsen habe:
http://download.nvidia.com/developer/Third_Party/Nature/NatureScene.zipbauste dir noch ne add Methode in die Window-Klasse und fertig

Window.add(new Label("Hallo Welt"));
wenn du willst kann ich mal den ganzen Grafikcode rausschmeißen und es so programmieren, das Window.add(new Label("Hallo Welt")); funktioniert
-
Würd mich freuen, wenn du es machst, aber ich brauche es zu OpeGL programmierung.
Ich wollte eine Anwendung mit Steuerelementen proggen, sodass ich einige OpenGL States Checken und ihre Eigenschaften lernen kann.