C# 使用WinApi操作剪切板Clipboard

前言:

最近正好写一个程序,需要操作剪切板

功能很简单,只需要从剪切板内读取字符串,然后清空剪切板,然后再把字符串导入剪切板

我想当然的使用我最拿手的C#来完成这项工作,原因无他,因为.Net框架封装了能实现这种功能的方法

然后就有了如下代码

 1             string Temp = "";
 2             while (true)
 3             {
 4                 string Tex = Clipboard.GetText().ToString();
 5                 if (!string.IsNullOrWhiteSpace(Tex) && Temp != Tex)
 6                 {
 7                     Clipboard.Clear();
 8                     Clipboard.SetDataObject(Tex, false);
 9                     Temp = Tex;
10                 }
11                 Thread.Sleep(1);
12             }
View Code

这段代码,也是网页上广泛流传的,使用.Net框架操作系统剪切板的方法,当然这个方法在某些情况下很管用

不过在我这确发生了点问题,主要的问题有两点

首先,我对剪切板的操作需求有实时性,也就是,操作人员复制的一瞬间就应该截取到剪切板的数据,处理完后再放入剪切板

结果

 Clipboard.SetDataObject(Tex, false);

没想到上面这条设置剪切板的指令竟然会卡焦点窗口的线程,比如说,我在A软件执行了一次复制操作,如果使用了上述代码,那么A软件强制线程堵塞大概几百毫秒的样子,反正很影响体验,我推测是因为该命令会锁定内存导致的

那怎么办,本着死马当活马医的态度,我专门为该指令启用了一个线程

       Task.Factory.StartNew(()=> {Clipboard.Clear();Clipboard.SetDataObject(Text, false);});

使用了线程以后,因为操作滞后(线程启动会延迟一会儿,并不实时)了,所以上述问题似乎解决了,但是没想到出现了新的问题

 string Tex = Clipboard.GetText().ToString();

上述从剪切板获得字符串的指令,在默写情况下,会卡滞住,然后程序在一分钟之后,因为超时而被系统吊销

emmmmm,在经过几番努力之后,我终于意识到,虽然.Net封装了不少操作系统API的方法,使得一些IO操作变简单不少,但是带来的问题也是同样大的,在遇到无法解决的问题的时候,会有点束手无策

于是不得已,我只能放弃使用过C#完成该项功能,想着幸好功能简单,而且操作WinAPI其实最好的还是使用C++来写,于是我用C++复现了上述功能

 1 #include "stdafx.h"
 2 #include <windows.h>
 3 #include <iostream>
 4 using namespace std;
 5 #pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup")
 6 
 7 int main(int argc, _TCHAR* argv[])
 8 {
 9     HANDLE THandle = GlobalAlloc(GMEM_FIXED, 1000);//分配内存
10     char* Temp = (char*)THandle;//锁定内存,返回申请内存的首地址
11     while (true)
12     {
13         HWND hWnd = NULL;
14         OpenClipboard(hWnd);//打开剪切板
15         if (IsClipboardFormatAvailable(CF_TEXT))
16         {
17             HANDLE h = GetClipboardData(CF_TEXT);//获取剪切板数据
18             char* p = (char*)GlobalLock(h);
19             GlobalUnlock(h);
20             if (strcmp(Temp, p))
21             {
22                 EmptyClipboard();//清空剪切板
23                 HANDLE hHandle = GlobalAlloc(GMEM_FIXED, 1000);//分配内存
24                 char* pData = (char*)GlobalLock(hHandle);//锁定内存,返回申请内存的首地址
25                 strcpy(pData, p);
26                 strcpy(Temp, p);
27                 SetClipboardData(CF_TEXT, hHandle);//设置剪切板数据
28                 GlobalUnlock(hHandle);//解除锁定
29             }
30         }
31         CloseClipboard();//关闭剪切板
32         Sleep(500);
33     }
34     return 0;
35 }
View Code

