C# 编写一个简单易用的 Windows 截屏增强工具
2022-05-11 14:12
2955 查看
半年前我开源了 DreamScene2 一个小而快并且功能强大的 Windows 动态桌面软件。有很多的人喜欢,这使我有了继续做开源的信心。这是我的第二个开源作品 ScreenshotEx 一个简单易用的 Windows 截屏增强工具。
欢迎 Star 和 Fork https://github.com/he55/ScreenshotEx
前言
在使用 Windows 系统的截屏快捷键
PrintScreen截屏时,如果需要把截屏保存到文件,需要先粘贴到画图工具然后另存为文件。以前我还没有觉得很麻烦,后来使用了 macOS 系统的截屏工具,我才知道原来一个小小的截屏工具也可以这么简单易用。于是参考 macOS 系统的截屏工具做了一个 Windows 版的。
功能
自动保存截屏到桌面
点击截屏预览可以编辑截屏
实现原理
如果想在按下系统的截屏快捷键后做一些事情,能想到的方法应该就是如何监听键盘事件。WIN32 API 提供的 SetWindowsHookExA 钩子函数刚好可以实现这个需求,
idHook参数设置成
WH_KEYBOARD_LL时是低等级键盘钩子可以捕获键盘消息。
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);
注册键盘钩子
需要注意:因为
SetWindowsHookEx是非托管函数第二个参数是个委托类型,
GC不会记录非托管函数对
.NET对象的引用。如果用临时变量保存委托出作用域就会被
GC释放,当
SetWindowsHookEx去调用已经被释放的委托就会报错。
SetWindowsHookEx函数第一个参数传
WH_KEYBOARD_LL低等级键盘钩子、第二个参数传键盘消息处理函数的委托、第三个参数使用
GetModuleHandle函数获取模块句柄、第四个参数传 0。
HookProc _hookProc; IntPtr _hhook; void StartHook() { _hookProc = new HookProc(LowLevelKeyboardProc); // 使用成员变量保存委托 _hhook = SetWindowsHookEx(WH_KEYBOARD_LL, _hookProc, GetModuleHandle(null), 0); // 注册键盘钩子,保存返回值卸载钩子时用到。GetModuleHandle(null) 获取当前模块句柄 }
键盘消息处理函数
在键盘消息处理函数里面捕获
PrintScreen按键消息,然后显示预览和保存图片逻辑
IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam) { if (nCode == HC_ACTION) { if (lParam.vkCode == VK_SNAPSHOT) // 捕获 PrintScreen 按键消息 { if ((int)wParam == WM_KEYUP || (int)wParam == 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); } }
相关文章推荐
- 用C#写一个简单的WINDOWS服务程序
- 利用C#编写一个简单的抓网页应用程序
- C# 编写的一个简单的 HTTP 服务器
- C#编写Windows桌面应用程序使程序同时只能运行一个副本
- C#编写windows服务,多服务为什么只启动一个(ServiceBase.Run)
- 编写一个简单的Windows文件过滤驱动(OS实验)
- C#编写的一个简单的登陆界面对话框的原代码
- C# 创建一个简单的Windows服务
- 用C#写一个简单的WINDOWS服务程序
- Java开发的一个简单截屏工具
- C#真他妈神奇,一个函数都不用写就能实现一个简单的邮件发送工具
- 用PB编写一个简单的网络信息收集小工具软件
- 用c#编写一个高级文件加密工具
- 分享一个C#编写简单的聊天程序(详细介绍)
- C#编写一个简单记事本功能
- 用编写一个简单的记事本(C#实现)
- 利用C#编写一个简单的抓网页应用程序
- C# 关闭/打开显示器工具+源码(一个API的简单操作)
- unity 3D学习日记:创建一个小场景并编写简单C#移动脚本
- C# Windows 服务 的简单程序的编写,安装 和 卸载