【问题标题】:Monitor changes in Clipboard via WinApi32通过 WinApi32 监控剪贴板中的变化
【发布时间】:2020-02-20 22:22:17
【问题描述】:

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

但我的问题/要求是我不想向 Windows.Forms 或 WPF 添加依赖项(我的应用程序是控制台应用程序)。我尝试查看 user32.dll 中的可用方法。我编写了一个创建窗口和窗口样式的代码。将 WndPrc 方法覆盖为自定义方法,然后将侦听器添加到剪贴板更改。 WndProcFunction 被以下消息调用了 4 次:

  • WM_GETMINMAXINFO
  • WM_NCCREATE
  • WM_NCCALCSIZE
  • WM_CREATE

但是,当我更改剪贴板的内容时,会发送 WM_CLIPBOARDUPDATE 类型的消息,也不会发送任何适合 WndProcFunction 的消息。我尝试使用“旧 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 调用方法后,我都会查看错误代码。但都成功了,没有任何失败。

【问题讨论】:

  • 检查过AddClipboardFormatListener 是否被调用?如果是,请检查return value 是否执行成功?如果没有,请通过检查返回值和窗口句柄确保您的窗口创建成功。
  • @RitaHan-MSFT “每次从 user32.dll 调用方法后,我都会查看错误代码。但一切都成功了,没有任何失败。”。此外,所有侦听器函数都返回 true。一切似乎都是正确的。

标签: c# winapi clipboard


【解决方案1】:

接收消息需要message loop。如果您不想要一个窗口,您可以创建一个message-only window,它使您能够发送和接收消息。

不可见,没有 z 顺序,不能枚举,不 接收广播消息。窗口只是发送消息。

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

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

请参阅MSGCreating a Message Loop

【讨论】:

    猜你喜欢
    • 2021-11-18
    • 2018-11-11
    • 2012-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多