Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

SetClassLongPtr broken?

316 views
Skip to first unread message

Pete Gontier

unread,
May 16, 2005, 9:57:39 PM5/16/05
to
Is it just me or is SetClassLongPtr broken? And I don't mean "Hey, how come
my program crashes?" :-) I mean I get a warning that I'm throwing away some
precision, and sure enough it turns out I am, because:

#define SetClassLongPtrW SetClassLongW

This looks like a text editing error. I have not encountered a similar
problem with SetWindowLongPtr in the same project, and I would expect
parallelism if this were deliberate.

In the short term, I blew this problem off and solved it by declaring a
global instance of std::map, but that seems slow and lame. I could probably
also go to the trouble of looking the function up at runtime, but that seems
even more lame.

In the long term, it may not be worth solving. SetClassLongPtr requires an
HWND, which is sort of crazy when you think about it because the time you
most want to use SetClassLongPtr you don't have an HWND yet, which is the
whole point. So I may just stick with my global std::map indefinitely.

John Carson

unread,
May 17, 2005, 12:09:51 AM5/17/05
to
"Pete Gontier" <kokor...@discussions.microsoft.com> wrote in message
news:BEAE9DA3.5C94%kokor...@discussions.microsoft.com

> Is it just me or is SetClassLongPtr broken? And I don't mean "Hey,
> how come my program crashes?" :-) I mean I get a warning that I'm
> throwing away some precision, and sure enough it turns out I am,
> because:
>
> #define SetClassLongPtrW SetClassLongW
>
> This looks like a text editing error. I have not encountered a similar
> problem with SetWindowLongPtr in the same project, and I would expect
> parallelism if this were deliberate.

Well, for Win32 development (as opposed to Win64 development) the definition
of SetWindowLongPtrW is:

#define SetWindowLongPtrW SetWindowLongW

which seems pretty parallel to me. If I run the following code:

LONG_PTR lp = GetWindowLongPtr(hwnd, GWLP_USERDATA);
SetWindowLongPtr(hwnd, GWLP_USERDATA, lp);

lp = GetClassLongPtr(hwnd, GCL_CBWNDEXTRA);
SetClassLongPtr(hwnd, GCL_CBWNDEXTRA, lp);

then I get the following warning for *both* SetWindowLongPtr and
SetClassLongPtr:

warning C4244: 'argument' : conversion from 'LONG_PTR' to 'LONG', possible
loss of data

Basically, if you are developing for Win32, then a pointer is 32 bits long,
which means that LONG_PTR is the same as LONG and that SetWindowLongPtr and
SetWindowLong are the same and SetClassLongPtr and SetClassLong are the
same. This means that *no* conversion is taking place betwen LONG_PTR and
LONG. Moreover, the code as written above will work perfectly on Win64 since
in that case LONG_PTR will be 64 bits long and SetWindowLongPtr and
SetClassLongPtr will no longer map to SetWindowLong and SetClassLong, but
will instead accept and return 64 bit values.

It is simply a VC++ bug that you keep getting warnings. My solution has been
to turn the warnings off. If and when I target the Win64 platform, I will
turn them back on.

--
John Carson

Pete Gontier

unread,
May 17, 2005, 2:10:20 PM5/17/05
to
On 5/16/05 9:09 PM, in article uhttOZpW...@TK2MSFTNGP09.phx.gbl, "John
Carson" <jcarson_n...@netspace.net.au> wrote:

> ...*no* conversion is taking place betwen LONG_PTR and LONG... It is simply a


> VC++ bug that you keep getting warnings. My solution has been to turn the
> warnings off.

Oh, this is funny. Or pathetic. You choose. I just now found this in my very
own code:

void Window::SetLongPtr (HWND window, int nIndex, LONG_PTR value)
{
// The call to SetWindowLongPtr here may generate a warning when
// the compiler has been instructed to inform us of 64-bit
// portability issues.
//
// warning C4244: 'argument' :
// conversion from 'LONG_PTR' to 'LONG', possible loss of data
//
// It is safe to ignore this warning because it is bogus per MSDN
Magazine.
//
// http://msdn.microsoft.com/msdnmag/issues/01/08/bugslayer/

MAW32_Assert (IsWindow (window));
SetLastError (ERROR_SUCCESS);
#pragma warning (push)
#pragma warning (disable : 4244) // inhibit ignorable
if (SetWindowLongPtr (window,nIndex,value)) return;
#pragma warning (pop)
DWORD lastError = GetLastError ( );
if (lastError) throw LastErrorException (lastError);
}

So apparently when I encountered this warning for SetWindowLongPtr I was
smart enough to Google for insight but when I encountered the very same
warning for SetClassLongPtr I had become stupider! :-)

John Carson

unread,
May 18, 2005, 3:54:59 AM5/18/05
to
"Pete Gontier" <kokor...@discussions.microsoft.com> wrote in message
news:BEAF819C.5D60%kokor...@discussions.microsoft.com


Well, it has been annoying me for years and I never thought to Google. I
have now discussed this on the MVP private newsgroups and there may be a
workaround if you think it sufficiently important to bother.

The problem appears to be that SetWindowLongPtr (and its relatives) are
simply macros, whereas LONG_PTR is a typedef. This means that the
preprocessor replaces SetWindowLongPtr with SetWindowLong while leaving the
LONG_PTR typedef intact. Thus the compiler sees SetWindowLong being used
with a LONG_PTR and this produces the warning. The other side of this is
that the compiler sometimes doesn't produce a warning when it should, e.g.,
the following code should generate a warning but doesn't:

