WPF 使用 AppBar 将窗口停靠在桌面上,让其他程序不占用此窗口的空间(附我封装的附加属性)
原文:WPF 使用 AppBar 将窗口停靠在桌面上,让其他程序不占用此窗口的空间(附我封装的附加属性)
本文介绍如何使用 Windows 的 AppBar 相关 API 实现固定停靠在桌面上的特殊窗口。
停靠窗口
你可能并不明白停靠窗口是什么意思。
看下图,你可能使用过 OneNote 的停靠窗口功能。当打开一个新的 OneNote 停靠窗口之后,这个新的 OneNote 窗口将固定显示在桌面的右侧,其他的窗口就算最大化也只会占据剩余的空间。
OneNote 的这种功能可以让你在一边浏览网页或做其他事情的时候,以便能够做笔记。同时又不用担心其他窗口最大化的时候会占据记笔记的一部分空间。

这其实也是 Windows 任务栏所使用的方法。
OneNote 中给出的名称叫做“停靠窗口”,于是这可以代表微软希望用户对这个概念的理解名词。
只是,这个概念在 Windows API 中的名称叫做 AppBar。
AppBar
要做出停靠窗口的效果,最核心的 API 是 SHAppBarMessage,用于发送 AppBar 消息给操作系统,以便让操作系统开始处理此窗口已形成一个 AppBar 窗口。也就是我们在用户交互上所说的“停靠窗口”。
虽然说要让一个窗口变成 AppBar 只需要一点点代码,但是要让整个停靠窗口工作得真的像一个停靠窗口,依然需要大量的辅助代码。所以我将其封装成了一个 DesktopAppBar 类,方便 WPF 程序来调用。
如何使用
以下使用,你需要先获取我封装的源码才可以编译通过:
你可以在 XAML 中使用:
<Window x:Class="Walterlv.Demo.DesktopDocking.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dock="clr-namespace:Walterlv.Demo.DesktopDocking"
mc:Ignorable="d" Title="Walterlv 的停靠窗口" Height="450" Width="500"
dock:DesktopAppBar.AppBar="Right">
<StackPanel Background="#ffcd42">
<TextBlock FontSize="64" Margin="64" TextAlignment="Center" Text="walterlv 的停靠窗口" />
<Button Content="再停靠一个 - blog.walterlv.com" FontSize="32" Padding="32" Margin="32" Background="#f9d77b" BorderThickness="0"
Click="Button_Click"/>
</StackPanel>
</Window>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
核心代码是其中的一处属性赋值 dock:DesktopAppBar.AppBar="Right",以及前面的命名空间声明 xmlns:dock="clr-namespace:Walterlv.Demo.DesktopDocking"。
你也可以在 C# 代码中使用:
using System;
using System.Windows;
namespace Walterlv.Demo.DesktopDocking
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
DesktopAppBar.SetAppBar(this, AppBarEdge.Right);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
使用以上代码中的任何一种方式,你就可以让你的窗口在右边停靠了。
从图中我们可以发现,我们的示例窗口停靠在了右边,其宽度就是我们在 XAML 中设置的窗口宽度(当然这是我封装的逻辑,而不是 AppBar 的原生逻辑)。
同时我们还能注意到,Visual Studio 的窗口是处于最大化的状态的——这是停靠窗口的最大优势——可以让其他窗口的工作区缩小,在最大化的时候不会覆盖到停靠窗口的内容。
另外,如果设置了第二个停靠窗口,那么第二个停靠窗口会挤下第一个窗口的位置。
[外链图片转存失败(img-OjZqTgeb-1564229607157)(https://i.loli.net/2019/07/27/5d3c3fbb9a9e364593.png)]

如何还原
Windows AppBar 的 API 有一个很不好的设定,如果进程退出了,那么 AppBar 所占用的空间 并不会还原!!!
不过不用担心,我在封装的代码里面加入了窗口关闭时还原空间的代码,如果你正常关闭窗口,那么停靠窗口占用的空间就会及时还原回来。
当然,你也可以适时调用下面的代码:
DesktopAppBar.SetAppBar(this, AppBarEdge.None);
- 1
附源码
由于源码一直在持续改进,所以本文中贴的源代码可能不是最新的。你可以在以下仓库找到这段源码的最新版本:
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable EnumUnderlyingTypeIsInt
// ReSharper disable MemberCanBePrivate.Local
// ReSharper disable UnusedMember.Local
// ReSharper disable UnusedMember.Global
namespace Walterlv.Demo.DesktopDocking
{
/// <summary>
/// 表示窗口停靠到桌面上时的边缘方向。
/// </summary>
public enum AppBarEdge
{
/// <summary>
/// 窗口停靠到桌面的左边。
/// </summary>
Left = 0,
/// <summary>
/// 窗口停靠到桌面的顶部。
/// </summary>
Top,
/// <summary>
/// 窗口停靠到桌面的右边。
/// </summary>
Right,
/// <summary>
/// 窗口停靠到桌面的底部。
/// </summary>
Bottom,
/// <summary>
/// 窗口不停靠到任何方向,而是成为一个普通窗口占用剩余的可用空间(工作区)。
/// </summary>
None
}
/// <summary>
/// 提供将窗口停靠到桌面某个方向的能力。
/// </summary>
public class DesktopAppBar
{
/// <summary>
/// 标识 Window.AppBar 的附加属性。
/// </summary>
public static readonly DependencyProperty AppBarProperty = DependencyProperty.RegisterAttached(
"AppBar", typeof(AppBarEdge), typeof(DesktopAppBar),
new PropertyMetadata(AppBarEdge.None, OnAppBarEdgeChanged));
/// <summary>
/// 获取 <paramref name="window"/> 当前的停靠边缘。
/// </summary>
/// <param name="window">要获取停靠边缘的窗口。</param>
/// <returns>停靠边缘。</returns>
public static AppBarEdge GetAppBar(Window window) => (AppBarEdge)window.GetValue(AppBarProperty);
/// <summary>
/// 设置 <paramref name="window"/> 的停靠边缘方向。
/// </summary>
/// <param name="window">要设置停靠的窗口。</param>
/// <param name="value">要设置的停靠边缘方向。</param>
public static void SetAppBar(Window window, AppBarEdge value) => window.SetValue(AppBarProperty, value);
private static readonly DependencyProperty AppBarProcessorProperty = DependencyProperty.RegisterAttached(
"AppBarProcessor", typeof(AppBarWindowProcessor), typeof(DesktopAppBar), new PropertyMetadata(null));
[SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")]
private static void OnAppBarEdgeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (DesignerProperties.GetIsInDesignMode(d))
{
return;
}
var oldValue = (AppBarEdge) e.OldValue;
var newValue = (AppBarEdge) e.NewValue;
var oldEnabled = oldValue is AppBarEdge.Left
|| oldValue is AppBarEdge.Top
|| oldValue is AppBarEdge.Right
|| oldValue is AppBarEdge.Bottom;
var newEnabled = newValue is AppBarEdge.Left
|| newValue is AppBarEdge.Top
|| newValue is AppBarEdge.Right
|| newValue is AppBarEdge.Bottom;
if (oldEnabled && !newEnabled)
{
var processor = (AppBarWindowProcessor) d.GetValue(AppBarProcessorProperty);
processor.Detach();
}
else if (!oldEnabled && newEnabled)
{
var processor = new AppBarWindowProcessor((Window) d);
d.SetValue(AppBarProcessorProperty, processor);
processor.Attach(newValue);
}
else if (oldEnabled && newEnabled)
{
var processor = (AppBarWindowProcessor) d.GetValue(AppBarProcessorProperty);
processor.Update(newValue);
}
}
/// <summary>
/// 包含对 <see cref="Window"/> 进行操作以便使其成为一个桌面停靠窗口的能力。
/// </summary>
private class AppBarWindowProcessor
{
/// <summary>
/// 创建 <see cref="AppBarWindowProcessor"/> 的新实例。
/// </summary>
/// <param name="window">需要成为停靠窗口的 <see cref="Window"/> 的实例。</param>
public AppBarWindowProcessor(Window window)
{
_window = window;
_callbackId = RegisterWindowMessage("AppBarMessage");
_hwndSourceTask = new TaskCompletionSource<HwndSource>();
var source = (HwndSource) PresentationSource.FromVisual(window);
if (source == null)
{
window.SourceInitialized += OnSourceInitialized;
}
else
{
_hwndSourceTask.SetResult(source);
}
_window.Closed += OnClosed;
}
private readonly Window _window;
private readonly TaskCompletionSource<HwndSource> _hwndSourceTask;
private readonly int _callbackId;
private WindowStyle _restoreStyle;
private Rect _restoreBounds;
private ResizeMode _restoreResizeMode;
private bool _restoreTopmost;
private AppBarEdge Edge { get; set; }
/// <summary>
/// 在可以获取到窗口句柄的时候,给窗口句柄设置值。
/// </summary>
private void OnSourceInitialized(object sender, EventArgs e)
{
_window.SourceInitialized -= OnSourceInitialized;
var source = (HwndSource) PresentationSource.FromVisual(_window);
_hwndSourceTask.SetResult(source);
}
/// <summary>
/// 在窗口关闭之后,需要恢复窗口设置过的停靠属性。
/// </summary>
private void OnClosed(object sender, EventArgs e)
{
_window.Closed -= OnClosed;
_window.ClearValue(AppBarProperty);
}
/// <summary>
/// 将窗口属性设置为停靠所需的属性。
/// </summary>
private void ForceWindowProperties()
{
_window.WindowStyle = WindowStyle.None;
_window.ResizeMode = ResizeMode.NoResize;
_window.Topmost = true;
}
/// <summary>
/// 备份窗口在成为停靠窗口之前的属性。
/// </summary>
private void BackupWindowProperties()
{
_restoreStyle = _window.WindowStyle;
_restoreBounds = _window.RestoreBounds;
_restoreResizeMode = _window.ResizeMode;
_restoreTopmost = _window.Topmost;
}
/// <summary>
/// 使一个窗口开始成为桌面停靠窗口,并开始处理窗口停靠消息。
/// </summary>
/// <param name="value">停靠方向。</param>
public async void Attach(AppBarEdge value)
{
var hwndSource = await _hwndSourceTask.Task;
BackupWindowProperties();
var data = new APPBARDATA();
data.cbSize = Marshal.SizeOf(data);
data.hWnd = hwndSource.Handle;
data.uCallbackMessage = _callbackId;
SHAppBarMessage((int) ABMsg.ABM_NEW, ref data);
hwndSource.AddHook(WndProc);
Update(value);
}
/// <summary>
/// 更新一个窗口的停靠方向。
/// </summary>
/// <param name="value">停靠方向。</param>
public async void Update(AppBarEdge value)
{
var hwndSource = await _hwndSourceTask.Task;
Edge = value;
var bounds = TransformToAppBar(hwndSource.Handle, _window.RestoreBounds, value);
ForceWindowProperties();
Resize(_window, bounds);
}
/// <summary>
/// 使一个窗口从桌面停靠窗口恢复成普通窗口。
/// </summary>
public async void Detach()
{
var hwndSource = await _hwndSourceTask.Task;
var data = new APPBARDATA();
data.cbSize = Marshal.SizeOf(data);
data.hWnd = hwndSource.Handle;
SHAppBarMessage((int) ABMsg.ABM_REMOVE, ref data);
_window.WindowStyle = _restoreStyle;
_window.ResizeMode = _restoreResizeMode;
_window.Topmost = _restoreTopmost;
Resize(_window, _restoreBounds);
}
private IntPtr WndProc(IntPtr hwnd, int msg,
IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == _callbackId)
{
if (wParam.ToInt32() == (int) ABNotify.ABN_POSCHANGED)
{
var hwndSource = _hwndSourceTask.Task.Result;
var bounds = TransformToAppBar(hwndSource.Handle, _window.RestoreBounds, Edge);
Resize(_window, bounds);
handled = true;
}
}
return IntPtr.Zero;
}
private static void Resize(Window window, Rect bounds)
{
window.Left = bounds.Left;
window.Top = bounds.Top;
window.Width = bounds.Width;
window.Height = bounds.Height;
}
private Rect TransformToAppBar(IntPtr hWnd, Rect area, AppBarEdge edge)
{
var data = new APPBARDATA();
data.cbSize = Marshal.SizeOf(data);
data.hWnd = hWnd;
data.uEdge = (int) edge;
if (data.uEdge == (int) AppBarEdge.Left || data.uEdge == (int) AppBarEdge.Right)
{
data.rc.top = 0;
data.rc.bottom = (int) SystemParameters.PrimaryScreenHeight;
if (data.uEdge == (int) AppBarEdge.Left)
{
data.rc.left = 0;
data.rc.right = (int) Math.Round(area.Width);
}
else
{
data.rc.right = (int) SystemParameters.PrimaryScreenWidth;
data.rc.left = data.rc.right - (int) Math.Round(area.Width);
}
}
else
{
data.rc.left = 0;
data.rc.right = (int) SystemParameters.PrimaryScreenWidth;
if (data.uEdge == (int) AppBarEdge.Top)
{
data.rc.top = 0;
data.rc.bottom = (int) Math.Round(area.Height);
}
else
{
data.rc.bottom = (int) SystemParameters.PrimaryScreenHeight;
data.rc.top = data.rc.bottom - (int) Math.Round(area.Height);
}
}
SHAppBarMessage((int) ABMsg.ABM_QUERYPOS, ref data);
SHAppBarMessage((int) ABMsg.ABM_SETPOS, ref data);
return new Rect(data.rc.left, data.rc.top,
data.rc.right - data.rc.left, data.rc.bottom - data.rc.top);
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public readonly IntPtr lParam;
}
private enum ABMsg : int
{
ABM_NEW = 0,
ABM_REMOVE,
ABM_QUERYPOS,
ABM_SETPOS,
ABM_GETSTATE,
ABM_GETTASKBARPOS,
ABM_ACTIVATE,
ABM_GETAUTOHIDEBAR,
ABM_SETAUTOHIDEBAR,
ABM_WINDOWPOSCHANGED,
ABM_SETSTATE
}
private enum ABNotify : int
{
ABN_STATECHANGE = 0,
ABN_POSCHANGED,
ABN_FULLSCREENAPP,
ABN_WINDOWARRANGE
}
[DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);
}
}
}
- 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
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
参考资料
- c# - How do you do AppBar docking (to screen edge, like WinAmp) in WPF? - Stack Overflow
- mgaffigan/WpfAppBar: AppBar implementation for WPF
- .net - How to dock an application in the Windows desktop? - Stack Overflow
- AppBar using C# - CodeProject
- SHAppBarMessage function (shellapi.h) - Microsoft Docs
- RegisterWindowMessageA function (winuser.h) - Microsoft Docs
我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。
如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。
WPF 使用 AppBar 将窗口停靠在桌面上,让其他程序不占用此窗口的空间(附我封装的附加属性)的更多相关文章
- 借鉴网上的winform模仿QQ窗口停靠功能稍作改动
2015-07-11 15:24:04 1 using System; using System.Collections.Generic; using System.ComponentModel; u ...
- wpf仿qq边缘自动停靠,支持多屏
wpf完全模仿qq边缘自动隐藏功能,采用鼠标钩子获取鼠标当前状态,在通过当前鼠标的位置和点击状态来计算是否需要隐藏. 以下是实现的具体方法: 一.鼠标钩子实时获取当前鼠标的位置和点击状态 /// &l ...
- [WPF疑难] 模式窗口被隐藏后重新显示时变成了非模式窗口
原文:[WPF疑难] 模式窗口被隐藏后重新显示时变成了非模式窗口 [WPF疑难] 模式窗口被隐藏后重新显示时变成了非模式窗口 周银辉 现象: 大家可以试试下面这个很有趣但会带来Defect的现象:当我 ...
- C盘里的桌面文件移到E盘里了,然后E盘里的文件都显示到桌面上了,怎么将桌面文件还原回C盘
1 . 直接按Windows键+R,打开"运行"对话框,在输入框中输入"regedit"命令,会打开注册表编辑窗口: 2.打开注册表文件将HKEY_CURREN ...
- Winform应用程序实现通用消息窗口
记得我之前发表过一篇文章<Winform应用程序实现通用遮罩层>,是实现了透明遮罩的消息窗口,功能侧重点在动图显示+消息提醒,效果看上去比较的炫,而本篇我又来重新设计通用消息窗口,功能重点 ...
- Delphi中如何控制其他程序窗体上的窗口控件
回调函数一般是按照调用者的要求定义好参数和返回值的类型,你向调用者提供你的回调函数的入口地址,然后调用者有什么事件发生的时候就可以随时按照你提供的地址调用这个函数通知你,并按照预先规定好的形式传递参数 ...
- MFC如何在有界面的应用程序中开启控制台窗口
在有界面的应用程序中开启控制台窗口有时候非常有用,尤其是在调试多线程应用程序中,由于通过断点的方式调试程序时会导致线程挂起从而导致各种难于预料的结果.这时候就可以通过开启控制台窗口往窗口输出信息来查看 ...
- VS中为非控制台程序提供控制台输出窗口
/************************************************************************/ /* 模块名:ConsoleAdapter 文件名 ...
- 框架一般用作Java应用程序的窗口,而Applet是Java小程序的窗口
框架一般用作Java应用程序的窗口,而Applet是Java小程序的窗口. 与Frame不同,Applet是在网页中显示的,也可以通过添加Panel进行组件布局. package TomAwt; im ...
随机推荐
- 第08组 Alpha冲刺(4/6)
队名:955 组长博客:https://www.cnblogs.com/cclong/p/11882079.html 作业博客:https://edu.cnblogs.com/campus/fzu/S ...
- 安装ubuntu 16.04版本时搭建环境参考的文章
重新编译的命令:make all ZIP_DEBUGINFO_FILES=0 DISABLE_HOTSPOT_OS_VERSION_CHECK=ok 解决ubuntu中vi不能正常使用方向键与退格键的 ...
- [原创]Android 常用adb命令总结
[原创]Android 常用adb命令总结 1 adb介绍 1.1 adb官方网站及下载 官方网站下载安装:http://adbshell.com/downloads 1.2 adb安装是否成功检查? ...
- docker-Harbor仓库映像
部署环境: centos-7.2 docker-engine-1.12.5 docker-compose-1.9.0 harbor-0.5.0 1.下载所需的软件包(以下需要-翻-墙-下载的地方你们自 ...
- jQuery实现列表框双向选择操作
对列表框的操作经常碰到过这样的应用:从左侧的列表框中选中要选的项添加到右侧列表框中,然后提交最终选择的项,对误操作而选中的项还可以执行移除操作.在很多系统中应用比如说求职网站的选择意向工作地区,QQ好 ...
- [1-2] Dependence-Aware Service Function Chain Design and Mapping
文献名称:Dependence-Aware Service Function Chain Design and Mapping 文献类型(期刊.硕论.博论):会议:Globecom 发表年份:2017 ...
- 上传一句话木马时<? php被过滤的解决办法
i春秋“百度杯”CTF比赛 九月场 web题 upload 题目描述:想怎么传就怎么传,就是这么任性.tips:flag在flag.php中 打开题目发现 于是想到通过上传一句话木马进入后台 上传一句 ...
- php 面试必备:各种缓存技术详解
这门课程以电商网站为例,通过具体场景模块实战,让你更系统的掌握缓存原理.使用场景等相关知识,帮助你构建完整的缓存知识体系,胜任实际开发中缓存的处理,提升代码性能! 从原理到场景 系统讲解PHP缓 ...
- 运维笔记--ubuntu系统卸载指定版本的python
1.卸载python3.5(指定相应的版本就可以,例:python2.7,下同) sudo apt-get remove --purge python3.5 2.完全卸载python3.5及其依赖软件 ...
- HTML+CSS实现鼠标点上去动画效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
