如何在 WinApi C# 中订阅剪贴板事件?

How to subscribe to clipboard events in WinApi C#?

我已经设置了一个查看器,它是当前进程并想接收消息WM_DRAWCLIPBOARD

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

var result = WinapiClipboard.SetClipboardViewer(Process.GetCurrentProcess().Handle);

它说 right here 我应该使用应用程序定义的函数来解析该消息。但它永远不会达到 function/method.

    private static IntPtr WndProc(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lParam)
    {
        var hdc = Process.GetCurrentProcess().Handle;
        var clipboard = new WinClipboard();

        switch (message)
        {
            case WinapiClipboard.WM_DRAWCLIPBOARD:
                var result = clipboard.GetUnicodeTextAsync().Result;


                return IntPtr.Zero;
            default:
                break;
        }
        return WinapiClipboard.DefWindowProc(hWnd, message, wParam, lParam);
    }

我应该如何收到这条消息?我订阅正确了吗?

更新:

我没有使用 WinForms/WPF 或任何 .NET 经典框架功能。我只有 .net 标准 2.0 或 .net 核心。

因为你正在做一个 winforms 项目覆盖现有的 Control.WndProc 而不是提供一个静态的。

protected override void WndProc(ref Message m)
{
if(WM_DRAWCLIPBOARD == msg.Msg)
{ ... }
else
  base.WndProc(ref msg);
}

这一切都非常直接来自 winforms 应用程序

Note : you cant do this from a console app (with any ease)

声明此

[DllImport("User32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

在您的表单构造函数中

// this.handle is your forms handle
_ClipboardViewerNext = SetClipboardViewer(this.Handle); 

然后您将通过覆盖 WndProc

在表单中收到 WM_DRAWCLIPBOARD 消息

在你的表单中

protected override void WndProc(ref Message m)
{
    switch ((Win32.Msgs)m.Msg)
    {
        case Win32.Msgs.WM_DRAWCLIPBOARD:
        // Handle clipboard changed
        break;
        // ... 
   }

   // we call this so we to pass the message along
   base.WndProc(ref m);
}


执行此操作的另一种方法,也许是一种更现代的方法

声明此

public const int WM_CLIPBOARDUPDATE = 0x031D;
public static IntPtr HWND_MESSAGE = new IntPtr(-3);

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AddClipboardFormatListener(IntPtr hwnd);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

在您的表单构造函数中

SetParent(Handle, HWND_MESSAGE);
AddClipboardFormatListener(Handle);

在你的表单中

protected override void WndProc(ref Message m)
{
     if (m.Msg == WM_CLIPBOARDUPDATE)
     {
        // handle message
     }
     base.WndProc(ref m);
}

如果您使用的是 class 库,则必须创建一个隐藏的动作或事件并传回该动作或事件

private class HiddenForm : Form
{
    public HiddenForm()
    {
        SetParent(Handle, HWND_MESSAGE);
        AddClipboardFormatListener(Handle);
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_CLIPBOARDUPDATE)
        {
            // do stuff here like call event
        }
        base.WndProc(ref m);
    }
}

Note : None of this is tested however should get you started


更新 1

SetClipboardViewer expects a window handle, not a process handle. If you don't have a window handle, create a message-only window for the sole purpose of receiving clipboard messages - thanks to IInspectable

更新 2

This will only ever work on the kind of machine that also always has the full framework available. - thanks to Hans Passant