文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

如何获取更新剪贴板的应用程序的进程ID或名称?

2024-02-12 14:53

关注

问题内容

我正在用 c# 创建剪贴板管理器,有时我会遇到剪贴板被某些应用程序设置为空的情况。

这发生在例如excel 取消选择刚刚复制的内容时,因此我需要确定剪贴板是否为空,但是如何获取更新剪贴板的应用程序名称

我希望我能以某种方式获得更新剪贴板的应用程序的 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;
    }
}

我希望我可以使用 message 对象中的 hwnd ,但这似乎是我自己的应用程序 - 可能是用此进程 id 通知应用程序:

如果我可以通过其他方式获得它,那么这当然也完全没问题,但我将不胜感激对此的任何见解:-)

解决方案

根据@jimi的回答,这很简单。我可以将以下 3 行添加到我的原始代码中:

// Import the "GetClipboardOwner" function from the User32 library
[DllImport("user32.dll")]
public static extern IntPtr GetClipboardOwner();
...

// Replace the original line with "HOW TO GET THIS ONE" with this line below - this will give the HWnd handle for the application that has changed the clipboard:
IntPtr handle = GetClipboardOwner();

解决方法

您可以调用getclipboardowner()< /a> 获取上次设置或清除剪贴板(触发通知的操作)的窗口句柄。

[...] 一般来说,剪贴板所有者是最后将数据放入剪贴板的窗口。
emptyclipboard 函数分配剪贴板所有权。

在某些特殊情况下,进程会将空句柄传递给 openclipboard():阅读此函数的备注部分和 emptyclipboard 函数。

在调用 emptyclipboard 之前,应用程序必须打开剪贴板 通过使用 openclipboard 函数。如果应用程序指定 打开剪贴板时窗口句柄为null,emptyclipboard成功 但将剪贴板所有者设置为 null。请注意,这会导致 setclipboarddata 失败。

▶ 这里我使用的是 nativewindow< /a> 派生类来设置剪贴板侦听器。处理剪贴板更新消息的窗口是通过初始化 创建的createparams 对象并将此参数传递给 nativewindow.createhandle(createparams) 方法,用于创建一个不可见窗口。
然后重写初始化的nativewindow的wndproc,接收 wm_clipboardupdate
通知。

addclipboardformatlistener 函数用于将窗口放置在系统剪贴板侦听器链中。

clipboardupdatemonitor 类在收到剪贴板通知时生成一个事件。事件中传递的自定义 clipboardchangedeventargs 对象包含由 getclipboardowner() 返回的剪贴板所有者的句柄、由 getwindowthreadprocessid() 和进程名称,由 process.getprocessbyid()。

您可以像这样设置 clipboardupdatemonitor 对象:
该类也可以在program.cs中初始化

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 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 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;
            }
        }
    }
}

自定义 eventargs 对象,用于携带收集的有关剪贴板所有者的信息:

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; }
}

nativemethods 类:

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;
}

以上就是如何获取更新剪贴板的应用程序的进程ID或名称?的详细内容,更多请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