通過C#編寫一個(gè)簡(jiǎn)易的Windows截屏增強(qiáng)工具
半年前我開源了 DreamScene2 一個(gè)小而快并且功能強(qiáng)大的 Windows 動(dòng)態(tài)桌面軟件。有很多的人喜歡,這使我有了繼續(xù)做開源的信心。這是我的第二個(gè)開源作品 ScreenshotEx 一個(gè)簡(jiǎn)單易用的 Windows 截屏增強(qiáng)工具。
前言
在使用 Windows 系統(tǒng)的截屏快捷鍵 PrintScreen 截屏?xí)r,如果需要把截屏保存到文件,需要先粘貼到畫圖工具然后另存為文件。以前我還沒有覺得很麻煩,后來使用了 macOS 系統(tǒng)的截屏工具,我才知道原來一個(gè)小小的截屏工具也可以這么簡(jiǎn)單易用。于是參考 macOS 系統(tǒng)的截屏工具做了一個(gè) Windows 版的。
功能
自動(dòng)保存截屏到桌面

點(diǎn)擊截屏預(yù)覽可以編輯截屏

實(shí)現(xiàn)原理
如果想在按下系統(tǒng)的截屏快捷鍵后做一些事情,能想到的方法應(yīng)該就是如何監(jiān)聽鍵盤事件。WIN32 API 提供的 SetWindowsHookExA 鉤子函數(shù)剛好可以實(shí)現(xiàn)這個(gè)需求,idHook 參數(shù)設(shè)置成 WH_KEYBOARD_LL 時(shí)是低等級(jí)鍵盤鉤子可以捕獲鍵盤消息。
SetWindowsHookExA 函數(shù)定義
HHOOK SetWindowsHookExA( [in] int idHook, // 鉤子類型 [in] HOOKPROC lpfn, // 鉤子處理函數(shù) [in] HINSTANCE hmod, // 模塊句柄 [in] DWORD dwThreadId // 線程Id );
鍵盤處理函數(shù)定義
LRESULT CALLBACK LowLevelKeyboardProc( _In_?int ???nCode, _In_?WPARAM wParam, // 鍵盤消息 _In_?LPARAM lParam // KBDLLHOOKSTRUCT 結(jié)構(gòu)體指針 );
代碼
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);
注冊(cè)鍵盤鉤子
需要注意:因?yàn)?nbsp;SetWindowsHookEx 是非托管函數(shù)第二個(gè)參數(shù)是個(gè)委托類型,GC 不會(huì)記錄非托管函數(shù)對(duì) .NET 對(duì)象的引用。如果用臨時(shí)變量保存委托出作用域就會(huì)被 GC 釋放,當(dāng) SetWindowsHookEx 去調(diào)用已經(jīng)被釋放的委托就會(huì)報(bào)錯(cuò)。
SetWindowsHookEx 函數(shù)第一個(gè)參數(shù)傳 WH_KEYBOARD_LL 低等級(jí)鍵盤鉤子、第二個(gè)參數(shù)傳鍵盤消息處理函數(shù)的委托、第三個(gè)參數(shù)使用 GetModuleHandle 函數(shù)獲取模塊句柄、第四個(gè)參數(shù)傳 0。
HookProc _hookProc;
IntPtr _hhook;
void StartHook()
{
_hookProc = new HookProc(LowLevelKeyboardProc); // 使用成員變量保存委托
_hhook = SetWindowsHookEx(WH_KEYBOARD_LL, _hookProc, GetModuleHandle(null), 0); // 注冊(cè)鍵盤鉤子,保存返回值卸載鉤子時(shí)用到。GetModuleHandle(null) 獲取當(dāng)前模塊句柄
}
鍵盤消息處理函數(shù)
在鍵盤消息處理函數(shù)里面捕獲 PrintScreen 按鍵消息,然后顯示預(yù)覽和保存圖片邏輯
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) // 按鍵釋放時(shí)保存圖片
SaveImage();
else
_previewWindow.SetHide();
}
}
return CallNextHookEx(_hhook, nCode, wParam, ref lParam);
}
保存圖片
從系統(tǒng)剪貼板獲取圖片
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#編寫一個(gè)簡(jiǎn)易的Windows截屏增強(qiáng)工具的詳細(xì)內(nèi)容,更多關(guān)于C#截屏增強(qiáng)工具的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 使用C#從零開始實(shí)現(xiàn)一個(gè)截屏工具
- 使用C#自制一個(gè)截屏工具
- C#?WPF使用GDI實(shí)現(xiàn)截屏功能
- C# WPF利用Clip屬性實(shí)現(xiàn)截屏框功能
- C#?wpf實(shí)現(xiàn)截屏框熱鍵截屏的示例代碼
- C#?wpf使用GDI+實(shí)現(xiàn)截屏效果
- C#?wpf使用DockPanel實(shí)現(xiàn)制作截屏框
- C#實(shí)現(xiàn)小截屏軟件功能
- 基于C#實(shí)現(xiàn)的屏幕指定區(qū)域截屏代碼
- c#不使用系統(tǒng)api實(shí)現(xiàn)可以指定區(qū)域屏幕截屏功能
- c#根據(jù)網(wǎng)址抓取網(wǎng)頁截屏生成圖片的示例
- C# 全屏截圖的功能實(shí)現(xiàn)
相關(guān)文章
C#?Winform實(shí)現(xiàn)圓角無鋸齒按鈕
這篇文章主要介紹了C#?Winform實(shí)現(xiàn)圓角無鋸齒按鈕,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下2022-08-08
c#異步讀取數(shù)據(jù)庫(kù)與異步更新ui的代碼實(shí)現(xiàn)
這篇文章主要介紹了c#從數(shù)據(jù)庫(kù)里取得數(shù)據(jù)并異步更新ui的方法,大家參考使用吧2013-12-12
使用C#實(shí)現(xiàn)將Word?轉(zhuǎn)文本存儲(chǔ)到數(shù)據(jù)庫(kù)并進(jìn)行管理
這篇文章主要為大家詳細(xì)介紹了如何使用C#實(shí)現(xiàn)將Word?轉(zhuǎn)文本存儲(chǔ)到數(shù)據(jù)庫(kù)并進(jìn)行管理,文中的示例代碼講解詳細(xì),需要的小伙伴可以參考一下2024-03-03
C#實(shí)現(xiàn)將千分位字符串轉(zhuǎn)換成數(shù)字的方法
這篇文章主要介紹了C#實(shí)現(xiàn)將千分位字符串轉(zhuǎn)換成數(shù)字的方法,很適合初學(xué)者更好的理解C#字符串原理,需要的朋友可以參考下2014-08-08
C#實(shí)現(xiàn)凍結(jié)Excel窗口以鎖定行列或解除凍結(jié)
在處理大型Excel工作簿時(shí),有時(shí)候我們需要在工作表中凍結(jié)窗格,這樣可以在滾動(dòng)查看數(shù)據(jù)的同時(shí)保持某些行或列固定不動(dòng),下面我們就來看看如何使用C#實(shí)現(xiàn)凍結(jié)Excel窗口吧2024-04-04

