本文介紹一個用C#寫的好用程式當按下鍵盤PrintScreen鍵時自動儲存畫面這樣就不用進入小畫家去複製貼上了

 

本程式須在 "有畫面" 時候才能使用如果因螢幕保護而回到登入畫面就無畫面可以抓取!
程式中用到KeyHook類別[1]用來跟系統鍵盤掛勾(或稱攔截),監聽是否PrintScreen按鍵被按下,Hook()函數如下:
 
        public void Hook()
        {
            if (m_HookHandle == 0)
            {
                using (Process curProcess = Process.GetCurrentProcess())
 
                using (ProcessModule curModule = curProcess.MainModule)
                {
                    m_KbdHookProc = new HookProc(KeyHooker.KeyboardHookProc);
                    m_HookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, m_KbdHookProc,
                        GetModuleHandle(curModule.ModuleName), 0);
                }
                if (m_HookHandle == 0)
                {
                    MessageBox.Show("呼叫 SetWindowsHookEx 失敗!");
                    return;
                }
            }
        }
 
 
攔截函數 KeyboardHookProc() 掛勾後每次按鍵被按下都會被呼叫,可用來判斷按鍵
 
        public static int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam) 
        {
            const int WM_KEYDOWN = 0x100;
 
            KBDLLHOOKSTRUCT kbd = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
            Keys vkCode = (Keys)kbd.vkCode;
 
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) //Key Pressed
            {
                if (vkCode == Keys.PrintScreen)
                {
                    IsPrintScreenPressed = true;               
                }
            }
            return CallNextHookEx(m_HookHandle, nCode, wParam, lParam);
        }
 
 
為了避免重複執行,主程式Form_Load事件中先判斷是否已經有proc執行,然後才跟keyboard掛勾
 
        private void PrintScreenForm_Load(object sender, EventArgs e)
        {
            // 判斷是否已有程式在執行
            string proc = Process.GetCurrentProcess().ProcessName;
            Process[] processes = Process.GetProcessesByName(proc);
            if (processes.Length > 1)
            {
                MessageBox.Show("PrintScreen " + proc + " 已經在執行", "重複執行錯誤", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                Close();
            }
            LoadConfig();
            KeyHookHandler.Hook();
            timerKeyboard.Enabled = true;
        } //PrintScreenForm_Load()
 
 
本程式並非在按下PrintScreen後產生call back 回主程式呼叫,而是用計時器去檢查按鍵PrintScreen否被按下,是的話就呼叫ScreenCapture()函數:
        private void timerKeyboard_Tick(object sender, EventArgs e)
        {
            if (KeyHooker.IsPrintScreenPressed) //有鍵盤輸入
            {
                ScreenCapture();
                KeyHooker.IsPrintScreenPressed = false;
            }
        } //imerKeyboard_Tick()
 
 
ScreenCapture() 會抓取所有螢幕畫面存檔如果有2個螢幕亦可同時抓取。這裡要注意的是,如果螢幕在休眠中,要先打開螢幕,否則畫面不會更新。
        private void ScreenCapture()
        {
            int MonitorIndex = 0; //default monitor number
 
            // Turn on screen before capture to enable buffer refresh
            SetMonitorState(MonitorState.ON);
            Thread.Sleep(100); //wait monitor on
 
            using (Bitmap bmpScreenCapture = new Bitmap(Screen.AllScreens[MonitorIndex].Bounds.Width,
                                            Screen.AllScreens[MonitorIndex].Bounds.Height))
            {
                using (Graphics g = Graphics.FromImage(bmpScreenCapture))
                {
                    g.CopyFromScreen(Screen.AllScreens[MonitorIndex].Bounds.X,
                                     Screen.AllScreens[MonitorIndex].Bounds.Y,
                                     0, 0,
                                     bmpScreenCapture.Size,
                                     CopyPixelOperation.SourceCopy);
                }
                BuildPath();
                String FileName = "PrintScreen" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".jpg";
                String BmpFileName = FilePath + "\\"+ FileName;
                bmpScreenCapture.Save(BmpFileName);
 
                if (chkSound.Checked)
                {
                    SoundPlayer simpleSound = new SoundPlayer(@"shutter.wav");
                    simpleSound.Play();
                }
 
                //if (chkBalloonTips.Checked && this.WindowState == FormWindowState.Minimized)
                {
                    notifyIcon.BalloonTipText = FileName;
                    notifyIcon.ShowBalloonTip(200);
                }
            }
        } //ScreenCapture()
 
打開螢幕的方法是送一個命令代碼給作業系統這是從很久以前Win32Message Queue沿襲而來的程式架構,為了使用SendMessage(),在C#中需使用DllImport來引用非C#語言寫的系統user32.dll
 
        private int SC_MONITORPOWER = 0xF170;
        private uint WM_SYSCOMMAND = 0x0112;
 
        [DllImport("user32.dll")]
        static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
 
        private void SetMonitorState(MonitorState state)
        {
            SendMessage(this.FindForm().Handle, WM_SYSCOMMAND, (IntPtr)SC_MONITORPOWER, (IntPtr)state);
        } //SetMonitorState(
 
 
畫面縮小到圖示與回復,主程式要使用Form_Resize() 事件NotifyIcon則需使用MouseDoubleClick() 事件
       private void PrintScreenForm_Resize(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                this.ShowInTaskbar = false;
                notifyIcon.Visible = true;
                notifyIcon.BalloonTipText = "Print Screen";
                notifyIcon.ShowBalloonTip(300);
                this.Hide();
            }
        } //PrintScreenForm_Resize()
 
        private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            this.WindowState = FormWindowState.Normal;
            this.ShowInTaskbar = true;
            notifyIcon.Visible = false;
            this.Show();
            //this.BringToFront();
        } //notifyIcon_MouseDoubleClick()
 
 