LONG lng = GetWindowLongPtr(hwnd, GWLP_HINSTANCE);

My first thought was that a workaround for the problem may be to use a
function pointer instead of a macro. Carl Daniel, however, proposed the use
of an inline function. This is both more efficient and works in a wider
class of scenarios.

As presently implemented, the idea is that you #include windows.h as per
normal and then repair the damage afterward by #including the following
header file. The most efficient way of organising the inclusion is up to
you. It seems to work, but no promises.

------------------------------------------------------------------

// Header file: fix64bit_port.h

#ifndef fix64bit_port_h
#define fix64bit_port_h

#ifndef _WIN64

#undef GetWindowLongPtrA
#undef GetWindowLongPtrW
#undef GetClassLongPtrA
#undef GetClassLongPtrW

#undef SetWindowLongPtrA
#undef SetWindowLongPtrW
#undef SetClassLongPtrA
#undef SetClassLongPtrW

__forceinline LONG_PTR GetWindowLongPtrA(HWND h, int i)
{ return (LONG_PTR)GetWindowLongA(h,i); }
__forceinline LONG_PTR GetWindowLongPtrW(HWND h, int i)
{ return (LONG_PTR)GetWindowLongW(h,i); }
__forceinline ULONG_PTR GetClassLongPtrA(HWND h, int i)
{ return (ULONG_PTR)GetClassLongA(h,i); }
__forceinline ULONG_PTR GetClassLongPtrW(HWND h, int i)
{ return (ULONG_PTR)GetClassLongW(h,i); }

#pragma warning(push)
#pragma warning( disable : 4244)
__forceinline LONG_PTR SetWindowLongPtrA(HWND h, int i, LONG_PTR l)
{ return (LONG_PTR)SetWindowLongA(h,i,l); }
__forceinline LONG_PTR SetWindowLongPtrW(HWND h, int i, LONG_PTR l)
{ return (LONG_PTR)SetWindowLongW(h,i,l); }
__forceinline ULONG_PTR SetClassLongPtrA(HWND h, int i, LONG_PTR l)
{ return (ULONG_PTR)SetClassLongA(h,i,l); }
__forceinline ULONG_PTR SetClassLongPtrW(HWND h, int i, LONG_PTR l)
{ return (ULONG_PTR)SetClassLongW(h,i,l); }
#pragma warning(pop)

#endif // _WIN64

#endif // include guard
--------------------------------------------------------------


--
John Carson

Pete Gontier

unread,
May 18, 2005, 4:50:07 PM5/18/05
to
On 5/18/05 12:54 AM, in article #amAr73W...@TK2MSFTNGP15.phx.gbl, "John
Carson" <jcarson_n...@netspace.net.au> wrote:

> there may be a workaround if you think it sufficiently important to bother

Looks like good work. Another idea I had would be to write a function
MySetClassLongPtr which contains a static reference to SetClassLongPtr
looked up once via some DLL API or other. I haven't looked into that, but I
suspect it would have the advantage of being more resistant to changes
(fixes, one would hope) in <Windows.h> and the disadvantage of being more
code.

I eventually discovered SetClassLongPtr isn't terribly useful in my case
because I need to call it *before* I have an HWND. I could create an HWND
for the exclusive purpose of calling SetClassLongPtr, then destroy the
window, but I decided instead to use a global instantiation of std::map to
solve the same problem. I'm left wondering what on earth the designers of
SetClassLongPtr must have been thinking. :-)

John Carson

unread,
May 18, 2005, 10:03:13 PM5/18/05
to
"Pete Gontier" <kokor...@discussions.microsoft.com> wrote in message
news:BEB0F88F.5F61%kokor...@discussions.microsoft.com

>
> I eventually discovered SetClassLongPtr isn't terribly useful in my
> case because I need to call it *before* I have an HWND. I could
> create an HWND for the exclusive purpose of calling SetClassLongPtr,
> then destroy the window, but I decided instead to use a global
> instantiation of std::map to solve the same problem. I'm left
> wondering what on earth the designers of SetClassLongPtr must have
> been thinking. :-)

I very rarely use it, but use Set/GetWindowLongPtr all the time.

I presume that you want (or wanted) to modify the properties of a standard
Windows control. Creating and destroying a window is inelegant but very
quick (naturally you don't make the window visible).

If you want elegance, then you can always superclass the control (not to be
confused with subclassing it). See here (the heading is "Window Procedure
Superclassing", but you can change any aspect of the WNDCLASS structure):

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windowprocedures/aboutwindowprocedures.asp

If superclassing a global class like an Edit control, make sure that you
remove CS_GLOBALCLASS from the style field.

--
John Carson

Pete Gontier

unread,
May 19, 2005, 2:20:02 AM5/19/05
to
On 5/18/05 7:03 PM, in article OaQ#6bBXFH...@TK2MSFTNGP14.phx.gbl, "John
Carson" <jcarson_n...@netspace.net.au> wrote:

> I presume that you want (or wanted) to modify the properties of a standard
> Windows control.

Nothing so exciting.

I just wanted to associate a C++ object with my own window class(es).

0 new messages