通过 WinApi32 监视剪贴板中的更改

Monitor changes in Clipboard via WinApi32

我想监控剪贴板的变化(仅文本)。我试图找到一些解决方案,并在 Whosebug 上发现了以下问题:

但是我的 problem/requirement 是我不想添加对 Windows.Forms 和 WPF 的依赖(我的应用程序是控制台应用程序)。我尝试查看 user32.dll 中可用的方法。我写了一段代码来创建 window 和 window 样式。将 WndPrc 方法重写为自定义方法,然后将侦听器添加到剪贴板更改。为以下消息调用 WndProcFunction 4 次:

但是当我更改剪贴板的内容时,发送了 WM_CLIPBOARDUPDATE 类型的消息,也没有发送任何适合 WndProcFunction 的消息。我尝试使用 "old API" (SetDashboardViewer) 但它没有任何改变。代码如下所示:

using PInvoke;

...
internal static class User32Ext
{
    [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("kernel32.dll")]
    internal static extern uint GetLastError();
}

class Program
{
    private static IntPtr _nextInChain = IntPtr.Zero;

    internal static unsafe IntPtr WndProcFunction(IntPtr hwnd, User32.WindowMessage windowMessage, void* wParam1, void* lParam1)
    {
        if (windowMessage == User32.WindowMessage.WM_CREATE)
        {
            var listener = User32Ext.AddClipboardFormatListener(hwnd);
            var result = User32.OpenClipboard(window);
            //_nextInChain = User32Ext.SetClipboardViewer(hwnd);
        }
        if (windowMessage == User32.WindowMessage.WM_CLIPBOARDUPDATE)
        {
            var pointerToText = User32.GetClipboardData_IntPtr(1);
            var text = Marshal.PtrToStringAnsi(pointerToText);
            Console.WriteLine(text);
        }

        if (windowMessage == User32.WindowMessage.WM_DRAWCLIPBOARD)
        {
            if (_nextInChain != IntPtr.Zero)
            {
                User32.SendMessage(_nextInChain, windowMessage, wParam1, lParam1);
            }
        }

        if (windowMessage == User32.WindowMessage.WM_CHANGECBCHAIN)
        {
            _nextInChain = hwnd;
            //send message...
        }

        if (windowMessage == User32.WM_DESTROY)
        {
            //chain msg
            User32Ext.RemoveClipboardFormatListener(hwnd);
        }

        return hwnd; //success
    }

    static void Main(string[] args)
    {
        unsafe
        {
            var hInstance = Marshal.GetHINSTANCE(typeof(Program).Module);
            string name = "Test";
            User32.WNDCLASSEX wndClassEx = new User32.WNDCLASSEX
            {
                cbSize = Marshal.SizeOf(typeof(User32.WNDCLASSEX)),
                style = User32.ClassStyles.CS_GLOBALCLASS,
                cbClsExtra = 0,
                cbWndExtra = 0,
                hbrBackground = IntPtr.Zero,
                hCursor = IntPtr.Zero,
                hIcon = IntPtr.Zero,
                hIconSm = IntPtr.Zero,
                lpszMenuName = null,
                hInstance = hInstance,
                lpfnWndProc = new User32.WndProc(WndProcFunction)
            };

            var stringPtr = Marshal.StringToHGlobalAuto(name);
            wndClassEx.lpszClassName_IntPtr = stringPtr;

            var register = User32.RegisterClassEx(ref wndClassEx);
            var window = User32.CreateWindowEx(
                User32.WindowStylesEx.WS_EX_TRANSPARENT,
                name,
                "Test Window",
                0,
                0, 0, 0, 0,
                IntPtr.Zero,
                IntPtr.Zero,
                wndClassEx.hInstance,
                IntPtr.Zero
            );

            User32.SetClipboardData(13, stringPtr);
            ...
        }
    }
}

代码中是否还缺少某些内容,一些额外的侦听器?每次从 user32.dll 调用方法后,我都会查看错误代码。但都成功了,没有失败。

A message loop is required for receiving messages. If you don't want a window you can create a message-only window 使您能够发送和接收消息。

It is not visible, has no z-order, cannot be enumerated, and does not receive broadcast messages. The window simply dispatches messages.

在 C# 中,在 main 函数的末尾,它会像这样:

    MSG msg;  
    while (User32.GetMessage(out msg, IntPtr.Zero, 0, 0) != 0)  
    {  
        User32.TranslateMessage(ref msg);  
        User32.DispatchMessage(ref msg);  
    }  

参考MSG, Creating a Message Loop