續:淺析如何用C#.NET做螢幕軟體以及註冊全域快速鍵(上)
下面來說第三種方法:類比PrintScreen按鍵,訪問Clipboard來獲得螢幕,繼而執行截取操作。
1、類比PrintScreen按鍵為我們省去了很多代碼,但剛開始也給我帶來了很大的費解,假如你想通過button_click事件調用API來類比按下PrintScreen鍵,然後通過Clipboard.GetImage()去擷取剪貼簿裡面的圖片,那樣結果只有一個:失敗!也許你認為是我們在調用Clipboard.GetImage()時類比按鍵還未完成操作,然後我們在它們中間加個Thread.Sleep(0),很遺憾,結果還是一樣。我懷疑是滑鼠點擊button和類比PrintScreen按鍵兩者之間的紊亂造成的,在此就不多說廢話了,本人想到的解決方案就是使用backgroundWorker來執行按鍵處理,這樣就分離了表單線程和類比按鍵之間的紊亂問題。
C#類比鍵盤按鍵的實現:
類比鍵盤要用到user32.dll裡面的keybd_event
原型如下:
[DllImport("user32.dll")]static extern void keybd_event( byte bVk,// 虛擬索引值 byte bScan,// 硬體掃描碼 uint dwFlags,// 動作標識 UIntPtr dwExtraInfo// 與鍵盤動作關聯的輔加資訊);
其中,bVk表示虛擬索引值,其實它是一個BYTE類型值的宏,其取值範圍為1-254。有關虛擬索引值表請在MSDN上使用關鍵字“Virtual-Key Codes”尋找相關資料。bScan表示當鍵盤上某鍵被按下和放開時,鍵盤系統硬體產生的掃描碼我們可以MapVirtualKey()函數在虛擬索引值與掃描碼之間進行轉換,一般可以設定為0。dwFlags表示各種各樣的鍵盤動作,應用程式可使用如下一些預定義常數的組合設定標誌位,KEYEVENTF_EXETENDEDKEY=1:若指定該值,則掃描碼前一個值為OXEO(224)的首碼位元組。DEYEVENTF_KEYUP=2:若指定該值,該鍵將被釋放;若未指定該值,該鍵交被接下。dwExtralnfo:定義與擊鍵相關的附加的32位值。C#類比PrintScreen按鍵樣本如下,
public static void PrintScreen() { IceApi.keybd_event( (byte)0x2c, 0, (uint)0, IntPtr.Zero );//down IceApi.keybd_event( (byte)0x2c, 0, (uint)2, IntPtr.Zero );//up }
貼一下button的處理,見~:
我們把click之後的事情都交給backgroundWorker去做,省去了很多很多麻煩問題。
接下來是開始進入真正的階段。首先New一個表單SnapForm,設定如下屬性:
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;this.Opacity = 0.99;this.ShowIcon = false;this.ShowInTaskbar = false;this.TopMost = true;this.WindowState =System.Windows.Forms.FormWindowState.Maximized;
修改建構函式為(見):
當表單顯示時,就已經是全螢幕的了。
之後,我們可以在form上面放個PictureBox,然後在PictureBox_Paint裡面畫線和矩形。
樣本:
picBox_Paint
private void picBox_Paint( object sender, PaintEventArgs e ) {
Graphics g = e.Graphics;
if (isDrawing) {
//g.DrawRectangle( penRect, captureRect);
DrawScableRect( captureRect, g );
}
else if (!isDrawned) {
g.DrawLine( penLine, 0, currentPoint.Y, fullScreen.Width, currentPoint.Y );
g.DrawLine( penLine, currentPoint.X, 0, currentPoint.X, fullScreen.Height );
}
}
首先我們需要在滑鼠按下時記錄矩形的起點,
if (!isDrawing) { startPoint = e.Location; captureRect.Location = startPoint; isDrawing = true; return; }
然後在MouseMove事件裡面重新整理矩形大小,最後調用this.Refresh(),強制表單重繪,調用Paint方法。
最最後是在MouseUp事件裡面檢測畫圖是否完成。這樣就停止調用this.Refresh()了。
至於畫圖形,就要用到Graphics.DrawRectangle()和Graphics.DrawLine(),可以到MSDN查詢它們的使用方法。
在MouseMove事件裡面,我們可以檢測滑鼠的當前位置,然後設定滑鼠形狀,繼而可以實現矩形的拖動以及擴大和縮小。
在此不詳贅述了。
關於儲存,我們可以加個MouseDoubleClick事件。在下面,我是直接從原始圖片截取矩形覆蓋地區的,
利用orgbmp.Clone( captureRect, screenSnap.PixelFormat )來複製我們需要的地區,這樣既保證了截取圖片的
映像品質,也避免了考慮矩形邊框問題。
MouseDoubleClick
private void SnapForm_MouseDoubleClick( object sender, MouseEventArgs e ) {
if (e.Button==MouseButtons.Left) {
Bitmap orgbmp = new Bitmap( screenSnap );
try {
Bitmap ab = orgbmp.Clone( captureRect, screenSnap.PixelFormat );
if (saveDlg.ShowDialog() == DialogResult.OK) {
ab.Save( saveDlg.FileName, imgFormat[Path.GetExtension( saveDlg.FileName )] );
MessageBox.Show( "Completed!" );
}
}
catch { }
finally { orgbmp.Dispose(); }
}
}
這樣C#就基本說完了,為了美化,我們可以在表單上面放個半透明圖層,做出類似QQ的效果。
最後說說在C#裡面如何來自訂滑鼠的樣式,以及定義全域快速鍵來實現。
1、自訂滑鼠的樣式:假如你只用C#裡面的Managed Code,自訂出的滑鼠結果顯示出來的會變成單色。
我們需要user32.dll裡面的幾個API來實現自訂彩色滑鼠樣式。
C# Signature:[DllImport("user32.dll")]static extern IntPtr LoadCursorFromFile(string lpFileName);[DllImport( "user32.dll" )]public static extern uint DestroyCursor( IntPtr cursorHandle );
這兩個都比較簡單,我們可以在form_load時載入滑鼠控制代碼,記得最後要在form_closing時釋放控制代碼資源~
this.Cursor = new Cursor( IntPtr handle )用來執行個體化滑鼠。
2、全域快速鍵或稱熱鍵,則需要調用user32裡面的另外兩個API來實現。
[DllImport( "user32.dll", SetLastError = true )] public static extern bool RegisterHotKey( IntPtr hWnd, // handle to window int id, // hot key identifier KeyModifiers fsModifiers, // key-modifier options System.Windows.Forms.Keys vk // virtual-key code ); [DllImport( "user32.dll", SetLastError = true )] public static extern bool UnregisterHotKey( IntPtr hWnd, // handle to window int id // hot key identifier ); [Flags] public enum KeyModifiers { None = 0, Alt = 1, Control = 2, Shift = 4, Windows = 8 }
比如註冊F3鍵為快速鍵:RegisterHotKey( this.Handle, 7890, IceApi.KeyModifiers.None, Keys.F3 );
這兩個API分別要放到Form_Load和Form_FormClosed事件裡面。
最後,我們需要截獲系統訊息來為我們定義的快速鍵執行相應操作。此時我們需要重寫WndProc
WndProc
protected override void WndProc( ref Message m ) {
switch (m.Msg) {
//hotkey pressed
case 0x0312:
if (m.WParam.ToString() == "7890") {
GlobalKeyProc( this.WindowState == FormWindowState.Minimized );
}
break;
}
base.WndProc( ref m );
}
在GlobalKeyProc()裡面處理我們需要的工作。
終於完成了。謝謝觀看。