Home > Enterprise >  Is it safe to reinterpret_cast LPNCCALCSIZE_PARAMS to LPRECT when intercepting WM_NCCALCSIZE?
Is it safe to reinterpret_cast LPNCCALCSIZE_PARAMS to LPRECT when intercepting WM_NCCALCSIZE?

Time:03-03

Though it worked for me without problems, but I am afraid that it will explode in my face some day in the future,

LPRECT pRect = reinterpret_cast<LPRECT>(lParam);

I need to know if it is safe to reinterpret_cast LPNCCALCSIZE_PARAMS to LPRECT when intercepting WM_NCCALCSIZE, if I need to deal with only (LPNCCALCSIZE_PARAMS)lParam->rgrc[0] or (LPARAM)lParam no matter of the rest of NCCALCSIZE_PARAMS will be!

Refs:

https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-nccalcsize_params

CodePudding user response:

The memory address of an object is the same as the memory address of its 1st data member. And the memory address of an array is the same as the memory address of its 1st element.

Per the WM_NCCALCSIZE documentation:

lParam

If wParam is TRUE, lParam points to an NCCALCSIZE_PARAMS structure that contains information an application can use to calculate the new size and position of the client rectangle.

If wParam is FALSE, lParam points to a RECT structure. On entry, the structure contains the proposed window rectangle for the window. On exit, the structure should contain the screen coordinates of the corresponding window client area.

Since the 1st data member of NCCALCSIZE_PARAMS is a RECT[3] array:

typedef struct tagNCCALCSIZE_PARAMS {
  RECT       rgrc[3];
  PWINDOWPOS lppos;
} NCCALCSIZE_PARAMS, *LPNCCALCSIZE_PARAMS;

Then logically, there will always be a RECT located at the memory address pointed at by the lParam, yes.

But technically, under C , what you are proposing is a Strict Alias Violation when wParam is TRUE, since the lParam will be pointing at a NCCALCSIZE_PARAMS, not a RECT. Per cppreference.com:

Strict aliasing

Given an object with effective type T1, using an lvalue expression (typically, dereferencing a pointer) of a different type T2 is undefined behavior, unless:

  • T2 and T1 are compatible types.
  • T2 is cvr-qualified version of a type that is compatible with T1.
  • T2 is a signed or unsigned version of a type that is compatible with T1.
  • T2 is an aggregate type or union type type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union).
  • T2 is a character type (char, signed char, or unsigned char).

Your proposal to access a NCCALCSIZE_PARAMS as if it were a RECT does not satisfy those requirements.

But, since the Win32 API is primarily designed for C not C , your proposal will likely "work" in most C compilers.

But, to be on the safe side, you really should cast lParam to the correct type based on the value of wParam, eg:

LPRECT pRect;
if (wParam) {
    pRect = &(reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam)->rgrc[0]);
    // or:
    // pRect = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam)->rgrc;
}
else {
    pRect = reinterpret_cast<LPRECT>(lParam);
}
  • Related