C# WPF 粘贴板记录器
工作学习中需要搜索很多资料,有建立文档对遇到过的问题进行记录,但是一来麻烦,二来有些当时认为不重要的事情,也许一段时间后认为是重要的,需要记录的,却又一时找不到,浪费时间做重复的事情。正好借着这个机会,学习一些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:
<Window x:Class="CSharp_ClipBoardRecorder.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CSharp_ClipBoardRecorder"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button x:Name="Start" Content="Start" HorizontalAlignment="Left" Height="40" Margin="54,10,0,0" VerticalAlignment="Top" Width="80" Click="Start_Click"/>
<Button x:Name="Stop" Content="Stop" HorizontalAlignment="Left" Height="40" Margin="153,10,0,0" VerticalAlignment="Top" Width="80" Click="Stop_Click"/>
<Button x:Name="Exit" Content="Exit" HorizontalAlignment="Left" Height="40" Margin="262,10,0,0" VerticalAlignment="Top" Width="80" RenderTransformOrigin="0.177,0.583" Click="Exit_Click"/>
<Label x:Name="Content" Content="" HorizontalAlignment="Left" Height="213" Margin="10,78,0,0" VerticalAlignment="Top" Width="481"/>
<Button x:Name="Set" Content="Set" HorizontalAlignment="Left" Height="40" Margin="383,10,0,0" VerticalAlignment="Top" Width="80" RenderTransformOrigin="0.177,0.583" Click="Set_Click"/> </Grid>
</Window>
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
{ /// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
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 粘贴板记录器的更多相关文章
- Ubuntu Vim 复制到系统粘贴板
/************************************************************************* * Ubuntu Vim 复制到系统粘贴板 * 说 ...
- AX 利用windows粘贴板功能实现批量数据快速导出EXCEL
static void test(Args _args) { int lineNum; int titleLines; SysExcelApplication excel; SysExcelWorkb ...
- 如何拷贝CMD命令行文本到粘贴板
/********************************************************************* * 如何拷贝CMD命令行文本到粘贴板 * To copy ...
- tmux复制到windows剪贴板/粘贴板的坑
以下所有操作都是在windows下面用putty连接linux centos6的情景下. 一直很纳闷为什么在tmux模式下不能把复制到的文字放到系统的粘贴板里面呢?通过层层阻碍,终于找到了原因. 去掉 ...
- IOS 访问系统粘贴板
粘贴板提供了一种核心OS特性,用于跨应用程序共享数据.用户可以跨应用来复制粘贴,也可以设置只在本应用中复制粘贴用来保护隐私. UIPasteboard类允许访问共享的设备粘贴板以及内容,下面代码返回一 ...
- 转JS--通过按钮直接把input或者textarea里的值复制到粘贴板里
document.activeElement属性为HTML 5中新增的document对象的一个属性,该属性用于返回光标所在元素.当光标未落在页面中任何元素内时,属性值返回body元素. setSel ...
- windows粘贴板操作-自己的应用和windows右键互动
一.粘贴板操作函数 BOOL OpenClipboard(HWND hWnd);参数 hWnd 是打开剪贴板的窗口句柄,成功返回TRUE,失败返回FALSE BOOL CloseClipboard() ...
- clipboardjs复制到粘贴板
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat=&qu ...
- js实现复制内容到粘贴板
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...
随机推荐
- Java基础笔记(四)——命名规则、数据类型
标识符即Java程序中需要自定义的名称,如变量名.方法名.类名.包名.工程名等. 标识符的命名规则: 1.可由字母.数字.下划线(_)和美元符($)组成,不能以数字开头. 2.严格区分大小写. 3.不 ...
- Web 加入favicon
一.点击 制作自己的favicon图标; 二.在网页head中加入: <link rel="shortcut icon" href="favicon.ico& ...
- linux 03 命令 续
linux 03 命令 续 一.vim 两种操作方式:新文件 pyvip@Vip:~/demo/2_3$ vim demo.txt #操作一个新文件 一开始进入的是命令模式,按i进入插入模式,开始编辑 ...
- 洛谷1373(dp)
常规线性dp,需要时就加一维.\(dp[i][j][t][s]\)表示在点\((i,j)\)时瓶子里剩\(t\)且为\(s\)走(0代表小a,1代表uim)时的方案数. de了半天发现是初次尝试的快速 ...
- Cent OS 6.5 下 Node.js安装
打开官网 http://nodejs.org/ 点击那个绿色的INSTALL 按钮下载安装包,然后解压. 基本的环境我原本已经安装完毕,这是需求的环境,来源安装包中的README.md,需要的自行 ...
- 053 Maximum Subarray 最大子序和
给定一个序列(至少含有 1 个数),从该序列中寻找一个连续的子序列,使得子序列的和最大.例如,给定序列 [-2,1,-3,4,-1,2,1,-5,4],连续子序列 [4,-1,2,1] 的和最大,为 ...
- B - Median Pyramid Easy 构造题
B - Median Pyramid Easy Time limit : 2sec / Memory limit : 256MB Score : 400 points Problem Statemen ...
- java es 骤合操作
ElasticSearch java API - 聚合查询 以球员信息为例,player索引的player type包含5个字段,姓名,年龄,薪水,球队,场上位置.index的mapping为: &q ...
- <Android Framework 之路>多线程
多线程编程 JAVA多线程方式 1. 继承Thread线程,实现run方法 2. 实现Runnable接口 JAVA单继承性,当我们想将一个已经继承了其他类的子类放到Thread中时,单继承的局限就体 ...
- cocos2d-x入门学习篇;切换场景
手机游戏开发最近很火爆,鉴于一直在学习c++,看起来上手就比较快了.这篇文章来自皂荚花 cocos2d-x技术,我把我的想法分享给大家. 首先来看一段代码: CCScene* HelloWorld:: ...