SetWindowLong() and GetWindowLong()

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);