拦截WM_NCCALCSIZE时reinterpret_castLPNCCALCSIZE_PARAMS转LPRECT安全吗?

Is it safe to reinterpret_cast LPNCCALCSIZE_PARAMS to LPRECT when intercepting WM_NCCALCSIZE?

虽然对我有用,但我担心将来有一天它会在我脸上爆炸,

LPRECT pRect = reinterpret_cast<LPRECT>(lParam);

我需要知道拦截WM_NCCALCSIZE时reinterpret_castLPNCCALCSIZE_PARAMSLPRECT是否安全,如果只需要处理(LPNCCALCSIZE_PARAMS)lParam->rgrc[0] or (LPARAM)lParam 不管剩下的 NCCALCSIZE_PARAMS 都会!

参考文献:

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

对象的内存地址与其第一个数据成员的内存地址相同。并且数组的内存地址与其第一个元素的内存地址相同。

根据 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.

由于 NCCALCSIZE_PARAMS 的第一个数据成员是一个 RECT[3] 数组:

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

那么逻辑上,总会有一个RECT位于lParam指向的内存地址,是的。

但是 技术上,在 C++ 下,当 wParamTRUE 时,您提出的是严格别名冲突,因为 lParam将指向 NCCALCSIZE_PARAMS,而不是 RECT。每 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).

您提出的将 NCCALCSIZE_PARAMS 当作 RECT 访问的提议不满足这些要求。

但是,由于 Win32 API 主要是为 C 而不是 C++ 设计的,您的提议 可能 在大多数 C++ 编译器中“有效”。

但是,为了安全起见,您确实应该根据 wParam 的值将 lParam 转换为正确的类型,例如:

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