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.pswp.cn/news/390357.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/390357.shtml
英文地址,請注明出處:http://en.pswp.cn/news/390357.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

聊聊spring cloud gateway的XForwardedHeadersFilter

序 本文主要研究spring cloud gateway的XForwardedHeadersFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC1-sources.jar!/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java Configuration ConditionalOnProperty(name "sp…

node緩沖區_Node.js緩沖區介紹

node緩沖區什么是緩沖液&#xff1f; (What are Buffers?) Binary is simply a set or a collection of 1 and 0. Each number in a binary, each 1 and 0 in a set are called a bit. Computer converts the data to this binary format to store and perform operations. Fo…

專訪趙加雨:WebRTC在網易云信的落地

去年的這個時候&#xff0c;在市面上公開表示使用WebRTC的公司還沒幾家&#xff0c;但2018年以來&#xff0c;宣布采用或支持WebRTC的公司已經越來越多。實時音視頻提供商網易云信也在自研的NRTC中集成了WebRTC。在他們眼里&#xff0c;2017年是WebRTC的轉折之年&#xff0c;而…

html/css雜題

1、css選擇器&#xff1a;詳細&#xff08;http://www.ruanyifeng.com/blog/2009/03/css_selectors.html&#xff09; 派生選擇器&#xff1a;按標簽 類別選擇器&#xff1a;按class ID選擇器&#xff1a;按ID 通用選擇器&#xff1a;* 匹配所有 屬性選擇器&#xff1a;按屬性&…

黑客馬拉松 招募_我如何贏得第一次黑客馬拉松-研究,設計和編碼的2個狂野日子

黑客馬拉松 招募I had no coding or engineering background. I studied biology in college, with no clue about what to do with my degree. 我沒有編碼或工程背景。 我在大學學習生物學&#xff0c;但不知道如何處理我的學位。 My first jobs were making cold calls in s…

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…

web圖像_Web圖像優化的基本介紹

web圖像Images are an essential ingredient of most websites. The visual quality of pictures has a direct impact on the brand image and the message those images convey. And the weight of images usually accounts for a 40-60% of the data transferred on the web…

ElasticSearch客戶端注解使用介紹

The best elasticsearch highlevel java rest api-----bboss 1.ElasticSearch客戶端bboss提供了一系列注解 ESId 用于標識實體對象中作為docid的屬性&#xff0c;該注解只有一個persistent 布爾值屬性&#xff0c;用于控制被本注解標注的字段屬性是否作為普通文檔屬性保存&am…

5827. 檢查操作是否合法

給你一個下標從 0 開始的 8 x 8 網格 board &#xff0c;其中 board[r][c] 表示游戲棋盤上的格子 (r, c) 。棋盤上空格用 ‘.’ 表示&#xff0c;白色格子用 ‘W’ 表示&#xff0c;黑色格子用 ‘B’ 表示。 游戲中每次操作步驟為&#xff1a;選擇一個空格子&#xff0c;將它變…

團隊的遠程管理_遠程團隊指南:如何管理您的遠程軟件開發團隊

團隊的遠程管理Guides to help you work remotely seem to have swept through the Internet these days. 這些天來&#xff0c;幫助您遠程工作的指南似乎席卷了Internet。 Do this, avoid that, stay productive, and all those run-of-the-mill tips we’ve already tried o…

JS 正則 錢

function ValidateIsDecial(sValue) {return (!sValue && !!!sValue && /^[0-9]{1,10}(\.[0-9]{0,2})?$/.test(sValue)); };驗證 decimal(12,2) 小數點前允許10位,小數點后允許2位 1234567890 true 12345678901 false 0123456789 true 01234567891 false 123.…

5193. 刪除字符使字符串變好

5193. 刪除字符使字符串變好 一個字符串如果沒有 三個連續 相同字符&#xff0c;那么它就是一個 好字符串 。 給你一個字符串 s &#xff0c;請你從 s 刪除 最少 的字符&#xff0c;使它變成一個 好字符串 。 請你返回刪除后的字符串。題目數據保證答案總是 唯一的 。 示例 …

2020計算機頂級大會_2020年頂級遠程調試工具

2020計算機頂級大會When it comes to debugging, the tool you use is extremely important and can determine how easy is is to fix problems within your code. 在調試方面&#xff0c;您使用的工具非常重要&#xff0c;可以確定在代碼中修復問題的難易程度。 In the earl…

BZOJ5292 洛谷4457 LOJ2513:[BJOI2018]治療之雨——題解

https://www.lydsy.com/JudgeOnline/problem.php?id5292 https://www.luogu.org/problemnew/show/P4457 https://loj.ac/problem/2513 你現在有m1個數&#xff1a;第一個為p&#xff0c;最小值為0&#xff0c;最大值為n&#xff1b;剩下m個都是無窮&#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,…

初創公司股本結構_我如何向初創公司的開發團隊添加一些結構-以及從過程中學到的東西

初創公司股本結構Until recently, Id spent the last 4 years of my career at FinTech start-ups. Id always worked for smaller companies, and being at a start-up was the next logical step in looking for roles where I could make the biggest difference. 直到最近…

拿什么拯救你,我的面試之——從零打卡刷Leetcode(No.003)

寫在前邊&#xff1a;小詹一直覺得自己編程能力不強&#xff0c;想在網上刷題&#xff0c;又怕不能堅持。不知道有木有和小伙伴和小詹一樣想找個人一起刷題呢&#xff1f;歡迎和小詹一起定期刷leetcode&#xff0c;每周一周五更新一題&#xff0c;每一題都吃透&#xff0c;歡迎…

146. LRU 緩存機制

146. LRU 緩存機制 運用你所掌握的數據結構&#xff0c;設計和實現一個 LRU (最近最少使用) 緩存機制 。 實現 LRUCache 類&#xff1a; LRUCache(int capacity) 以正整數作為容量 capacity 初始化 LRU 緩存 int get(int key) 如果關鍵字 key 存在于緩存中&#xff0c;則返回…