Ja, mit ein wenig Vorsicht haut das bei einfachen Fenstern wohl hin, da Du mit Sicherheit weißt das GWL_USERDATA unbenutzt ist. Und für eine Engine braucht man ja meist nicht mehr :) Ich benutze meinen Hook Code, weil er mich noch nie im Stich gelassen hat.dot hat geschrieben:Schon klar, genau das Herstellen des Mapping von HWND auf Objekt ist imo aber das eigentliche Problem und genau das lässt sich durch SetWindowLongPtr() z.B. relativ elegant lösen, da man damit einfach seinen Pointer direkt im Window Objekt des Betriebssystems ablegen kann... ;)
Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
Forumsregeln
Möglichst sinnvolle Präfixe oder die Themensymbole nutzen.
Möglichst sinnvolle Präfixe oder die Themensymbole nutzen.
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
- dot
- Establishment
- Beiträge: 1752
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
The Horror...
:shock:
Macht natürlich absolut Sinn, auch wieso man keinen direkten Zugriff auf das Window Extra Memory bekommt, hab nur nie wirklich drüber nachgedacht. Also doch Thunks...
:shock:
Macht natürlich absolut Sinn, auch wieso man keinen direkten Zugriff auf das Window Extra Memory bekommt, hab nur nie wirklich drüber nachgedacht. Also doch Thunks...
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
Sieht so aus als gäb's wieder was neues für mich zu lernen :D Was ist ein Thunk und warum ist der so schlecht?
- dot
- Establishment
- Beiträge: 1752
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
Nein, Thunks sind damit seit heute 21:40 herum die effizienteste noch irgendwie praktikable Lösung für das Problem, die ich kenne. Die Idee ist ganz einfach: Man generiert dynamisch beim Erzeugen seines Objektes ein Stück Maschinencode, das den Aufruf des WndProc Callback direkt vor Ort in einen Aufruf der Methode des jeweiligen Objektes umwandelt und verwendet als WndProc einen Pointer auf selbiges. Wie du dir denken kannst, ist die Umsetzung davon aber nicht ganz unproblematisch...
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
Dynamischer Maschinencode? Du meinst so mit VirtualAlloc und Zeugs für executable pages? Das hört sich schon echt heftig an.
EDIT: Das erinnert mich an selbstmodifizierenden Code zu Amiga und DOS Zeiten :D
EDIT: Das erinnert mich an selbstmodifizierenden Code zu Amiga und DOS Zeiten :D
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
Eine hervorragende Zusammenfassung findet sich hier.
Die dort gezeigte Implementierung entspricht auch so ziemlich der Referenzimplementierung in der VC/atlmfc/include/atlstdthunk.h (mit Ausnahme des grandiosen union_casts); kein Wunder, in den Referenzen taucht die ATL auf, d.h. der gezeigte Code ist von dortgeklaut inspiriert.
Aber wichtig: In der atlstdthunk.h gibt es auch eine x64-Variante. Und: Mit diesem Patch hat man auch den Code für operator new und delete des Thunk-Objekts (welches ja mit auf einer Page mit PAGE_EXECUTE_READWRITE liegen muss). Damit hat man alles zusammen was man für die Thunks braucht. Außer die Zeit, um das jetzt auch noch zusammenzukopieren schreiben.
Die dort gezeigte Implementierung entspricht auch so ziemlich der Referenzimplementierung in der VC/atlmfc/include/atlstdthunk.h (mit Ausnahme des grandiosen union_casts); kein Wunder, in den Referenzen taucht die ATL auf, d.h. der gezeigte Code ist von dort
Aber wichtig: In der atlstdthunk.h gibt es auch eine x64-Variante. Und: Mit diesem Patch hat man auch den Code für operator new und delete des Thunk-Objekts (welches ja mit auf einer Page mit PAGE_EXECUTE_READWRITE liegen muss). Damit hat man alles zusammen was man für die Thunks braucht. Außer die Zeit, um das jetzt auch noch zu
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
Ich habe dann mal Thunks implementiert.
Erst einmal den vollständigen, kompilierbaren Code, welcher bei der Fenstererstellung noch das Hello-World-Beispiel aus der MSDN benutzt, um das es hier aber ja schließlich nicht geht:
Funktionsweise:
Abschließend noch zur Performance unter x64 im Release-Modus:
Im Übrigen funktioniert sämtlicher sonstiger Thunking-Code aus dem Internet wegen DEP nicht mehr. Dieser Post ist das einzige unter x64 funktionierende Thunking-Beispiel, welches mir bekannt ist.
Ich habe mal getestet, was passiert, wenn man direkt die Instanzmethode anspringen würde (was nur unter x64 aufgrund der Aufrufkonvention geht): Der Compiler erstellt korrekterweise einen Eintrag in die Relocation-Table, und ist damit genauso effizient wie unsere obige Implementierung; nur dass die obige auch unter x86 funktioniert, da der Compiler dort mehr Zwischencode als Kleber zwischen den Aufrufkonventionen generiert.