不愧是C++,使用上述代码后,完美实现我需要的功能,而且不管是主程序,还是我写的这个程序,都不会出现卡滞或者不工作的情况了,真是可喜可贺。

那么本教程就到此为止。

 

 

以下是正文

想着,既然我能用C++调用WinAPI完美实现我需要的功能,而且C#也能调用非托管的代码来执行WinAPI,那么我不是可以把上面C++写的代码移植到C#里面执行?说干就干

首先,C#调用WinAPI需要先申明

        [DllImport("User32")]internal static extern bool OpenClipboard(IntPtr hWndNewOwner);[DllImport("User32")]internal static extern bool CloseClipboard();[DllImport("User32")]internal static extern bool EmptyClipboard();[DllImport("User32")]internal static extern bool IsClipboardFormatAvailable(int format);[DllImport("User32")]internal static extern IntPtr GetClipboardData(int uFormat);[DllImport("User32", CharSet = CharSet.Unicode)]internal static extern IntPtr SetClipboardData(int uFormat, IntPtr hMem);

操作剪切板需要调用的API大致就上面这些

有了API以后,我们还需要自己手动封装方法

     internal static void SetText(string text){if (!OpenClipboard(IntPtr.Zero))
        {
        SetText(text);
        return;
        }EmptyClipboard();SetClipboardData(
13, Marshal.StringToHGlobalUni(text));CloseClipboard();}internal static string GetText(int format){string value = string.Empty;OpenClipboard(IntPtr.Zero);if (IsClipboardFormatAvailable(format)){IntPtr ptr = NativeMethods.GetClipboardData(format);if (ptr != IntPtr.Zero){value = Marshal.PtrToStringUni(ptr);}}CloseClipboard();return value;}

我们也就用到两个方法,从剪切板获得文本和设置文本到剪切板,哦关于SetClipboardData的第一个参数13是怎么来的问题,其实这个剪切板的格式参数,下面有一张表,就是自从这里来的

