软工项目查许久资料才搞定,stackoverflow上的资料一直没说清楚花了不少时间 = =

借此水一篇博文,不过好像还需要实现一个让窗口不贴在桌面上的需求,但是这个把父窗口设置成Zero应该就行了

实现步骤

WPF中的应用场景:将窗口钉在桌面上,使用快捷键Win+D显示桌面时不会让窗口隐藏

在窗口类中引入dll,调用系统api用于查找句柄、设置句柄等操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 获取指定窗口的样式
[DllImport("user32.dll", SetLastError = true)]
static extern UInt64 GetWindowLong(IntPtr hWnd, int nIndex);
// 设置指定窗口的样式
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, UInt64 dwNewLong);
// 获取桌面Desktop窗口的Handle
[DllImport("user32.dll")]
static extern IntPtr GetDesktopWindow();
// 设置窗口的父窗口
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
// 查找窗口handle
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpWindowClass, string lpWindowName);
// 查找窗口handle
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);

然后通过窗口的特性来查找正确的桌面窗口

桌面窗口的类名为WorkerW,但是根据这个类名会有重名的窗口,所以需要另外编写函数来进行查找

可以通过Microsoft Spy++来查看,正确的桌面窗口下至少有一个子窗口,其类名为SHELLDLL_DefView

查找正确桌面窗口句柄的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static IntPtr SearchDesktopHandle() {
IntPtr hRoot = GetDesktopWindow();
IntPtr hShellDll = IntPtr.Zero;
IntPtr hDesktop = FindWindowEx(hRoot, IntPtr.Zero, "WorkerW", String.Empty);
while (true) {
hShellDll = FindWindowEx(hDesktop, IntPtr.Zero, "SHELLDLL_DefView", String.Empty);
if (hShellDll != IntPtr.Zero) {
return hDesktop;
}
hDesktop = FindWindowEx(hRoot, hDesktop, "WorkerW", String.Empty);
}

return IntPtr.Zero;
}

然后还需要把窗口添加一个WS_CHILD的属性,否则调用SetParent函数是没有任何用处的(注意:具有WS_POPUP 属性的窗口需要将其去掉,否则存在冲突)

1
2
3
4
5
const int GWL_STYLE = (-16);
const UInt64 WS_CHILD = 0x40000000;
IntPtr hWnd = new WindowInteropHelper(this).Handle;
UInt64 iWindowStyle = GetWindowLong(hWnd, GWL_STYLE);
SetWindowLong(hWnd, GWL_STYLE, (iWindowStyle| WS_CHILD));

最后设置一下窗口的父窗口即可

1
2
IntPtr desktopHandle = SearchDesktopHandle();
SetParent(hWnd, desktopHandle);

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public partial class MainWindow : Window{
[DllImport("user32.dll", SetLastError = true)]
static extern UInt64 GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, UInt64 dwNewLong);
[DllImport("user32.dll")]
static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpWindowClass, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);

public static IntPtr SearchDesktopHandle() {
IntPtr hRoot = GetDesktopWindow();
IntPtr hShellDll = IntPtr.Zero;
IntPtr hDesktop = FindWindowEx(hRoot, IntPtr.Zero, "WorkerW", String.Empty);
while (true) {
hShellDll = FindWindowEx(hDesktop, IntPtr.Zero, "SHELLDLL_DefView", String.Empty);
if (hShellDll != IntPtr.Zero) {
return hDesktop;
}
hDesktop = FindWindowEx(hRoot, hDesktop, "WorkerW", String.Empty);
}
return IntPtr.Zero;
}

public MainWindow() {
InitializeComponent();
}

private void MainWindow_onLoaded(object sender, EventArgs e) {
const int GWL_STYLE = (-16);
const UInt64 WS_CHILD = 0x40000000;
IntPtr hWnd = new WindowInteropHelper(this).Handle;
UInt64 iWindowStyle = GetWindowLong(hWnd, GWL_STYLE);
SetWindowLong(hWnd, GWL_STYLE, (iWindowStyle| WS_CHILD));

IntPtr desktopHandle = Utils.SearchDesktopHandle();
SetParent(hWnd, desktopHandle);
}
}

参考文章

SetWindowLongA function (winuser.h)

win32 修改窗口属性

GetWindowLongA function (winuser.h)

Window Styles