Der einzige Overhead in obiger Fassung ist ein einziger jmp, wie bei einer Relokationstabelle. Womit der Code wohl ziemlich optimal ist.
Erst einmal den vollständigen, kompilierbaren Code, welcher bei der Fenstererstellung noch das Hello-World-Beispiel aus der MSDN benutzt, um das es hier aber ja schließlich nicht geht:
Code: Alles auswählen
// Suppress warnings from Windows and STL headers
#pragma warning(disable : 4668 4710 4711 4820 4986)
#include <Windows.h>
#include <cstdint>
#include <string>
#include <iostream>
#include <type_traits>
// Helpers
namespace memory
{
template <typename Type>
void * AllocateReadWriteVirtualMemory()
{
return ::VirtualAlloc(0, sizeof(Type), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
}
template <typename Type>
Type * MakeMemoryExecutable(
Type * thePointer
) {
unsigned long oldProtection;
VirtualProtect(thePointer, sizeof(Type), PAGE_EXECUTE, &oldProtection);
return thePointer;
}
void DeallocateVirtualMemory(
void * thePointer
) {
::VirtualFree(thePointer, 0, MEM_RELEASE);
}
}
namespace meta
{
namespace addressOf
{
template<class Type>
struct DisableConversionOperators
{
Type & myValue;
inline DisableConversionOperators(
Type & theValue
) : myValue(theValue)
{
return;
}
inline operator Type&() const
{
return myValue;
}
private:
DisableConversionOperators & operator=(
DisableConversionOperators const &
);
};
template<class Type>
struct ReturnFunctionAddress
{
static inline Type * AddressOf(
Type & theValue,
long
) {
return reinterpret_cast<Type*>(&const_cast<char &>(reinterpret_cast<const volatile char &>(theValue)));
}
static inline Type * AddressOf(
Type * theValue,
int
) {
return theValue;
}
};
template<class Type>
Type * AddressOf(
Type & theValue
) {
return ReturnFunctionAddress<Type>::AddressOf(DisableConversionOperators<Type>(theValue), 0);
}
}
using namespace addressOf;
namespace unionCast
{
template<typename DestinationType, typename SourceType>
inline DestinationType UnionCast(SourceType theSource)
{
static_assert(sizeof(DestinationType) == sizeof(SourceType), "UnionCast size mismatch.");
union
{
SourceType mySource;
DestinationType myDestination;
} unionForCast;
unionForCast.mySource = theSource;
return unionForCast.myDestination;
}
}
using namespace unionCast;
}
// Windows
namespace windows
{
typedef ::LRESULT (__stdcall * WindowProcedureType)(::HWND, unsigned int, ::WPARAM, ::LPARAM);
// Thunks
namespace thunks
{
namespace
{
template <typename SourceType>
void FillArray(
char * theArray,
size_t & theOffset,
SourceType theSource
) {
SourceType * destination = reinterpret_cast<SourceType *>(theArray + theOffset);
*destination = theSource;
theOffset += sizeof(SourceType);
}
}
#ifdef _M_IX86
template <typename WindowClassType>
class ThunkImplementation
{
public:
explicit ThunkImplementation(
WindowClassType & theWindowClass,
WindowProcedureType theWindowProcedure
) {
size_t offset = 0u;
// mov dword ptr [esp+4], <pointer to theWindowClass>
FillArray<uint32_t>(myCode, offset, 0x042444c7);
FillArray(myCode, offset, meta::AddressOf(theWindowClass));
// jmp <relative address to theWindowProcedure>
FillArray<uint8_t>(myCode, offset, 0xe9);
FillArray(myCode, offset, meta::UnionCast<char *>(theWindowProcedure) - reinterpret_cast<char *>(this) - sizeof(*this));
::FlushInstructionCache(GetCurrentProcess(), this, sizeof(*this));
}
operator WindowProcedureType() const
{
return meta::UnionCast<WindowProcedureType>(myCode);
}
void * operator new(
size_t
) {
return memory::AllocateReadWriteVirtualMemory<ThunkImplementation>();
}
void operator delete(
void * thePointer
) {
memory::DeallocateVirtualMemory(thePointer);
}
private:
typedef char ThunkCode[sizeof(uint32_t) + sizeof(WindowClassType *) + sizeof(uint8_t) + sizeof(ptrdiff_t)];
ThunkCode myCode;
};
#elif defined _M_AMD64
template <typename WindowClassType>
class ThunkImplementation
{
public:
explicit ThunkImplementation(
WindowClassType & theWindowClass,
WindowProcedureType theWindowProcedure
) {
size_t offset = 0u;
// mov rcx, <pointer to theWindowClass>
FillArray<uint16_t>(myCode, offset, 0xb948);
FillArray(myCode, offset, meta::AddressOf(theWindowClass));
// mov rax, <pointer to theWindowProcedure>
FillArray<uint16_t>(myCode, offset, 0xb848);
FillArray(myCode, offset, meta::UnionCast<char *>(theWindowProcedure));
// jmp rax
FillArray<uint16_t>(myCode, offset, 0xe0ff);
::FlushInstructionCache(GetCurrentProcess(), this, sizeof(*this));
}
operator WindowProcedureType() const
{
return meta::UnionCast<WindowProcedureType>(myCode);
}
void * operator new(
size_t
) {
return memory::AllocateReadWriteVirtualMemory<ThunkImplementation>();
}
void operator delete(
void * thePointer
) {
memory::DeallocateVirtualMemory(thePointer);
}
private:
typedef char ThunkCode[sizeof(uint16_t) + sizeof(WindowClassType *) + sizeof(uint16_t) + sizeof(ptrdiff_t) + sizeof(uint16_t)];
ThunkCode myCode;
};
#else
#error Unsupported architecture.
#endif
template <typename WindowClassType>
class Thunk
{
public:
Thunk(
WindowClassType & theWindowClass,
WindowProcedureType theWindowProcedure
) : myThunk(memory::MakeMemoryExecutable(new ThunkImplementation<WindowClassType>(theWindowClass, theWindowProcedure)))
{
return;
}
~Thunk()
{
delete myThunk;
}
operator WindowProcedureType() const
{
return *myThunk;
}
private:
thunks::ThunkImplementation<WindowClassType> * myThunk;
};
}
namespace
{
void SetWindowProcedure(
::HWND theWindowhandle,
WindowProcedureType theWindowProcedure
) {
::SetWindowLongPtrW(theWindowhandle, GWLP_WNDPROC, reinterpret_cast<std::make_signed<size_t>::type>(theWindowProcedure));
}
}
template <typename DerivedWindowProcedure>
class ThunkedWindowProcedure
{
public:
explicit ThunkedWindowProcedure(
::HWND theWindowHandle,
DerivedWindowProcedure * theThisPointer
) : myThunk(*theThisPointer, DefaultWindowProcedure),
myWindowHandle(theWindowHandle)
{
SetWindowProcedure(theWindowHandle, static_cast<WindowProcedureType>(myThunk));
}
void SetWindowHandle(
::HWND theWindowHandle
) {
myWindowHandle = theWindowHandle;
}
void GetWindowHandle()
{
return myWindowHandle;
}
protected:
::HWND myWindowHandle;
thunks::Thunk<DerivedWindowProcedure> myThunk;
private:
static ::LRESULT __stdcall DefaultWindowProcedure(
::HWND theWindowHandle,
unsigned int theMessage,
::WPARAM theAdditionalMessageInformation1,
::LPARAM theAdditionalMessageInformation2
) {
DerivedWindowProcedure * thisPointer = reinterpret_cast<DerivedWindowProcedure * >(theWindowHandle);
return thisPointer->WindowProcedure
(
theMessage,
theAdditionalMessageInformation1,
theAdditionalMessageInformation2
);
}
};
class ExampleWindowProcedure : public ThunkedWindowProcedure<ExampleWindowProcedure>
{
public:
typedef std::basic_string<wchar_t> WideString;
// This is legal code, see N3337 12.6.2 Clause 12
#pragma warning(push)
#pragma warning(disable : 4355)
explicit ExampleWindowProcedure(
::HWND theWindowHandle,
WideString theString
) : ThunkedWindowProcedure(theWindowHandle, this),
myString(theString)
{
return;
}
#pragma warning(pop)
public:
::LRESULT WindowProcedure(
unsigned int theMessage,
::WPARAM theAdditionalMessageInformation1,
::LPARAM theAdditionalMessageInformation2
) {
if(theMessage == WM_DESTROY)
{
::PostQuitMessage(0);
}
else if(theMessage == WM_PAINT)
{
PAINTSTRUCT paintInformation;
auto displayDeviceContext = ::BeginPaint(myWindowHandle, &paintInformation);
TextOut(displayDeviceContext, 5, 5, myString.c_str(), static_cast<int>(myString.length()));
::EndPaint(myWindowHandle, &paintInformation);
}
else
{
return DefWindowProc
(
myWindowHandle,
theMessage,
theAdditionalMessageInformation1,
theAdditionalMessageInformation2
);
}
return 0;
}
private:
WideString myString;
};
}
// The following code is taken from the "Hello, World!" sample
// http://msdn.microsoft.com/en-us/library/vstudio/bb384843
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
// Global variables
// The main window class name.
static TCHAR szWindowClass[] = _T("win32app");
// The string that appears in the application's title bar.
static TCHAR szTitle[] = _T("Win32 Guided Tour Application");
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = ::DefWindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClassEx(&wcex))
{
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}
// The parameters to CreateWindow explained:
// szWindowClass: the name of the application
// szTitle: the text that appears in the title bar
// WS_OVERLAPPEDWINDOW: the type of window to create
// CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y)
// 500, 100: initial size (width, length)
// NULL: the parent of this window
// NULL: this application does not have a menu bar
// hInstance: the first parameter from WinMain
// NULL: not used in this application
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 100,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}
windows::ExampleWindowProcedure window(hWnd, L"Hello, World!");
// The parameters to ShowWindow explained:
// hWnd: the value returned from CreateWindow
// nCmdShow: the fourth parameter from WinMain
ShowWindow(hWnd,
nCmdShow);
UpdateWindow(hWnd);
// Main message loop:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}- Die Fensterklasse wcex kriegt zunächst einmal die ::DefWindowProc als Fensterprozedur:
- Wir werden die später umsetzen
- Das bedeutet aber auch, dass wir hier ein paar Nachrichten, wie etwa WM_NCCREATE, gar nicht mitkriegen, weil die an ::DefWindowProc gehen
- In der jetztigen Implementierung gibt es nichts, was diese Nachrichten bräuchte; wenn ihr die jedoch unbedingt selbst behandeln müsst, gibt es keinen Grund, bereits bei Erstellung von wcex schon eine eigene Fensterprozedur anzugeben
- Nach einem gewöhnlichen Aufruf an ::CreateWindow erstellen wir eine Instanz von Typ ExampleWindowProcedure, welche unsere Beispiels-Fensterprozedur halten wird:
- Die Klasse kriegt im Konstruktor das Fenster-Handle vom zuvor erstellten Fenster (HWND) und einen String, hier „Hello, World!“, übergeben
- Diese Fensterprozedur macht jetzt erstmal nichts anderes, als diesen Member-String ins Fenster zu malen – zumindest ein kleines Beispiel für die Verwendung den this-Zeigers
- Unsere ExampleWindowProcedure ist von ThunkedWindowProcedure<ExampleWindowProcedure> abgeleitet, einer generischen Basisklasse für gethunkte Fensterprozeduren (hier wird ein CRTP benutzt)
- ThunkedWindowProcedure<DerivedWindowProcedure> enthält ein Thunk-Objekt vom Typ thunks::Thunk<DerivedWindowProcedure> (Template-Parameter hat den Typ der abgeleiteten Klasse) namens myThunk
- Dieses Thunk-Objekt wird eine Thunk-Implementierung im Speicher anlegen
- Dieses Thunk-Objekt myThunk wird erstellt und nimmt im Konstruktor zwei Argumente entgegen:
- Einen this-Pointer auf die abgeleitete Klasse
- Einen Pointer auf die Fensterprozedur als Member-Funktion der abgeleiteten Klasse
- Der Thunk-Konstruktor für den Typ Thunk erstellt mit diesen zwei Parametern eine Thunk-Implementierung, welche abhängig von der Architektur ist und die ganze Low-Level-Arbeit macht:
- Die Thunk-Implementierung myThunk (ja, ich weiß, gleicher Name wie oben, das ist jetzt doof) ist ein Pointer vom Typ ThunkImplementation<WindowClassType>, welcher dynamisch allokiert und freigegeben wird
- Bei dieser Allokation und Freigabe werden operator new/operator delete der ThunkImplementation aufgerufen
- Diese setzen mittels AllocateReadWriteVirtualMemory die Thunk-Implementierung an den Anfang einer Page, welche lesbar und beschreibbar, aber nicht ausführbar ist.
- Die Thunk-Implementierung hat ein char-Array, und dieses wird mit ein wenig Daten als Code gefüllt:
- Für x86: sizeof(uint32_t) + sizeof(WindowClassType *) + sizeof(uint8_t) + sizeof(ptrdiff_t)
mov dword ptr [esp+4], <pointer to theWindowClass>
jmp <relative address to theWindowProcedure> - Für x64: sizeof(uint16_t) + sizeof(WindowClassType *) + sizeof(uint16_t) + sizeof(ptrdiff_t) + sizeof(uint16_t)
mov rcx, <pointer to theWindowClass>
mov rax, <pointer to theWindowProcedure>
jmp rax - Was der Code genau macht, dazu komme ich später
- Für x86: sizeof(uint32_t) + sizeof(WindowClassType *) + sizeof(uint8_t) + sizeof(ptrdiff_t)
- Anschließend wird die Page auf nicht mehr lesbar oder schreibbar gesetzt, sondern nur noch auf ausführbar (MakeMemoryExecutable)
- Nun setzen wir endlich die Fensterprozedur unseres Fensters mittels ::SetWindowLongPtrW mit Parameter GWLP_WNDPROC um, und zwar auf die Speicheradresse, an der unser char-Array der ThunkImplementation-Instanz liegt
- Damit wird der obige Code, den wir als Daten in unser char-Array gesteckt haben, ausgeführt
- Das einzige, was noch fehlt, ist zu erklären, was der Code nun macht:
- Für x86:
- Metavariablen:
- <pointer to theWindowClass> ist ein this-Pointer auf unsere ExampleWindowProcedure-Instanz
- <relative address to theWindowProcedure> ist eine relative Adresse auf eine statische Methode mit __stdcall-Konvention namens DefaultWindowProcedure in der Elternklasse von ExampleWindowProcedure
- Da wir __stdcall-Konvention haben, und da alles über den Stack läuft, überschreiben wir den ersten Parameter an DefaultWindowProcedure (das ist ::HWND theWindowHandle) mit dem this-Pointer
- Dann machen wir einen jmp auf DefaultWindowProcedure
- Dort frickeln wir uns mittels DerivedWindowProcedure * thisPointer = reinterpret_cast<DerivedWindowProcedure * >(theWindowHandle); wieder unseren this-Pointer zurück
- Und rufen dann mittels thisPointer->WindowProcedure(…) die Fensterprozedur endlich auf
- Metavariablen:
- Für x64:
- Eigentlich so wie x86, nur dass __stdcall vom Kompiler ignoriert wird, und x64-Konvention benutzt wird.
- Dadurch geht erstmal nichts über den Stack, sondern über die Register; hierbei steckt in rcx der erste Parameter an DefaultWindowProcedure
- Wir überschreiben dieses Register mit dem this-Pointer, und der Rest funktioniert wie unter x86
- Für x86:
Abschließend noch zur Performance unter x64 im Release-Modus:
- Windows ruft unsere benutzerdefiniert gesetzte Fensterprozedur in user32.dll!UserCallWinProcCheckWow auf:
- In unserem Beispiel hat die CPU 00000000024A0000 in r14 geladen
- user32.dll!UserCallWinProcCheckWow() + 0x13b bytes:
000007FEC980171B 41 FF D6 call r14
- Dies ruft den Code in unserer Thunk-Implementierung auf:
00000000024A0000 48 B9 68 F9 A7 00 00 00 00 00 mov rcx,0A7F968h
00000000024A000A 48 B8 20 15 DC 13 F6 07 00 00 mov rax,7F613DC1520h
00000000024A0014 FF E0 jmp rax - An der in rax geladenen Speicheradresse 000007F613DC1520 steht unsere DefaultWindowProcedure-Funktion, welche der Optimizer auf einen einzigen unkonditionalen jmp optimiert hat:
static ::LRESULT __stdcall DefaultWindowProcedure(
::HWND theWindowHandle,
unsigned int theMessage,
::WPARAM theAdditionalMessageInformation1,
::LPARAM theAdditionalMessageInformation2
) {
DerivedWindowProcedure * thisPointer = reinterpret_cast<DerivedWindowProcedure * >(theWindowHandle);
return thisPointer->WindowProcedure
(
theMessage,
theAdditionalMessageInformation1,
theAdditionalMessageInformation2
);
}
000007F613DC1520 E9 FB FB FF FF jmp ExampleWindowProcedure::WindowProcedure (7F613DC1120h) - Und der jmp geht dann an unsere Fensterprozedur in unserer Instanz:
::LRESULT WindowProcedure(
unsigned int theMessage,
::WPARAM theAdditionalMessageInformation1,
::LPARAM theAdditionalMessageInformation2
) {
000007F613DC1120 40 53 push rbx
000007F613DC1122 48 81 EC 90 00 00 00 sub rsp,90h
000007F613DC1129 48 8B 05 F0 3E 00 00 mov rax,qword ptr [__security_cookie (7F613DC5020h)]
000007F613DC1130 48 33 C4 xor rax,rsp
000007F613DC1133 48 89 84 24 80 00 00 00 mov qword ptr [rsp+80h],rax
000007F613DC113B 48 8B D9 mov rbx,rcx
if(theMessage == WM_DESTROY)
000007F613DC113E 83 FA 02 cmp edx,2
000007F613DC1141 75 0C jne ExampleWindowProcedure::WindowProcedure+2Fh (7F613DC114Fh)
{
…
Im Übrigen funktioniert sämtlicher sonstiger Thunking-Code aus dem Internet wegen DEP nicht mehr. Dieser Post ist das einzige unter x64 funktionierende Thunking-Beispiel, welches mir bekannt ist.
Ich habe mal getestet, was passiert, wenn man direkt die Instanzmethode anspringen würde (was nur unter x64 aufgrund der Aufrufkonvention geht): Der Compiler erstellt korrekterweise einen Eintrag in die Relocation-Table, und ist damit genauso effizient wie unsere obige Implementierung; nur dass die obige auch unter x86 funktioniert, da der Compiler dort mehr Zwischencode als Kleber zwischen den Aufrufkonventionen generiert.
Der einzige Overhead in obiger Fassung ist ein einziger jmp, wie bei einer Relokationstabelle. Womit der Code wohl ziemlich optimal ist.
- Aramis
- Moderator
- Beiträge: 1458
- Registriert: 25.02.2009, 19:50
- Echter Name: Alexander Gessler
- Wohnort: 2016
- Kontaktdaten:
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
Der Thread war mir voellig entgangen - wir sollten das Original-Tutorial entsprechend editieren oder alternativ ganz verschwinden lassen. Wenn ich mir das so durchlese, sehe ich hauptsaechlich mein juengeres selbst vor mir :-)
- Krishty
- Establishment
- Beiträge: 8413
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
Microsoft hat ein eigenes deutschsprachiges Tutorial geschrieben, das die WinAPI erklärt. Dabei ist das WndProc()-in-Klasse-Problem als Verwalten des Anwendungsservers eingetragen. Ich würde sagen, dass es unseren Thread um Längen schlägt:
http://msdn.microsoft.com/de-de/library ... p/ff381400
http://msdn.microsoft.com/de-de/library ... p/ff381400
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
Naja, die dort haben aber auch bei jeder Nachricht das Problem, dass GetWindowLongPtr laut dot ja einmal in den Kernel abtaucht. (Übrigens ist der englische Titel „Managing Application State“; nur, falls der deutsche Titel irgendeinen hier verwirrt haben sollte. ;))Krishty hat geschrieben:Ich würde sagen, dass es unseren Thread um Längen schlägt
- Krishty
- Establishment
- Beiträge: 8413
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
Ich bin kein Fan von Thunks. Sie haben schon einmal Kompatibilitätsprobleme bei einer ganzen Generation von Programmen verursacht, und werden es bestimmt wieder tun. Ich persönlich bleibe deshalb beim „offiziell empfohlenen“ Weg.
Und ja; der deutsche Titel ist … exotisch.
Und ja; der deutsche Titel ist … exotisch.
- Krishty
- Establishment
- Beiträge: 8413
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Was ist mit unserem WNDPROC-in-Klasse-Tutorial?
GWL_USERDATA ist bei Dialogen unbrauchbar, weil wir nicht Besitzer der Fensterklasse sind. Für diesen Fall scheint es aber DWL_USER zu geben. Falls also jemand aus Fenster in Klasse wrappen ein Dialog in Klasse wrappen machen will, ist das die Lösung (aber in WM_INITDIALOG statt WM_NCCREATE).
Raymond Chen schreibt zwar im StackOverflow-Thread Win32 API check if current window is dialog or normal window, dass DWL_USER jemand anders gehöre; aber ich glaube, dass sich das nur auf das Subclassing bezieht (wo man stattdessen GetProp() / SetProp() verwenden sollte), zumal er DWL_USER in einigen seiner Artikel selber benutzt (z.B. in A different type of dialog procedure oder The dialog manager, part 5: Converting a non-modal dialog box to modal).
Raymond Chen schreibt zwar im StackOverflow-Thread Win32 API check if current window is dialog or normal window, dass DWL_USER jemand anders gehöre; aber ich glaube, dass sich das nur auf das Subclassing bezieht (wo man stattdessen GetProp() / SetProp() verwenden sollte), zumal er DWL_USER in einigen seiner Artikel selber benutzt (z.B. in A different type of dialog procedure oder The dialog manager, part 5: Converting a non-modal dialog box to modal).