public static class ClipboardFormat
{/// <summary>/// Text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals/// the end of the data. Use this format for ANSI text./// </summary>public const int CF_TEXT = 1;/// <summary>/// A handle to a bitmap (<c>HBITMAP</c>)./// </summary>public const int CF_BITMAP = 2;/// <summary>/// Handle to a metafile picture format as defined by the <c>METAFILEPICT</c> structure. When passing a/// <c>CF_METAFILEPICT</c> handle by means of DDE, the application responsible for deleting <c>hMem</c> should/// also free the metafile referred to by the <c>CF_METAFILEPICT</c> handle./// </summary>public const int CF_METAFILEPICT = 3;/// <summary>/// Microsoft Symbolic Link (SYLK) format./// </summary>public const int CF_SYLK = 4;/// <summary>/// Software Arts' Data Interchange Format./// </summary>public const int CF_DIF = 5;/// <summary>/// Tagged-image file format./// </summary>public const int CF_TIFF = 6;/// <summary>/// Text format containing characters in the OEM character set. Each line ends with a carriage return/linefeed/// (CR-LF) combination. A null character signals the end of the data./// </summary>public const int CF_OEMTEXT = 7;/// <summary>/// A memory object containing a <c>BITMAPINFO</c> structure followed by the bitmap bits./// </summary>public const int CF_DIB = 8;/// <summary>/// Handle to a color palette. Whenever an application places data in the clipboard that depends on or assumes/// a color palette, it should place the palette on the clipboard as well. If the clipboard contains data in/// the <see cref="CF_PALETTE"/> (logical color palette) format, the application should use the/// <c>SelectPalette</c> and <c>RealizePalette</c> functions to realize (compare) any other data in the/// clipboard against that logical palette. When displaying clipboard data, the clipboard always uses as its/// current palette any object on the clipboard that is in the <c>CF_PALETTE</c> format./// </summary>public const int CF_PALETTE = 9;/// <summary>/// Data for the pen extensions to the Microsoft Windows for Pen Computing./// </summary>public const int CF_PENDATA = 10;/// <summary>/// Represents audio data more complex than can be represented in a CF_WAVE standard wave format./// </summary>public const int CF_RIFF = 11;/// <summary>/// Represents audio data in one of the standard wave formats, such as 11 kHz or 22 kHz PCM./// </summary>public const int CF_WAVE = 12;/// <summary>/// Unicode text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character/// signals the end of the data./// </summary>public const int CF_UNICODETEXT = 13;/// <summary>/// A handle to an enhanced metafile (<c>HENHMETAFILE</c>)./// </summary>public const int CF_ENHMETAFILE = 14;/// <summary>/// A handle to type <c>HDROP</c> that identifies a list of files. An application can retrieve information/// about the files by passing the handle to the <c>DragQueryFile</c> function./// </summary>public const int CF_HDROP = 15;/// <summary>/// The data is a handle to the locale identifier associated with text in the clipboard. When you close the/// clipboard, if it contains <c>CF_TEXT</c> data but no <c>CF_LOCALE</c> data, the system automatically sets/// the <c>CF_LOCALE</c> format to the current input language. You can use the <c>CF_LOCALE</c> format to/// associate a different locale with the clipboard text./// An application that pastes text from the clipboard can retrieve this format to determine which character/// set was used to generate the text./// Note that the clipboard does not support plain text in multiple character sets. To achieve this, use a/// formatted text data type such as RTF instead. /// The system uses the code page associated with <c>CF_LOCALE</c> to implicitly convert from/// <see cref="CF_TEXT"/> to <see cref="CF_UNICODETEXT"/>. Therefore, the correct code page table is used for/// the conversion./// </summary>public const int CF_LOCALE = 16;/// <summary>/// A memory object containing a <c>BITMAPV5HEADER</c> structure followed by the bitmap color space/// information and the bitmap bits./// </summary>public const int CF_DIBV5 = 17;/// <summary>/// Owner-display format. The clipboard owner must display and update the clipboard viewer window, and receive/// the <see cref="ClipboardMessages.WM_ASKCBFORMATNAME"/>, <see cref="ClipboardMessages.WM_HSCROLLCLIPBOARD"/>,/// <see cref="ClipboardMessages.WM_PAINTCLIPBOARD"/>, <see cref="ClipboardMessages.WM_SIZECLIPBOARD"/>, and/// <see cref="ClipboardMessages.WM_VSCROLLCLIPBOARD"/> messages. The <c>hMem</c> parameter must be <c>null</c>./// </summary>public const int CF_OWNERDISPLAY = 0x0080;/// <summary>/// Text display format associated with a private format. The <c>hMem</c> parameter must be a handle to data/// that can be displayed in text format in lieu of the privately formatted data./// </summary>public const int CF_DSPTEXT = 0x0081;/// <summary>/// Bitmap display format associated with a private format. The <c>hMem</c> parameter must be a handle to/// data that can be displayed in bitmap format in lieu of the privately formatted data./// </summary>public const int CF_DSPBITMAP = 0x0082;/// <summary>/// Metafile-picture display format associated with a private format. The <c>hMem</c> parameter must be a/// handle to data that can be displayed in metafile-picture format in lieu of the privately formatted data./// </summary>public const int CF_DSPMETAFILEPICT = 0x0083;/// <summary>/// Enhanced metafile display format associated with a private format. The <c>hMem</c> parameter must be a/// handle to data that can be displayed in enhanced metafile format in lieu of the privately formatted data./// </summary>public const int CF_DSPENHMETAFILE = 0x008E;/// <summary>/// Start of a range of integer values for application-defined GDI object clipboard formats. The end of the/// range is <see cref="CF_GDIOBJLAST"/>. Handles associated with clipboard formats in this range are not/// automatically deleted using the <c>GlobalFree</c> function when the clipboard is emptied. Also, when using/// values in this range, the <c>hMem</c> parameter is not a handle to a GDI object, but is a handle allocated/// by the <c>GlobalAlloc</c> function with the <c>GMEM_MOVEABLE</c> flag./// </summary>public const int CF_GDIOBJFIRST = 0x0300;/// <summary>/// See <see cref="CF_GDIOBJFIRST"/>./// </summary>public const int CF_GDIOBJLAST = 0x03FF;/// <summary>/// Start of a range of integer values for private clipboard formats. The range ends with/// <see cref="CF_PRIVATELAST"/>. Handles associated with private clipboard formats are not freed/// automatically; the clipboard owner must free such handles, typically in response to the/// <see cref="ClipboardMessages.WM_DESTROYCLIPBOARD"/> message./// </summary>public const int CF_PRIVATEFIRST = 0x0200;/// <summary>/// See <see cref="CF_PRIVATEFIRST"/>./// </summary>public const int CF_PRIVATELAST = 0x02FF;
}
View Code

