通过 WinApi32 监视剪贴板中的更改
Monitor changes in Clipboard via WinApi32
我想监控剪贴板的变化(仅文本)。我试图找到一些解决方案,并在 Whosebug 上发现了以下问题:
Clipboard event C#
How do non-window program monitor system clipboard?
但是我的 problem/requirement 是我不想添加对 Windows.Forms 和 WPF 的依赖(我的应用程序是控制台应用程序)。我尝试查看 user32.dll 中可用的方法。我写了一段代码来创建 window 和 window 样式。将 WndPrc 方法重写为自定义方法,然后将侦听器添加到剪贴板更改。为以下消息调用 WndProcFunction 4 次:
- WM_GETMINMAXINFO
- WM_NCCREATE
- WM_NCCALCSIZE
- WM_CREATE
但是当我更改剪贴板的内容时,发送了 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);
}
我想监控剪贴板的变化(仅文本)。我试图找到一些解决方案,并在 Whosebug 上发现了以下问题:
Clipboard event C#
How do non-window program monitor system clipboard?
但是我的 problem/requirement 是我不想添加对 Windows.Forms 和 WPF 的依赖(我的应用程序是控制台应用程序)。我尝试查看 user32.dll 中可用的方法。我写了一段代码来创建 window 和 window 样式。将 WndPrc 方法重写为自定义方法,然后将侦听器添加到剪贴板更改。为以下消息调用 WndProcFunction 4 次:
- WM_GETMINMAXINFO
- WM_NCCREATE
- WM_NCCALCSIZE
- WM_CREATE
但是当我更改剪贴板的内容时,发送了 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);
}