使用SetParent劫持Win32 PopUp窗体

SetParent说明: MSDN-SetParent()

这个方法的原型大概是这样的(WinUser.h):

WINUSERAPI
HWND
WINAPI
SetParent(
    _In_ HWND hWndChild,
    _In_opt_ HWND hWndNewParent);

使用这个方法在劫持一些Style包含 WS_POPUP 的WIN32窗体时会出现劫持失败的情况(一般就是被劫持的窗体消失,指定的父窗体里没有出现子窗体)这是因为一般POPUP的窗体是不支持直接劫持的……MSDN里说的是 For compatibility reasons, SetParent does not modify the WS_CHILD or WS_POPUP window styles of the window whose parent is being changed.
能用SetParent劫持的窗体一般需要有 WS_CHILD (或者 WS_CHILDWINDOW 的样式,两者是完全一样的),在 WinUser.h 里,代码是这样的:

/*
* Window Styles
*/
...   
#define WS_POPUP            0x80000000L
#define WS_CHILD            0x40000000L
...
#define WS_POPUPWINDOW      (WS_POPUP          | \
                             WS_BORDER         | \
                             WS_SYSMENU)

#define WS_CHILDWINDOW      (WS_CHILD)

设置窗体的样式,需要用到 GetWindowLongSetWindowLong ,两这个原型在 WinUser.h 中是这样的:

WINUSERAPI
LONG
WINAPI
GetWindowLongA(
    _In_ HWND hWnd,
    _In_ int nIndex);

WINUSERAPI
LONG
WINAPI
GetWindowLongW(
    _In_ HWND hWnd,
    _In_ int nIndex);

#ifdef UNICODE
#define GetWindowLong  GetWindowLongW
#else
#define GetWindowLong  GetWindowLongA
#endif // !UNICODE

WINUSERAPI
LONG
WINAPI
SetWindowLongA(
    _In_ HWND hWnd,
    _In_ int nIndex,
    _In_ LONG dwNewLong);

WINUSERAPI
LONG
WINAPI
SetWindowLongW(
    _In_ HWND hWnd,
    _In_ int nIndex,
    _In_ LONG dwNewLong);

#ifdef UNICODE
#define SetWindowLong  SetWindowLongW
#else
#define SetWindowLong  SetWindowLongA
#endif // !UNICODE

#ifdef _WIN64

WINUSERAPI
LONG_PTR
WINAPI
GetWindowLongPtrA(
    _In_ HWND hWnd,
    _In_ int nIndex);

WINUSERAPI
LONG_PTR
WINAPI
GetWindowLongPtrW(
    _In_ HWND hWnd,
    _In_ int nIndex);

#ifdef UNICODE
#define GetWindowLongPtr  GetWindowLongPtrW
#else
#define GetWindowLongPtr  GetWindowLongPtrA
#endif // !UNICODE

WINUSERAPI
LONG_PTR
WINAPI
SetWindowLongPtrA(
    _In_ HWND hWnd,
    _In_ int nIndex,
    _In_ LONG_PTR dwNewLong);

WINUSERAPI
LONG_PTR
WINAPI
SetWindowLongPtrW(
    _In_ HWND hWnd,
    _In_ int nIndex,
    _In_ LONG_PTR dwNewLong);

#ifdef UNICODE
#define SetWindowLongPtr  SetWindowLongPtrW
#else
#define SetWindowLongPtr  SetWindowLongPtrA
#endif // !UNICODE

#else  /* _WIN64 */

#define GetWindowLongPtrA   GetWindowLongA
#define GetWindowLongPtrW   GetWindowLongW
#ifdef UNICODE
#define GetWindowLongPtr  GetWindowLongPtrW
#else
#define GetWindowLongPtr  GetWindowLongPtrA
#endif // !UNICODE

#define SetWindowLongPtrA   SetWindowLongA
#define SetWindowLongPtrW   SetWindowLongW
#ifdef UNICODE
#define SetWindowLongPtr  SetWindowLongPtrW
#else
#define SetWindowLongPtr  SetWindowLongPtrA
#endif // !UNICODE

MSDN-SetWindowLong()

在调用 SetWindowLong 之后,有时需要调用 SetWindowPos 来刷新一下窗体的缓存。这里有一点需要注意,MSDN中有如下描述:
If you have changed certain window data using SetWindowLong, you must call SetWindowPos for the changes to take effect. Use the following combination for uFlags: SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED.

C#中写C++函数原型的时候,类型映射一般是这样的规则:

C++ C#
LPTR IntPtr
int int
long int/uint

在更改窗体样式的时候,会用到 WS_POPUP = 0x80000000L ,这个值C#用int装会吃警告,所以可以用uint装这个数。C#中函数原型以及代码可以如下书写:

const uint
    WS_POPUP = 0x80000000,
    WS_CHILD = 0x40000000,

const int 
    GWL_STYLE = (-16),

const int
    SWP_NOMOVE = 0x02,
    SWP_NOSIZE = 0x01,
    SWP_NOZORDER = 0x04,
    SWP_FRAMECHANGED = 0x20,

[DllImport("user32.dll", SetLastError = true)]
public static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

[DllImport("user32.dll", EntryPoint = "GetWindowLongA", SetLastError = true)]
public static extern uint GetWindowLong(IntPtr hwnd, int nIndex);

[DllImport("user32.dll", EntryPoint = "SetWindowLong", SetLastError = true)]
public static extern uint SetWindowLong(IntPtr hwnd, int nIndex, uint dwNewLong);

var ptr = new IntPtr(HWND);
var style = Win32API.GetWindowLong(ptr, Win32API.GWL_STYLE);
if ((style & Win32API.WS_POPUP) != 0)
{
    style &= ~Win32API.WS_POPUP;
    style |= Win32API.WS_CHILD;
    var rst0 = Win32API.SetWindowLong(ptr, Win32API.GWL_STYLE, style);
    var rst1 = Win32API.SetWindowPos(ptr, 0, 0, 0, 0,0, Win32API.SWP_NOMOVE | Win32API.SWP_NOSIZE | Win32API.SWP_NOZORDER | Win32API.SWP_FRAMECHANGED);
}
var rst2 = Win32API.SetParent(ptr, _windowHandle);

你可能感兴趣的:(C#.NET)