在C++里面是不用指定数字的,只需要用CF_UNICODETEXT就行,不过.Net里面应该没有对应的索引表,所以只能手动输入(我这里是为了说明用才专门用数字,自己代码那是索引的枚举类)

上面两个工作做完以后,就能实现功能了,功能代码如下

                   var LastS = string.Empty;while (!CancelInfoClipboard.IsCancellationRequested){var Temp = ClipboardControl.GetText(ClipboardFormat.CF_UNICODETEXT);if (!string.IsNullOrEmpty(Temp) && Temp != LastS){ClipboardControl.SetText(Temp);LastS = Temp;}Thread.Sleep(50);}

是不是和最开始展示的调用.Net框架的方法一模一样(笑),不过使用底层API实现的功能,就没有那么多乱七八糟的Bug了,自己也很清楚到底实现了啥功能,同时也收获了不少新知识(主要是非托管代码调用的时候的注意事项什么的,还有,向非托管代码传递数据的时候,最好多用Marshal类里面的方法,不然可能会出错,毕竟这个类就是专门为非托管代码而设立的)

接下来是新的发现

在研究MSDN上面关于剪切板的API的时候,发现了一个函数

bool AddClipboardFormatListener(HWND hwnd);

根据描述来讲,是添加一个剪切板的监控,在剪切板有任何变动的时候,通知你所指定的句柄的窗口,我一想,这不就是我所需要的么,有了这么一个API以后,其实我上面所展示的,使用死循环轮询剪切板的方法就变得很傻逼,而且也很容易出错了,于是,基于这个新发现的API,我重新更改了全部的程序逻辑,反而比原先的实现更加简单了。

首先我们需要一个新的窗口或者控件来接收Windows消息更新后所发来的消息,只要New 一个form就行

        public Form2(){InitializeComponent();AddClipboardFormatListener(this.Handle);}

然后我们在初始化组件的命令后面,把使用添加剪切板监听的API把当前窗口的句柄发给系统,这样系统在接收到剪切板改变的命令后,会把消息发给当前窗口

然后我们需要复写WndProc方法

   protected override void WndProc(ref Message m){if (m.Msg == 0x031D && Onice){var Temp = ClipboardControl.GetText(ClipboardFormat.CF_UNICODETEXT);if (!string.IsNullOrEmpty(Temp)){ClipboardControl.SetText(Temp);Onice = false;}}else if (!Onice){Onice = true;}else{base.WndProc(ref m);}}
   private bool Onice = true;

首先WndProc如果是Form类下面一个专门用来接收系统发送过来的消息的方法

然后关于m.Msg == 0x031D的0x031D在WinAPI定义上的意义是WM_CLIPBOARDUPDATE ,也就是剪切板更新事件,这个通过查找MSDN能够找到

下面没有特别奇怪的函数,就是有一点需要注意,我们这里设置了剪切板数据,相当于进行了一次更新,所以会在这一瞬间再次产生剪切板更新事件,然后又会通知这个方法,然后就会形成死循环,我在这里用了一个布尔判断来通过布尔状态决定是否截取剪切板,不只有有没有更好的办法来实现

