It is typical of windows UI code to store a pointer to a C++ object into the GWL_USERDATA
slot of an HWND
, using code like this:
long oldValue = SetWindowLong(hWnd, GWL_USERDATA, (long) this);
And, to get it back to the winproc
:
MyClass* me = (MyClass*) GetWindowLong(hWnd, GWL_USERDATA);
This is not compatible with x64: GWL_USERDATA
is not even defined when building for x64. You are forced to use the GWLP_USERDATA
define, which should be used with the LONG_PTR
-friendly versions as follows:
LONG_PTRoldValue = SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) this);
MyClass* me = (MyClass*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
This is better, but, because of issues in the header files, this can still generate portability warnings in Win32 when building with the /Wp64 switch which is enabled for 3ds Max projects now.
To obtain a clean, warning-free compile of the above code, you should write it as follows:
#if !defined( _WIN64 )
// SetWindowLongPtr() maps to SetWindowLong() in 32 bit land; react accordingly to
// keep the compiler happy, even with /Wp64.
LONG_PTRoldValue = static_cast<LONG_PTR>(::SetWindowLongPtr(hWnd, n, (LONG)((LONG_PTR)(ptr))));
#else
LONG_PTRoldValue = static_cast<LONG_PTR>(::SetWindowLongPtr(hWnd, n, (LONG_PTR)(ptr)));
#endif
MyClass* me = reinterpret_cast<MyClass*>(static_cast<LONG_PTR>(::GetWindowLongPtr(hWnd, n)));
While this works, it is complicated. To simplify there are a number of functions in maxsdk
\include\3dsmaxport.h which aid in portability. Using these functions we can rewrite this as:
MyClass* oldValue = DLSetWindowLongPtr(hWnd, this);
MyClass* me = DLGetWindowLongPtr<MyClass*>(hWnd);
These default to GWLP_USERDATA
, since this is the main reason these are used, but you can also specify it explicitly as:
DLSetWindowLongPtr(hWnd, this, GWLP_USERDATA);
MyClass* me = DLGetWindowLongPtr<MyClass*>(hWnd, GWLP_USERDATA);