C# WPF 粘贴板记录器

2021-07-03 07:06

阅读:693

标签:tab   led   设置   好的   sage   错误   txt   files   remove   

     工作学习中需要搜索很多资料,有建立文档对遇到过的问题进行记录,但是一来麻烦,二来有些当时认为不重要的事情,也许一段时间后认为是重要的,需要记录的,却又一时找不到,浪费时间做重复的事情。正好借着这个机会,学习一些C#的东西,做一个粘贴板记录器,用来记录所有赋值过的东西。

  做法很简单,设置log文件,启动记录,然后从粘贴板中获得数据,将其格式化后存储起来。最终得到的这个东西虽然够用,但是还是有很多需要提高的地方,比如1,记录所有的数据,后期需要人工清除。2,需要手动启动程序,如果能做成后台服务的话也许会更好。3.没做富文本的支持。4.代码结构比较简单。不管怎么说,总算完成了,过程中遇到很多问题,也学到了不少知识。

  最终成品的样子:

 

   技术分享图片

 

技术分享图片

 

退出后log文件内容如下:

【2018-11-12 20:43:05】
  this.StartRecordFlag = false;

【2018-11-12 20:43:18】


【2018-11-12 20:43:49】
        private void Stop_Click(object sender, RoutedEventArgs e)
        {
            //点击stop后停止记录,此时只有start exit set可选
            this.Stop.IsEnabled = false;
            this.Start.IsEnabled = true;
            this.Exit.IsEnabled = true;
            this.Set.IsEnabled = true;
            this.StartRecordFlag = false;

        }

【2018-11-12 20:44:03】

  

其中两处空白是复制图片的内容,能够通过RichTextbox显示富文本内容,但是没找到好的写入到文件的方式,暂时没做。

提供了四个按钮,分别提供开始/停止/退出/设置功能,其中启动后只有退出/设置按钮可用,设置文件后开始按钮可用,点击开始后只有停止按钮可用。按钮的灰选利用isEnabled属性设置。虽然只有几个按钮,相互之间关系比较简单,可以直接设置,不过想到另一种更通用的方式,比如设计一个表格,分别对应状态可该状态下的按钮情况,比如:

  start stop exit set
初始状态 0(不可用) 0 1(可用) 1
设置完成 1 0 1 1
开始 0 1 0 0
停止 1 0 1 1

则只需要根据当前所处状态更新按钮状态就可以了,即直观明了,又可以避免某些按钮遗漏处理导致状态错误。

 

set调用了系统的Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();来获得一个文件名,通过设置Filter进行过滤,如果可选多个文件类型,通过;隔开。

 

数据通过Clipboard获得,目前仅仅处理文本,所以通过(string)iData.GetData(System.Windows.DataFormats.Text)得到数据内容。将其显示到一个label里面,同时写到文件即可。

花费精力比较多的是如何实时监控粘贴板的变化,搜索到的资料大多是WinForm的,而WPF中没有WndProc、WM_CLIPBOARDUPDATE,WSInitialized等,可能是WPF中采用了不同的方式,最终通过参考https://blog.csdn.net/shalyf/article/details/8854591? 中的方法,在OnSourceInitialized函数中通过AddHook添加处理函数,在处理函数里面监控粘贴板。虽然实现了目的,还是很多东西没搞懂,比如OnSourceInitialized,看名字是在资源初始化时候调用,但具体哪些资源呢?HwndSource hs = PresentationSource.FromVisual(this) as HwndSource;是做什么的?AddHook看起来是个类似注册函数的东西,具体机制实现是什么呢?对里面函数的要求是什么?msg的可取范围有哪些?参考的例子中0x219是用作WM_DEVICECHANGE,哪一个是用作粘贴板内容更新的呢?最终也没找到对应的值(又看到一些0x031D的,不过都是winForm下的例子),最终采取方式为直接内容后和上一次对比,如果有差异就说明改变了,目前只能这样变通了,不知道效率上是否会有不好的影响。

  完成后总结其实挺简单的,做的过程中大多是时间花在学习上了,之前做的大多是控制台命令行程序,在windows编程上没有系统的学习过,只能头痛医头脚痛医脚,效率太低了,抽时间得好好补一补窗口应用程序的开发了。

 

 

完整代码如下:

界面code:


        
        
        
        

    

  

