C# 监视剪贴板更改控制台
C# Monitor Clipboard changes console
我正在开发一个控制台应用程序,它应该在剪贴板内容更改时捕获事件。为此,WinRT 中有一个 API,Windows.ApplicationModel.DataTransfer.Clipboard.ContentChanged。我已经在 WinForms 和 WPF 应用程序上对此进行了测试,它运行良好。但是,在控制台应用程序中执行此操作时遇到问题。代码非常基础。在 WinForms 应用程序上做时,我简单地写了这行代码:
public MyApp()
{
InitializeComponent();
Windows.ApplicationModel.DataTransfer.Clipboard.ContentChanged += OnClipboardChanged;
}
public async void OnClipboardChanged(Object sender, Object e)
{
MyCodeHere
}
然而,当尝试在我的控制台应用程序中执行相同操作时:
class Program
{
[STAThread]
static void Main(string[] args)
{
Windows.ApplicationModel.DataTransfer.Clipboard.ContentChanged += OnClipboardChanged;
}
public static void OnClipboardChanged(Object sender, Object e)
{
Console.WriteLine("Hello");
}
}
然而控制台在启动后就退出了。如果我输入“Console.ReadKey”,它仍然会出错但不会退出。两种方式都不会调用我在 Main 中编写的事件。我希望控制台是 运行ning 而不是结束,即使剪贴板发生变化也是如此。所以它应该在后台不断 运行 ,每次剪贴板改变时它应该只是向控制台写一个“Hello”。我已经阅读了所有其他答案,但其中 none 对我有用,因为他们想操纵剪贴板,而我正在调用有关剪贴板内容更改的事件。感谢大家的帮助!
另一个问题,如果我改用 C++/winRT,性能会有什么不同吗?
在控制台上下文中,您必须确保 windows 消息得到处理,因为剪贴板依赖于它,例如,您可以使用 Winforms 的 DoEvents 方法(如果您没有真实的 window 并且没有任何消息推送):
class Program
{
static void Main()
{
Windows.ApplicationModel.DataTransfer.Clipboard.ContentChanged += (s, e) => Console.WriteLine("ContentChanged");
Console.WriteLine("Press any key to quit");
do
{
System.Windows.Forms.Application.DoEvents();
if (Console.KeyAvailable)
break;
Thread.Sleep(100); // for example
}
while (true);
}
}
要在 .NET 5 控制台项目中启用 Winforms 支持,这是最简单的方法(您甚至不需要添加 Windows.SDK nuget 包),只需将 .csproj 修改为像这样:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0-windows10.0.19041.0</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
</PropertyGroup>
</Project>
如果您没有 .NET 5,或者不想引用 Winforms,那么您可以使用 P/Invoke 声明自己的消息泵,如下所示:
class Program
{
[STAThread]
static void Main()
{
Windows.ApplicationModel.DataTransfer.Clipboard.ContentChanged += (s, e) => Console.WriteLine("ContentChanged");
Console.WriteLine("Press any key to quit");
do
{
while (GetMessage(out var msg, IntPtr.Zero, 0, 0) != 0)
{
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
if (Console.KeyAvailable)
break;
Thread.Sleep(100);
Console.Write(".");
}
while (true);
}
[StructLayout(LayoutKind.Sequential)]
private struct MSG
{
public IntPtr hwnd;
public int message;
public IntPtr wParam;
public IntPtr lParam;
public int time;
public POINT pt;
public int lPrivate;
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[DllImport("user32")]
private static extern int GetMessage(out MSG lpMsg, IntPtr hWnd, int wMsgFilterMin, int wMsgFilterMax);
[DllImport("user32")]
private static extern bool TranslateMessage(ref MSG lpMsg);
[DllImport("user32")]
private static extern IntPtr DispatchMessage(ref MSG lpmsg);
}
我正在开发一个控制台应用程序,它应该在剪贴板内容更改时捕获事件。为此,WinRT 中有一个 API,Windows.ApplicationModel.DataTransfer.Clipboard.ContentChanged。我已经在 WinForms 和 WPF 应用程序上对此进行了测试,它运行良好。但是,在控制台应用程序中执行此操作时遇到问题。代码非常基础。在 WinForms 应用程序上做时,我简单地写了这行代码:
public MyApp()
{
InitializeComponent();
Windows.ApplicationModel.DataTransfer.Clipboard.ContentChanged += OnClipboardChanged;
}
public async void OnClipboardChanged(Object sender, Object e)
{
MyCodeHere
}
然而,当尝试在我的控制台应用程序中执行相同操作时:
class Program
{
[STAThread]
static void Main(string[] args)
{
Windows.ApplicationModel.DataTransfer.Clipboard.ContentChanged += OnClipboardChanged;
}
public static void OnClipboardChanged(Object sender, Object e)
{
Console.WriteLine("Hello");
}
}
然而控制台在启动后就退出了。如果我输入“Console.ReadKey”,它仍然会出错但不会退出。两种方式都不会调用我在 Main 中编写的事件。我希望控制台是 运行ning 而不是结束,即使剪贴板发生变化也是如此。所以它应该在后台不断 运行 ,每次剪贴板改变时它应该只是向控制台写一个“Hello”。我已经阅读了所有其他答案,但其中 none 对我有用,因为他们想操纵剪贴板,而我正在调用有关剪贴板内容更改的事件。感谢大家的帮助!
另一个问题,如果我改用 C++/winRT,性能会有什么不同吗?
在控制台上下文中,您必须确保 windows 消息得到处理,因为剪贴板依赖于它,例如,您可以使用 Winforms 的 DoEvents 方法(如果您没有真实的 window 并且没有任何消息推送):
class Program
{
static void Main()
{
Windows.ApplicationModel.DataTransfer.Clipboard.ContentChanged += (s, e) => Console.WriteLine("ContentChanged");
Console.WriteLine("Press any key to quit");
do
{
System.Windows.Forms.Application.DoEvents();
if (Console.KeyAvailable)
break;
Thread.Sleep(100); // for example
}
while (true);
}
}
要在 .NET 5 控制台项目中启用 Winforms 支持,这是最简单的方法(您甚至不需要添加 Windows.SDK nuget 包),只需将 .csproj 修改为像这样:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0-windows10.0.19041.0</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
</PropertyGroup>
</Project>
如果您没有 .NET 5,或者不想引用 Winforms,那么您可以使用 P/Invoke 声明自己的消息泵,如下所示:
class Program
{
[STAThread]
static void Main()
{
Windows.ApplicationModel.DataTransfer.Clipboard.ContentChanged += (s, e) => Console.WriteLine("ContentChanged");
Console.WriteLine("Press any key to quit");
do
{
while (GetMessage(out var msg, IntPtr.Zero, 0, 0) != 0)
{
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
if (Console.KeyAvailable)
break;
Thread.Sleep(100);
Console.Write(".");
}
while (true);
}
[StructLayout(LayoutKind.Sequential)]
private struct MSG
{
public IntPtr hwnd;
public int message;
public IntPtr wParam;
public IntPtr lParam;
public int time;
public POINT pt;
public int lPrivate;
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[DllImport("user32")]
private static extern int GetMessage(out MSG lpMsg, IntPtr hWnd, int wMsgFilterMin, int wMsgFilterMax);
[DllImport("user32")]
private static extern bool TranslateMessage(ref MSG lpMsg);
[DllImport("user32")]
private static extern IntPtr DispatchMessage(ref MSG lpmsg);
}