以上

转载于:https://www.cnblogs.com/ACDIV/p/9114472.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/390357.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

专访赵加雨:WebRTC在网易云信的落地

去年的这个时候&#xff0c;在市面上公开表示使用WebRTC的公司还没几家&#xff0c;但2018年以来&#xff0c;宣布采用或支持WebRTC的公司已经越来越多。实时音视频提供商网易云信也在自研的NRTC中集成了WebRTC。在他们眼里&#xff0c;2017年是WebRTC的转折之年&#xff0c;而…

1、Linux命令随笔

1 Linux命令总结2 3 man 命令帮助;4 help 命令的帮助&#xff08;bash的内置命令&#xff09;;5 ls list,查看目录列表;6 -ld&#xff1a;查看目录权限;7 -l:(long)长格式显示属性;8 -F:给不同的文件类型结尾加标识9 -p:给目录加斜线10 …

1137. 第 N 个泰波那契数

泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2 给你整数 n&#xff0c;请返回第 n 个泰波那契数 Tn 的值。 示例 1&#xff1a; 输入&#xff1a;n 4 输出&#xff1a;4 解释&#xff1a; T_3 0 1 1 2 T_4 1…

5827. 检查操作是否合法

给你一个下标从 0 开始的 8 x 8 网格 board &#xff0c;其中 board[r][c] 表示游戏棋盘上的格子 (r, c) 。棋盘上空格用 ‘.’ 表示&#xff0c;白色格子用 ‘W’ 表示&#xff0c;黑色格子用 ‘B’ 表示。 游戏中每次操作步骤为&#xff1a;选择一个空格子&#xff0c;将它变…

5193. 删除字符使字符串变好

5193. 删除字符使字符串变好 一个字符串如果没有 三个连续 相同字符&#xff0c;那么它就是一个 好字符串 。 给你一个字符串 s &#xff0c;请你从 s 删除 最少 的字符&#xff0c;使它变成一个 好字符串 。 请你返回删除后的字符串。题目数据保证答案总是 唯一的 。 示例 …

PHP--------微信网页开发实现微信扫码功能

今天说说微商城项目中用到的扫一扫这个功能&#xff0c;分享一下&#xff0c;希望对各位有所帮助。 前提&#xff1a;要有公众号&#xff0c;和通过微信认证&#xff0c;绑定域名&#xff0c;得到相应信息&#xff0c;appid&#xff0c;appsecret等。 微信开发文档&#xff1a;…

313. 超级丑数

超级丑数 是一个正整数&#xff0c;并满足其所有质因数都出现在质数数组 primes 中。 给你一个整数 n 和一个整数数组 primes &#xff0c;返回第 n 个 超级丑数 。 题目数据保证第 n 个 超级丑数 在 32-bit 带符号整数范围内。 示例 1&#xff1a; 输入&#xff1a;n 12,…

[SQL] 请教一下 count里面有case when 一般情况下啥时候用

http://www.itpub.net/forum.php?modviewthread&tid1810967 问题: 比如 count(case when pday_id${deal_date} then 1 end) 我有点想不明白具体什么情况下count&#xff08;&#xff09; 这个小括号里面还要用case when 大家做BI统计的时候一般什么情况用啊 还有个…

路由器架设虚拟服务器让外网访问到本地网站

确定电脑与路由器正确连接&#xff0c;并且已连至互联网。在地址栏中输入192.168.0.1回车&#xff0c;输入用户名密码&#xff0c;进入路由器主界面。 然后点击左侧菜单中的“虚拟服务器”&#xff0c;——“端口段映射”打开“端口段映射”界面。 由于网站用的是80端口&#x…

selenium模块

selenium模块 阅读目录 一 介绍二 安装三 基本使用四 选择器五 等待元素被加载六 元素交互操作七 其他八 项目练习一 介绍 selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题selenium本质是通过驱动浏览器&#xff0c;完…

