C# 如何获取已更新剪贴板的应用程序的进程ID或名称?
我正在用C#创建一个剪贴板管理器,有时我会感觉到剪贴板被某个应用程序设置为空 例如,在Excel中,当取消选择刚复制的内容时会发生这种情况,因此我需要确定剪贴板是否为空,但如何获取更新剪贴板的应用程序名 我希望我能以某种方式获得更新剪贴板的应用程序的C# 如何获取已更新剪贴板的应用程序的进程ID或名称?,c#,.net,winforms,process,clipboard,C#,.net,Winforms,Process,Clipboard,我正在用C#创建一个剪贴板管理器,有时我会感觉到剪贴板被某个应用程序设置为空 例如,在Excel中,当取消选择刚复制的内容时会发生这种情况,因此我需要确定剪贴板是否为空,但如何获取更新剪贴板的应用程序名 我希望我能以某种方式获得更新剪贴板的应用程序的HWnd句柄,这样我就可以用以下代码查找其背后的流程: [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThrea
HWnd
句柄,这样我就可以用以下代码查找其背后的流程:
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
...
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_CLIPBOARDUPDATE:
// How to get the "handle" HWnd?
IntPtr handle = ??? <============= HOW TO GET THIS ONE ???
// Get the process ID from the HWnd
uint processId = 0;
GetWindowThreadProcessId(handle, out processId);
// Get the process name from the process ID
string processName = Process.GetProcessById((int)processId).ProcessName;
Console.WriteLine("Clipboard UPDATE event from [" + processName + "]");
break;
}
default:
base.WndProc(ref m);
break;
}
}
您可以调用以获取上次设置或清除剪贴板的窗口的句柄(触发通知的操作)
[…]通常,剪贴板所有者是最后一次将数据放入剪贴板的窗口。EmptyClipboard函数分配剪贴板所有权 在某些特殊情况下,进程将空句柄传递给:读取此函数和函数的备注部分 在调用EmptyClipboard之前,应用程序必须打开剪贴板 通过使用OpenClipboard函数。如果应用程序指定 空窗口句柄打开剪贴板时,EmptyClipboard成功 但将剪贴板所有者设置为空。请注意,这会导致 将ClipboardData设置为失败
► 这里我使用一个派生类来设置剪贴板侦听器。处理剪贴板更新消息的窗口是在初始化对象并将此参数传递给方法时创建的,以创建一个不可见窗口。
然后覆盖初始化的NativeWindow的
WndProc
,以接收通知
该函数用于将窗口放置在系统剪贴板侦听器链中
► 当收到剪贴板通知时,clipboadupdatemonitor
类生成一个事件。事件中传递的自定义ClipboardChangedEventArgs
对象包含由GetClipboardOwner()
返回的剪贴板所有者句柄、由返回的ThreadId
和ProcessId
以及由标识的进程名称
您可以像这样设置一个clipboadupdatemonitor
对象:此类也可以在
Program.cs
NativeMethods
class:
internal static class NativeMethods
{
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AddClipboardFormatListener(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool RemoveClipboardFormatListener(IntPtr hwnd);
[DllImport("user32.dll")]
internal static extern IntPtr GetClipboardOwner();
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
internal const int WM_CLIPBOARDUPDATE = 0x031D;
internal const int WS_CLIPCHILDREN = 0x02000000;
internal const int WS_EX_TOOLWINDOW = 0x00000080;
internal const int WS_EX_CONTROLPARENT = 0x00010000;
}
微软的sysmon显然可以做到这一点()如果你只需要知道,对于调试…对于调试,这可能是好的,但我需要它在我的应用程序中,因为我应该做一些行动,如果剪贴板变空。例如,当您在Excel中复制某些内容,然后取消选择时,会发生这种情况,然后清空剪贴板。我自己的应用程序有时也会出于某种原因清空剪贴板,当它清空剪贴板时,我需要恢复剪贴板-但我需要知道哪个应用程序会这样做。
private ClipboardUpdateMonitor clipboardMonitor = null;
// [...]
clipboardMonitor = new ClipboardUpdateMonitor();
clipboardMonitor.ClipboardChangedNotify += this.ClipboardChanged;
// [...]
private void ClipboardChanged(object sender, ClipboardChangedEventArgs e)
{
Console.WriteLine(e.ProcessId);
Console.WriteLine(e.ProcessName);
Console.WriteLine(e.ThreadId);
}
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;
public sealed class ClipboardUpdateMonitor : IDisposable
{
private bool isDisposed = false;
private static ClipboardWindow window = null;
public event EventHandler<ClipboardChangedEventArgs> ClipboardChangedNotify;
public ClipboardUpdateMonitor()
{
window = new ClipboardWindow();
if (!NativeMethods.AddClipboardFormatListener(window.Handle)) {
throw new TypeInitializationException(nameof(ClipboardWindow),
new Exception("ClipboardFormatListener could not be initialized"));
}
window.ClipboardChanged += ClipboardChangedEvent;
}
private void ClipboardChangedEvent(object sender, ClipboardChangedEventArgs e)
=> ClipboardChangedNotify?.Invoke(this, e);
public void Dispose()
{
if (!isDisposed) {
// Cannot allow to throw exceptions here: add more checks to verify that
// the NativeWindow still exists and its handle is a valid handle
NativeMethods.RemoveClipboardFormatListener(window.Handle);
window?.DestroyHandle();
isDisposed = true;
}
}
~ClipboardUpdateMonitor() => Dispose();
private class ClipboardWindow : NativeWindow
{
public event EventHandler<ClipboardChangedEventArgs> ClipboardChanged;
public ClipboardWindow() {
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
var cp = new CreateParams();
cp.Caption = "ClipboardWindow";
cp.Height = 100;
cp.Width = 100;
cp.Parent = IntPtr.Zero;
cp.Style = NativeMethods.WS_CLIPCHILDREN;
cp.ExStyle = NativeMethods.WS_EX_CONTROLPARENT | NativeMethods.WS_EX_TOOLWINDOW;
this.CreateHandle(cp);
}
protected override void WndProc(ref Message m)
{
switch (m.Msg) {
case NativeMethods.WM_CLIPBOARDUPDATE:
IntPtr owner = NativeMethods.GetClipboardOwner();
var threadId = NativeMethods.GetWindowThreadProcessId(owner, out uint processId);
string processName = string.Empty;
if (processId != 0) {
using (var proc = Process.GetProcessById((int)processId)) {
processName = proc?.ProcessName;
}
}
ClipboardChanged?.Invoke(null, new ClipboardChangedEventArgs(processId, processName, threadId));
m.Result = IntPtr.Zero;
break;
default:
base.WndProc(ref m);
break;
}
}
}
}
public class ClipboardChangedEventArgs : EventArgs
{
public ClipboardChangedEventArgs(uint processId, string processName, uint threadId)
{
this.ProcessId = processId;
this.ProcessName = processName;
this.ThreadId = threadId;
}
public uint ProcessId { get; }
public string ProcessName { get; }
public uint ThreadId { get; }
}
internal static class NativeMethods
{
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AddClipboardFormatListener(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool RemoveClipboardFormatListener(IntPtr hwnd);
[DllImport("user32.dll")]
internal static extern IntPtr GetClipboardOwner();
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
internal const int WM_CLIPBOARDUPDATE = 0x031D;
internal const int WS_CLIPCHILDREN = 0x02000000;
internal const int WS_EX_TOOLWINDOW = 0x00000080;
internal const int WS_EX_CONTROLPARENT = 0x00010000;
}