复制剪切监控自动对字符串Trim去除空白字符串
一、需求背景:
浏览器有些页面没有对文本输入框内容的前后空白字符做Trim处理,避免操作时不注意复制剪切文本有空白字符,能够后台监控自动处理掉。
二、剪贴板知识点:
监控函数,如、绑定:
setClipboardViewer 函数 (winuser.h) - Win32 apps | Microsoft Learn
事件值,如、复制/剪切:
WM_DRAWCLIPBOARD消息 (Winuser.h) - Win32 apps | Microsoft Learn
三、实现:
window下监控粘贴板事件:
public class ClipboardHelper
{
public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data);
public static event OnClipboardChangeEventHandler OnClipboardChange;
public static void Start()
{
ClipboardWatcher.Start();
ClipboardWatcher.OnClipboardChange += (ClipboardFormat format, object data) =>
{
if (OnClipboardChange != null)
OnClipboardChange(format, data);
};
}
public static void Stop()
{
// 修改点:不要置空,方便可以启动/停止来回切换。
// OnClipboardChange = null;
ClipboardWatcher.Stop();
}
private class ClipboardWatcher : Form
{
// static instance of this form
private static ClipboardWatcher mInstance;
// needed to dispose this form
static IntPtr nextClipboardViewer;
public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data);
public static event OnClipboardChangeEventHandler OnClipboardChange;
// start listening
public static void Start()
{
// we can only have one instance if this class
if (mInstance != null)
return;
var t = new Thread(new ParameterizedThreadStart(x => Application.Run(new ClipboardWatcher())));
t.SetApartmentState(ApartmentState.STA); // give the [STAThread] attribute
t.Start();
}
// stop listening (dispose form)
public static void Stop()
{
mInstance.Invoke(new MethodInvoker(() =>
{
ChangeClipboardChain(mInstance.Handle, nextClipboardViewer);
}));
mInstance.Invoke(new MethodInvoker(mInstance.Close));
mInstance.Dispose();
mInstance = null;
}
// on load: (hide this window)
protected override void SetVisibleCore(bool value)
{
CreateHandle();
mInstance = this;
nextClipboardViewer = SetClipboardViewer(mInstance.Handle);
base.SetVisibleCore(false);
}
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
// defined in winuser.h
const int WM_DRAWCLIPBOARD = 0x308;
const int WM_CHANGECBCHAIN = 0x030D;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_DRAWCLIPBOARD:
ClipChanged();
SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
break;
case WM_CHANGECBCHAIN:
if (m.WParam == nextClipboardViewer)
nextClipboardViewer = m.LParam;
else
SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
break;
default:
base.WndProc(ref m);
break;
}
}
static readonly string[] formats = Enum.GetNames(typeof(ClipboardFormat));
private void ClipChanged()
{
IDataObject iData = Clipboard.GetDataObject();
ClipboardFormat? format = null;
foreach (var f in formats)
{
if (iData.GetDataPresent(f))
{
format = (ClipboardFormat)Enum.Parse(typeof(ClipboardFormat), f);
break;
}
}
object data = iData.GetData(format.ToString());
if (data == null || format == null)
return;
if (OnClipboardChange != null)
OnClipboardChange((ClipboardFormat)format, data);
}
}
}
public enum ClipboardFormat : byte
{
/// <summary>Specifies the standard ANSI text format. This static field is read-only.
/// </summary>
/// <filterpriority>1</filterpriority>
Text,
/// <summary>Specifies the standard Windows Unicode text format. This static field
/// is read-only.</summary>
/// <filterpriority>1</filterpriority>
UnicodeText,
/// <summary>Specifies the Windows device-independent bitmap (DIB) format. This static
/// field is read-only.</summary>
/// <filterpriority>1</filterpriority>
Dib,
/// <summary>Specifies a Windows bitmap format. This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
Bitmap,
/// <summary>Specifies the Windows enhanced metafile format. This static field is
/// read-only.</summary>
/// <filterpriority>1</filterpriority>
EnhancedMetafile,
/// <summary>Specifies the Windows metafile format, which Windows Forms does not
/// directly use. This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
MetafilePict,
/// <summary>Specifies the Windows symbolic link format, which Windows Forms does
/// not directly use. This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
SymbolicLink,
/// <summary>Specifies the Windows Data Interchange Format (DIF), which Windows Forms
/// does not directly use. This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
Dif,
/// <summary>Specifies the Tagged Image File Format (TIFF), which Windows Forms does
/// not directly use. This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
Tiff,
/// <summary>Specifies the standard Windows original equipment manufacturer (OEM)
/// text format. This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
OemText,
/// <summary>Specifies the Windows palette format. This static field is read-only.
/// </summary>
/// <filterpriority>1</filterpriority>
Palette,
/// <summary>Specifies the Windows pen data format, which consists of pen strokes
/// for handwriting software, Windows Forms does not use this format. This static
/// field is read-only.</summary>
/// <filterpriority>1</filterpriority>
PenData,
/// <summary>Specifies the Resource Interchange File Format (RIFF) audio format,
/// which Windows Forms does not directly use. This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
Riff,
/// <summary>Specifies the wave audio format, which Windows Forms does not directly
/// use. This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
WaveAudio,
/// <summary>Specifies the Windows file drop format, which Windows Forms does not
/// directly use. This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
FileDrop,
/// <summary>Specifies the Windows culture format, which Windows Forms does not directly
/// use. This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
Locale,
/// <summary>Specifies text consisting of HTML data. This static field is read-only.
/// </summary>
/// <filterpriority>1</filterpriority>
Html,
/// <summary>Specifies text consisting of Rich Text Format (RTF) data. This static
/// field is read-only.</summary>
/// <filterpriority>1</filterpriority>
Rtf,
/// <summary>Specifies a comma-separated value (CSV) format, which is a common interchange
/// format used by spreadsheets. This format is not used directly by Windows Forms.
/// This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
CommaSeparatedValue,
/// <summary>Specifies the Windows Forms string class format, which Windows Forms
/// uses to store string objects. This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
StringFormat,
/// <summary>Specifies a format that encapsulates any type of Windows Forms object.
/// This static field is read-only.</summary>
/// <filterpriority>1</filterpriority>
Serializable,
}
参考文章:https://www.codenong.com/621577/
原理注解:
ClipboardFormat枚举 参照 System.Windows.Forms.DataFormats
线程启动应用窗口不可视化,通过WndProc监控事件Clipboard 判断粘贴板内容类型,委托事件回调。
启动是应用窗口、绑定SetClipboardViewer监控;停止时关闭窗口、解绑ChangeClipboardChain监控;监控对象调整WM_CHANGECBCHAIN时,调整下传句柄nextClipboardViewer,SendMessage保证下传对象能够收到消息。
监控事件,通过Clipboard.SetText(trim) 替换文本
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ClipboardHelper.OnClipboardChange += ClipboardMonitor_OnClipboardChange;
ClipboardHelper.Start();
//最小化主窗口
this.WindowState = FormWindowState.Minimized;
//不在系统任务栏显示主窗口图标
this.ShowInTaskbar = false;
}
private void ClipboardMonitor_OnClipboardChange(ClipboardFormat format, object data)
{
switch (format)
{
case ClipboardFormat.Text:
case ClipboardFormat.UnicodeText:
var str = data as string;
if (str != null)
{
var trim = str.Trim();
if (str.Length != trim.Length)
{
Clipboard.SetText(trim);
//this.notifyIcon1.ShowBalloonTip(1, string.Empty, "已去除空白字符", ToolTipIcon.Info);
}
}
break;
}
}
private bool isOpen = true;
private void StateToolStripMenuItem_Click(object sender, EventArgs e)
{
if (isOpen)
{
isOpen = false;
this.StateToolStripMenuItem.Text = "启用";
ClipboardHelper.Stop();
}
else
{
isOpen = true;
this.StateToolStripMenuItem.Text = "禁用";
ClipboardHelper.Start();
}
}
}