CS code:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CSharp_ClipBoardRecorder
{


    /// 
    /// MainWindow.xaml 的交互逻辑
    /// 
    public partial class MainWindow : Window
    {
        //参考:
        //https://blog.csdn.net/shalyf/article/details/8854591  消息处理
        string FileName = "";  //log文件名
        FileStream FileReader = null; //为了防止出现意外,在设置选择文件的时候就打开占用log文件        
        string PreContent = "";  //用作优化,如果和上一次一样,则不记录        
        bool StartRecordFlag = false;
        string NextLine = "\n";
        //初始加载窗体,这个时候只有Set/Exit 按钮可用
        public MainWindow()
        {
            InitializeComponent();            
            this.Start.IsEnabled = false;
            this.Stop.IsEnabled = false;
            //string abc = "测试中文字符";
            //this.Content.Content = abc;
        }

        //设置log文件,如果没有选择合适的文件询问是否重新选择或者退出
        private void Set_Click(object sender, RoutedEventArgs e)
        {
            Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();
            dialog.Multiselect = false;
            dialog.Title = "请选择文件:";
            dialog.Filter = "文本文件(*.log;*.txt)| *.log;*.txt"; //log文件后缀可选 log/txt
            try
            {
                if (dialog.ShowDialog() == true)
                {
                    if (this.FileReader != null)
                    {
                        this.FileReader.Close(); //如果打开过文件,解除对上次选择文件的占用,用于重新设置log文件
                    }
                    this.FileName = dialog.FileName;
                    this.FileReader = new FileStream(this.FileName, FileMode.Append, FileAccess.Write);
                    //如果设定了log文件,start可选
                    this.Start.IsEnabled = true;
                }
                else
                {
                    MessageBoxResult mr = System.Windows.MessageBox.Show("未选择合适文件,请重新选择或退出!\nYes 重新选择,No 退出", "Warring", MessageBoxButton.YesNo);
                    if (mr == MessageBoxResult.No)
                    {
                        Environment.Exit(0);
                    }
                    this.Set_Click(sender, e);
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }

        }


        //开始按钮
        //按下开始按钮后,就只有stop按钮可用
        //开始监听clipboard,并将数据格式化后存储到log文件中
        private void Start_Click(object sender, RoutedEventArgs e)
        {
            this.Start.IsEnabled = false;
            this.Exit.IsEnabled = false;
            this.Stop.IsEnabled = true;
            this.Set.IsEnabled = false;
            this.StartRecordFlag = true;
        }
   
        private void Stop_Click(object sender, RoutedEventArgs e)
        {
            //点击stop后停止记录,此时只有start exit set可选
            this.Stop.IsEnabled = false;
            this.Start.IsEnabled = true;
            this.Exit.IsEnabled = true;
            this.Set.IsEnabled = true;
            this.StartRecordFlag = false;

        }

        private void Exit_Click(object sender, RoutedEventArgs e)
        {
            if (this.FileReader != null)
            {
                this.FileReader.Close();
            }
            //RemoveClipboardFormatListener(this.CurrentWindowHander);
            Environment.Exit(0);
        }

 
        private string GetStringData()
        {
            string ret = "";
            System.Windows.IDataObject iData = System.Windows.Clipboard.GetDataObject();
            if (iData.GetDataPresent(System.Windows.DataFormats.Text))
            {
                ret = (string)iData.GetData(System.Windows.DataFormats.Text);
            }
            return ret;
        }
        //复制内容格式化,格式为
        /*
        【时间】        
        XXXX
         */

        private string FormatString(string info)
        {
            string ret = "";
            DateTime current = System.DateTime.Now;
            ret ="【"+ current.Year.ToString("d4") + "-" + current.Month.ToString("d2") + "-" + current.AddHours(-24).Date.ToString("d2") + " " + current.Hour.ToString("d2") + ":" + current.Minute.ToString("d2") + ":" + current.Second.ToString("d2")+"】"+this.NextLine;
            ret += info+this.NextLine + this.NextLine;
            return ret;
        }
        string GetTimeString()
        {
            string time="";

            return time;
        }
        private void ShowInText(string content)
        {
            this.Content.Content = content;
        }

        protected override void OnSourceInitialized(EventArgs e)
        {

            base.OnSourceInitialized(e);
            HwndSource hs = PresentationSource.FromVisual(this) as HwndSource;            
            hs.AddHook(WndProc);            
        }
        //https://blog.csdn.net/xlm289348/article/details/8050957
        IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            try
            {
                string tmp = GetStringData();
                if (tmp != this.PreContent && this.StartRecordFlag == true)
                //if (tmp != this.PreContent)
                {
                    this.PreContent = tmp;
                    tmp = FormatString(tmp);
                    this.ShowInText(tmp);
                    //https://blog.csdn.net/zhuyu19911016520/article/details/46502857
                    StreamWriter tmpSW = new StreamWriter(this.FileReader,Encoding.UTF8);                    
                    tmpSW.Write(tmp);
                    tmpSW.Flush();  //测试过直接点X关闭,数据也能写入文件                    
                }
            }
            catch (Exception ex)
            {

            }
            finally
            {
                
            }
            return IntPtr.Zero;
        }
    }
}

  

 

C# WPF 粘贴板记录器

标签:tab   led   设置   好的   sage   错误   txt   files   remove   

原文地址:https://www.cnblogs.com/youdias/p/9898498.html


评论


亲,登录后才可以留言!