將使用者設定存檔每次程式執行時自動載入上次的設定設定檔PrintScreen.cfg的內容
 
RootPath=C:\Users\ghosty\Desktop
BalloonTips=True
Sound=True
 
Config的載入與存檔
        void LoadConfig()
        {
            if (!File.Exists(ConfigFileName))
            {
                // Config not exists
                RootPath = Environment.GetEnvironmentVariable("HOMEDRIVE")
                            + Environment.GetEnvironmentVariable("HOMEPATH")
                            + @"\ScreenCapture";
                SaveConfig();
            }
            else
            {
                StreamReader ConfigFileStream = new StreamReader(ConfigFileName);
 
                String Line;
                while ((Line = ConfigFileStream.ReadLine()) != null)
                {
                    if (Line[0] == '#')
                    {
                        //skip comment line
                    }
                    else if (Line.Contains("RootPath="))
                    {
                        RootPath = Line.Substring("RootPath=".Length);
                    }
                    else if (Line.Contains("BalloonTips="))
                    {
                        chkBalloonTips.Checked = Convert.ToBoolean( Line.Substring("BalloonTips=".Length));
                    }
                    else if (Line.Contains("Sound="))
                    {
                        chkSound.Checked = Convert.ToBoolean(Line.Substring("Sound=".Length));
                    }
                }
                ConfigFileStream.Close();
            }
            tbFilePath.Text = RootPath;
        } // LoadConfig()
 
        void SaveConfig()
        {
            StreamWriter ConfigFileStream = new StreamWriter(ConfigFileName);
 
            ConfigFileStream.WriteLine("RootPath=" + RootPath);
            ConfigFileStream.WriteLine("BalloonTips=" + chkBalloonTips.Checked);
            ConfigFileStream.WriteLine("Sound=" + chkSound.Checked);
 
            ConfigFileStream.Close();
        } //SaveConfig()
 
 
剩下的就是UI元件的事件處理
        private void btnBrowse_Click(object sender, EventArgs e)
        {
            folderBrowserDialog.ShowDialog();
            RootPath = tbFilePath.Text = folderBrowserDialog.SelectedPath;
            SaveConfig();
        } //btnBrowse_Click()
 
        private void chkBalloonTips_Click(object sender, EventArgs e)
        {
            SaveConfig();
        } //chkBalloonTips_Click()
 
        private void chkSound_Click(object sender, EventArgs e)
        {
            SaveConfig();
        } //chkSound_Click()
 
 
 
 
參考資料
[1] “C# 全域鍵盤掛鉤(Global Keyboard Hook)範例”, http://www.dotblogs.com.tw/huanlin/archive/2008/04/23/3320.aspx
arrow
arrow
    文章標籤
    c# Print Screen
    全站熱搜

    ghostyguo 發表在 痞客邦 留言(0) 人氣()