tags:

views:

51

answers:

5

This is the code I have:

HWND WebformCreate(HWND hParent, UINT id)
{
    return CreateWindowEx(0, WEBFORM_CLASS, _T("about:blank"),
        WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 0, 0, 100, 100, hParent,
        (HMENU)id, GetModuleHandle(NULL), 0);
}

This is the warning I get:

warning C4312: 'type cast' : conversion from 'UINT' to 'HMENU' of greater size

These are the questions I have:

  1. Why does the compiler think it's a bad idea to cast to a bigger type?
  2. What's the best way to get rid of the warning? (I don't want to disable it.)
  3. Doing a double type cast like this: (HMENU)(UINT_PTR)id gets rid of the warning. Why/how?
  4. Disabling "Detect 64-bit Portability Issues" (Wp64) also gets rid of the warning. Why is Wp64 deprecated? Should I have it on?
A: 

google says see

http://msdn.microsoft.com/en-us/library/h97f4b9y%28VS.80%29.aspx

pm100
That link basically says it's bad because negative numbers might screw stuff up. But since I'm using a UINT I don't see how that applies. Can you please explain?
Tobbe
+1  A: 

You're casting a 32bit UINT to a 64bit pointer. That's suicide - you're trying to point to something but forgot half it's location! You absolutely MUST take a UINT_PTR. When you cast a pointer to an int, the behaviour is only OK if the int has the same size as the pointer. Else, it's the end of your application's runtime courtesy of an access violation.

Edit:
Why does the compiler think it's a bad idea to cast to a bigger type?
R.E. above

What's the best way to get rid of the warning? (I don't want to disable it.)
Fix the problem. This code will almost certainly instacrash.

Doing a double type cast like this: (HMENU)(UINT_PTR)id gets rid of the warning. Why/how?
It happens because casting a UINT to a UINT_PTR is perfectly valid- UINT_PTR is just an integral type, there's no loss of data.

Disabling "Detect 64-bit Portability Issues" (Wp64) also gets rid of the warning. Why is Wp64 deprecated? Should I have it on?
It's deprecated because, actually, I can't quite remember why. I think it warns a little too readily. But for the basic "Don't cast integral types and pointers", you should definitely leave it on.

DeadMG
I don't understand. Can you please expand on this answer? I'm not dealing with pointers at all here. It's just a control id.
Tobbe
HMENU is a Handle to a Menu, which is a pointer. typedef HMENU__ *HMENU is HMENU's definition.
DeadMG
The code works fine as it is. No crashes. Copy/paste from msdn: "For a child window, hMenu specifies the child-window identifier, an integer value used by a dialog box control to notify its parent about events."
Tobbe
It also clearly says "A handle to a menu". The fact that your code works only shows that you use it as only part of it's functionality. If I were to make a menu and pass it in, your code will crash. You're begging for bugs. Take a UINT_PTR, it's four characters added. Visual Studio is right to warn.
DeadMG
Yeah, that does sound like the best solution. Thanks for getting back to me with answers to my comments.
Tobbe
@DeadMG: Not true. The code will not crash, because the cast in the code is perfectly safe and correct from the WinAPI point of view. When you create a window of `WS_CHILD` style, you have to pass the ID of the child winodow cast to `HMENU` type. That's just how it works in WinAPI. It is not "a handle to a menu". It would be "a handle to a menu" if the windows were not a `WS_CHILD`.
AndreyT
+2  A: 

The type that's hiding behind the HMENU name is actually a pointer type. The compiler is telling you that casing a smaller integer type to a larger pointer type makes no sense, since the resultant pointer value will be "incomplete", i.e. the higher order bits if the pointer value will be filled with zeros. The latter makes very little sense with pointer types.

In your particular case this is safe, since this HMENU value is not really supposed to be a pointer that points anywhere. However, the compiler does not know that, which is why it issues a warning. Use a larger integer type as an intermediate type in the cast and the warning shall go away (you suggested that yourself), since in this case you are doing two casts: smaller integer to a larger integer, and then larger integer to a pointer. Smaller integer to a larger integer is an arithmetic cast, for which it makes perfect sense to fill the higher-order bits with zeros (the represented value does not change), so there will be no warning for it.

AndreyT
Thanks for the great explanation on why the double cast works!
Tobbe
A: 

Why does the compiler think it's a bad idea to cast to a bigger type?

Casting between types of different size is generally a problematic operation, because the source type might not be able to represent all values needed for the target.

What's the best way to get rid of the warning? (I don't want to disable it.)

I'd say, use HMENU everywhere and declare your function as

HWND WebformCreate(HWND hParent, HMENU id)

Doing a double type cast like this: (HMENU)(UINT_PTR)id gets rid of the warning. Why/how?

UINT_PTR is an integer type large enough to hold all pointer values, therefore the warnings go away.

HMENU is a pointer type. This doesn't mean a value of a hMenu is actually a pointer, but this is a hack that disallows you to implicitly mix eg. HMENU and HWND (because HMENU and HWND are something like struct _hMENU* and struct _hWND*, respectively, which are not compatible, whereas UINTs would be).

jpalecek