DreamScene2 一个小而快并且功能强大的 Windows 动态桌面软件。有很多的人喜欢,这使我有了继续做开源的信心。这是我的第二个开源作品 ScreenshotEx 一个简单易用的 Windows 截屏增强工具。
https://github.com/he55/ScreenshotEx
功能
-
自动保存截屏到桌面
实现原理
SetWindowsHookExA 钩子函数刚好可以实现这个需求,参数设置成 时是低等级键盘钩子可以捕获键盘消息。
HHOOK SetWindowsHookExA(
[in] int idHook, // 钩子类型
[in] HOOKPROC lpfn, // 钩子处理函数
[in] HINSTANCE hmod, // 模块句柄
[in] DWORD dwThreadId // 线程Id
;
键盘处理函数定义
LRESULT CALLBACK LowLevelKeyboardProc(
_In_ int nCode,
_In_ WPARAM wParam, // 键盘消息
_In_ LPARAM lParam // KBDLLHOOKSTRUCT 结构体指针
;
代码
C# PInvoke 定义
const int HC_ACTION = 0;
const int WH_KEYBOARD_LL = 13;
const int WM_KEYUP = 0x0101;
const int WM_SYSKEYUP = 0x0105;
const int VK_SNAPSHOT = 0x2C;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto]
public struct KBDLLHOOKSTRUCT
{
public uint vkCode;
public uint scanCode;
public uint flags;
public uint time;
public UIntPtr dwExtraInfo;
}
[UnmanagedFunctionPointer(CallingConvention.Winapi]
public delegate IntPtr HookProc(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam;
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto]
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hmod, int dwThreadId;
[DllImport("User32.dll", SetLastError = true, ExactSpelling = true]
[return: MarshalAs(UnmanagedType.Bool]
public static extern bool UnhookWindowsHookEx(IntPtr hhk;
[DllImport("User32.dll", SetLastError = false, ExactSpelling = true]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam;
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto]
public static extern IntPtr GetModuleHandle([Optional] string lpModuleName;
注册键盘钩子
函数第一个参数传 低等级键盘钩子、第二个参数传键盘消息处理函数的委托、第三个参数使用 函数获取模块句柄、第四个参数传 0。
HookProc _hookProc;
IntPtr _hhook;
void StartHook(
{
_hookProc = new HookProc(LowLevelKeyboardProc; // 使用成员变量保存委托
_hhook = SetWindowsHookEx(WH_KEYBOARD_LL, _hookProc, GetModuleHandle(null, 0; // 注册键盘钩子,保存返回值卸载钩子时用到。GetModuleHandle(null 获取当前模块句柄
}
键盘消息处理函数
IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam
{
if (nCode == HC_ACTION
{
if (lParam.vkCode == VK_SNAPSHOT // 捕获 PrintScreen 按键消息
{
if ((intwParam == WM_KEYUP || (intwParam == WM_SYSKEYUP // 按键释放时保存图片
SaveImage(;
else
_previewWindow.SetHide(;
}
}
return CallNextHookEx(_hhook, nCode, wParam, ref lParam;
}
保存图片
void SaveImage(
{
if (Clipboard.ContainsImage(
{
if (!Directory.Exists(_settings.SavePath
Directory.CreateDirectory(_settings.SavePath;
string ext = "png";
ImageFormat imageFormat = ImageFormat.Png;
switch (_settings.SaveExtension
{
case 0:
imageFormat = ImageFormat.Png;
ext = "png";
break;
case 1:
imageFormat = ImageFormat.Jpeg;
ext = "jpg";
break;
case 2:
imageFormat = ImageFormat.Bmp;
ext = "bmp";
break;
}
if (_settings.SaveName == 0
{
string name = DateTime.Now.ToString("yyyy-MM-dd HH.mm.ss";
_saveFilePath = Path.Combine(_settings.SavePath, $"{PrefixName} {name}.{ext}";
}
else
{
do
{
_saveFilePath = Path.Combine(_settings.SavePath, $"{PrefixName} {_nameIndex}.{ext}";
_nameIndex++;
} while (File.Exists(_saveFilePath;
}
Image image = Clipboard.GetImage(;
image.Save(_saveFilePath, imageFormat;
if (_settings.IsPlaySound
_soundPlayer.Play(;
if (_settings.IsShowPreview
_previewWindow.SetImage(_saveFilePath;
}
}
完整代码 https://github.com/he55/ScreenshotEx