关于tomcat Post 数据参数的问题

2019独角兽企业重金招聘Python工程师标准>>> POST请求本身并未限制传入参数大小&#xff0c;是tomcat 容器设置了接收参数大小的限制。修改server.xml <Connector port"8080" protocol"HTTP/1.1" connectionTimeout"2000" red…

杜教筛--51nod1239 欧拉函数之和

求$\sum_{i1}^{n}\varphi (i)$&#xff0c;$n\leqslant 1e10$。 这里先把杜教筛的一般套路贴一下&#xff1a; 要求$S(n)\sum_{i1}^{n}f(i)$&#xff0c;而现在有一数论函数$g(i)$&#xff0c;$g(i)$的前缀和很无脑&#xff0c;且$f$和$g$的狄利克雷卷积的前缀和很无脑&#xf…

修改npm全局安装模式的路径

修改npm全局安装模式的路径 在正式写此文章之前&#xff0c;我得说一点血泪史。 刚学nodeJS不久&#xff0c;很纳闷为什么全局安装的模块在 node安装目录/node_modules‘ 中没找到&#xff01;后来仔细看了下安装成功后的信息&#xff0c;才发现原来是自动安装在C盘了&#xff…

在Mac上为自己手动编译安装一套PHP7的开发环境

首先你得去官网下载php7 beta1的版本 这里由于我是在mac上安装&#xff0c;所以就去下载linux相关的版本&#xff0c;地址也直接附上了php7 beta1windows版的官方也有发布详情猛戳&#xff1a;这里 解压安装包&#xff0c;进入源代码目录 tar -zxvf php-7.0.0beta1.tar.gz cd p…

卡特兰数 HDU2067 HDU4165 HDU1134

题目链接&#xff1a;https://vjudge.net/problem/HDU-2067 小兔的棋盘 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11800 Accepted Submission(s): 5952 Problem Description小兔的叔叔从外面旅游回来给她…

Publish/Subscribe

Publish/Subscribe 我们将会投递一个消息给多个消费者&#xff0c;这种模式被称为“publish/subscribe” 通俗的讲&#xff0c;前面的是点对点队列模型&#xff0c;现在讲的是发布订阅模型。 Exchanges producer&#xff1a;一个发送消息的用户应用程序 queue&#xff1a;一个存…

[转]在ROS下使用zeroconf配置多机通信

原文地址&#xff1a;http://www.corvin.cn/635.html&#xff0c;转载主要方便随时查阅&#xff0c;如有版权要求&#xff0c;请及时联系。 0x00 为何需要配置ROS多机通信 众所周知ROS是分布式系统&#xff0c;因此可以将机器人需要处理的复杂、计算量大的任务分解在多台机器上…

NGINX原理 之 SLAB分配机制(转)

1 引言 众所周知&#xff0c;操作系统使用伙伴系统管理内存&#xff0c;不仅会造成大量的内存碎片&#xff0c;同时处理效率也较低下。SLAB是一种内存管理机制&#xff0c;其拥有较高的处理效率&#xff0c;同时也有效的避免内存碎片的产生&#xff0c;其核心思想是预分配。其按…

dynamic web module消失不见

2019独角兽企业重金招聘Python工程师标准>>> 方法1&#xff1a;在project Facets选项中勾选Dynamic Web Module即可 方法2&#xff1a; 我用eclipse对项目进行修改名称&#xff0c;修改成功后。项目就没有Deployment Descriptor&#xff08;如下图红色框中&#xff…

576. 出界的路径数

576. 出界的路径数 给你一个大小为 m x n 的网格和一个球。球的起始坐标为 [startRow, startColumn] 。你可以将球移到在四个方向上相邻的单元格内&#xff08;可以穿过网格边界到达网格之外&#xff09;。你 最多 可以移动 maxMove 次球。 给你五个整数 m、n、maxMove、star…