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)
设置窗体的样式,需要用到 GetWindowLong
和 SetWindowLong
,两这个原型在 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);