Home  >  Article  >  Backend Development  >  How do I get the process ID or name of an application that updates the clipboard?

How do I get the process ID or name of an application that updates the clipboard?

王林
王林forward
2024-02-12 13:40:06891browse

How do I get the process ID or name of an application that updates the clipboard?

Question content

I am creating a clipboard manager in c# and sometimes I encounter the situation where the clipboard is set to empty by some applications.

This happens when for example excel deselects something that was just copied, so I need to determine if the clipboard is empty, but how do I get the name of the application that updated the clipboard ?

I'm hoping I can somehow get a hwnd handle to the application that updates the clipboard so I can find the process behind it using the following code:

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

I wish I could use the message in the hwnd object, but this seems to be my own application - probably the notification application with this process id:

If I can get it some other way, then that's also totally fine of course, but I'd appreciate any insight on this :-)

solution

Based on @jimi's answer, it's easy. I can add the following 3 lines to my original code:

// 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();

Workaround

You can call getclipboardowner() to get the window handle of the last time the clipboard was set or cleared (the operation that triggered the notification).

[...] Generally speaking, the clipboard owner is the window that last put data into the clipboard.
The emptyclipboard function assigns clipboard ownership.

In some special cases, a process will pass an empty handle to openclipboard(): read the remarks section of this function and the emptyclipboard function.

The application must open the clipboard before calling emptyclipboard By using the openclipboard function. If the application specifies The window handle is null when opening the clipboard, emptyclipboard succeeds But sets the clipboard owner to null. Note that this results in setclipboarddata failed.

▶ Here I am using the nativewindow derived class to set the clipboard listener. The window that handles clipboard update messages is created by initializing the createparams object and passing this parameter to the nativewindow.createhandle(createparams) method, which is used to create an invisiblewindow.
Then rewrite the wndproc of the initialized nativewindow and receive the wm_clipboardupdate notification.

addclipboardformatlistener Function is used to place a window in the system clipboard listener chain.

clipboardupdatemonitor Class generates an event when a clipboard notification is received. The custom clipboardchangedeventargs object passed in the event contains the handle to the clipboard owner, returned by getclipboardowner(), and the process name, returned by getwindowthreadprocessid() ##process.getprocessbyid().

You can set the

clipboardupdatemonitor object like this:
This class can also be initialized in 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<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;
            }
        }
    }
}

Custom

eventargs Object that carries collected information about the clipboard owner:

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

The above is the detailed content of How do I get the process ID or name of an application that updates the clipboard?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete