Clipboard và kéo thả hoạt động như thế nào — Xử lý đúng truyền dữ liệu OLE trong ứng dụng nghiệp vụ

· · Windows, Clipboard, Kéo thả, OLE, COM, Phát triển Windows, WinForms, WPF

“Khi chúng tôi dán bảng sao chép từ Excel, định dạng tan. Chúng tôi muốn nó dán thành bảng.” “Nội dung chúng tôi sao chép trong ứng dụng hóa thành thứ kỳ lạ khi dán vào Word.” “Chúng tôi muốn nhận tệp bằng kéo thả.” — Trong các cuộc trò chuyện tư vấn về thay đổi ứng dụng nghiệp vụ, yêu cầu quanh sao chép-và-dán và kéo thả (D&D) là món chủ lực.

Chính vì đây là “tính năng mọi người coi là đương nhiên”, cách chúng thực sự hoạt động ít được biết đến một cách đáng ngạc nhiên. Nếu bạn nghĩ clipboard như “một hộp bạn bỏ một mảnh dữ liệu vào”, bạn không thể giải thích vì sao cùng lần sao chép cho kết quả khác tùy nơi bạn dán, hoặc vì sao dán ngừng hoạt động sau khi đóng ứng dụng nguồn. Clipboard thật là cơ chế đặt cùng nội dung ở nhiều định dạng cùng lúc, và để phía dán chọn một định dạng nó hiểu.

Và kéo thả, ở đáy, là truyền dữ liệu OLE giao đúng cùng biểu diễn dữ liệu với clipboard (IDataObject), qua giao diện COM. Nói cách khác, sao chép-và-dán và D&D là anh em: hiểu đúng một cái thì cái kia ở ngay đó.

Bài viết này hướng tới nhân viên IT tại công ty vừa và nhỏ và nhà phát triển ứng dụng Windows. Nó buộc lại, trong một bức tranh, cách định dạng clipboard hoạt động, các thực hành phía dán và phía sao chép, cách đúng để theo dõi clipboard, các chính sách quản trị cho lịch sử clipboard, đồng bộ đám mây, và RDP, cùng cấu trúc và cái bẫy của kéo thả OLE.

1. Kết luận trước

  • Clipboard là một vùng duy nhất được các ứng dụng trên cùng desktop (window station) dùng chung, và thứ ngồi đó không phải “một mảnh dữ liệu” mà là cùng nội dung ở nhiều định dạng cùng lúc. Một phiên khác, như RDP, vốn có clipboard khác; tính năng chuyển hướng mới là thứ bắc cầu hai bên. Vì đích chọn một định dạng nó hiểu, cùng lần sao chép cho kết quả khác tùy nơi bạn dán.12
  • Với văn bản, dùng CF_UNICODETEXT. CF_TEXT là ANSI và phụ thuộc trang mã, và trên hệ thống tiếng Nhật nó là ổ nuôi mojibake. Hệ thống chuyển giữa hai cái ngầm, nhưng phía chuẩn tắc là Unicode.3
  • Tệp đi như CF_HDROP (mảng đường dẫn kết thúc bằng NUL kép), và văn bản đã định dạng dùng định dạng đã đăng ký “HTML Format”. HTML Format có cấu trúc khác thường: văn bản UTF-8 với đầu đề các offset byte.45
  • Nguyên nhân thật của “tôi đóng ứng dụng nguồn rồi không còn dán được” là kết xuất trì hoãn. Đó là cơ chế đặt không phải payload mà chỉ lời hứa “tạo khi được hỏi”; nếu bạn bỏ qua hiện thực hóa lúc thoát (đáp WM_RENDERALLFORMATS, hoặc OleFlushClipboard với OLE), dán ngừng hoạt động.26
  • Coi dữ liệu đã dán như đầu vào không tin cậy từ bên ngoài. Chính Microsoft nói thẳng rằng “dữ liệu clipboard không được tin. Phân tích nó cẩn thận”.7
  • Để theo dõi clipboard, AddClipboardFormatListener + WM_CLIPBOARDUPDATE là lựa chọn duy nhất. Đừng dùng thăm dò, và đừng dùng SetClipboardViewer cũ (chuỗi viewer). Các định dạng đã đăng ký giữ bí mật khỏi lịch sử và đồng bộ (ExcludeClipboardContentFromMonitorProcessing và bạn) cũng được cung cấp.81
  • Lịch sử clipboard (Win+V) và đồng bộ đám mây là mối quan tâm quản trị IT. Bạn có thể điều khiển chúng bằng AllowClipboardHistory và AllowCrossDeviceClipboard qua GPO / Intune (Policy CSP), và chuyển hướng clipboard RDP có chính sách riêng.91011
  • Kéo thả là COM. Cùng IDataObject với clipboard được giao giữa IDropSource (nguồn kéo) và IDropTarget (đích thả) qua vòng DoDragDrop. RegisterDragDrop đòi khởi tạo bằng OleInitialize (STA).1213
  • Bạn không thể thả từ File Explorer đặc quyền thông thường lên ứng dụng đã nâng quyền. UIPI (chặn thông điệp theo mức toàn vẹn) là nguyên nhân, và đó là ràng buộc bạn nên biết lúc thiết kế.14

Dưới đây chúng ta lần từ nền tảng clipboard lên.

2. Clipboard thực sự là gì — Không phải “một mảnh dữ liệu” mà là “cùng nội dung ở nhiều định dạng”

Clipboard là cơ chế chia sẻ dữ liệu chung mà mọi ứng dụng chia cùng desktop đều với tới được (chính xác hơn, nó theo từng window station: phiên người dùng khác hoặc phiên RDP mỗi cái có clipboard riêng. Sao chép-và-dán hoạt động qua RDP vì tính năng chuyển hướng bắc cầu hai bên — Chương 7). Nguyên tắc đầu tiên là nó do người dùng dẫn: lập trường thiết kế chính thức là bạn không bỏ dữ liệu vào hay lấy dữ liệu ra sau lưng người dùng.1

Điểm quan trọng là một lần sao chép không đặt “một mảnh dữ liệu”. Cửa sổ sao chép làm trống clipboard rồi đặt vài định dạng nối tiếp, biểu đạt cùng nội dung từ định dạng mạnh hơn xuống định dạng yếu hơn.2 Ví dụ, khi bạn sao chép một bảng trong bảng tính, về mặt khái niệm thứ như sau nằm trên clipboard cùng lúc.

Ưu tiên Định dạng Nội dung
1 Định dạng riêng ứng dụng Biểu diễn nội bộ đầy đủ, gồm công thức và định dạng (để dán lại vào cùng ứng dụng)
2 HTML Format Mảnh HTML giữ cấu trúc bảng và định dạng
3 CSV Văn bản phân cách ô
4 CF_UNICODETEXT Văn bản thuần phân cách bằng tab
5 Định dạng ảnh Bitmap về cách bảng trông

Phía dán chọn một định dạng nó hiểu từ danh sách này rồi trích. Dán vào Word thì bạn được bảng đã định dạng; dán vào Notepad thì bạn được văn bản phân cách bằng tab — vì hai bên chọn định dạng khác. “Kết quả tùy nơi bạn dán” không phải lỗi; đó là hệ quả bình thường của thiết kế này.

Vì sao cùng lần sao chép cho kết quả khác tùy nơi bạn dánPhía sao chép đặt cùng nội dung lên clipboard ở nhiều định dạng, và phía dán chọn một định dạng nó hiểu, nên Word được bảng đã định dạng và Notepad được văn bản phân cách bằng tabWordNotepadSao chép: bảng tínhClipboard(nhiều định dạng)Định dạng giàu hơnĐịnh dạng thuần hơnRiêng ứng dụngHTML FormatCSVCF_UNICODETEXTBảng đã định dạngVăn bản phân cách bằng tab

Nói ngược lại, các khiếu nại mở đầu — “định dạng tan”, “thứ kỳ lạ được dán” — gần như đều quy về vấn đề cách một phía chọn định dạng, hoặc cách phía kia cung cấp chúng. Chương 4 trình bày phía dán; Chương 5 trình bày phía sao chép.

3. Định dạng chuẩn và định dạng đã đăng ký — CF_UNICODETEXT, CF_HDROP, HTML Format

3.1. Định dạng chuẩn — Dùng phía Unicode cho văn bản

Các định dạng hệ điều hành định nghĩa sẵn được gọi là định dạng chuẩn. Những cái hiện liên tục trong ứng dụng nghiệp vụ là các cái sau.3

Định dạng Giá trị Nội dung
CF_TEXT 1 Văn bản ANSI (phụ thuộc trang mã)
CF_UNICODETEXT 13 Văn bản Unicode. Đây là định dạng chuẩn tắc cho văn bản
CF_HDROP 15 Danh sách đường dẫn tệp (handle HDROP)
CF_DIB 8 Bitmap độc lập thiết bị
CF_LOCALE 16 Định danh locale gắn với văn bản

CF_TEXT và CF_UNICODETEXT được hệ thống chuyển thành nhau ngầm (định dạng tổng hợp). Chuyển mã ký tự dùng trang mã gắn với CF_LOCALE.3 Dựa vào lần chuyển đó làm rơi các ký tự ANSI không biểu diễn được (ví dụ ký hiệu chỉ Unicode và ký tự kết hợp), nên quy tắc là thống nhất những gì ứng dụng đọc và ghi trên CF_UNICODETEXT (DataFormats.UnicodeText trong .NET).

Chuyển ngầm giữa CF_UNICODETEXT và CF_TEXTỨng dụng chỉ đọc và ghi CF_UNICODETEXT; hệ thống tổng hợp CF_TEXT bằng chuyển ngầm với trang mã CF_LOCALE. Các ký tự ANSI không biểu diễn được bị rơi trong lần chuyển đóChuyển CF_LOCALEỨng dụng đọc và ghiCF_UNICODETEXTCF_TEXT(ANSI)Ký tự không biểu diễn được bị rơi

3.2. CF_HDROP — Tệp đi như “danh sách đường dẫn”

CF_HDROP là thứ được dùng khi bạn sao chép tệp trong File Explorer, hoặc khi bạn kéo thả tệp. Payload không phải bản thân các tệp; đó là khối bộ nhớ trải một mảng “kết thúc bằng NUL kép”: sau đầu đề cấu trúc DROPFILES, các chuỗi đường dẫn đầy đủ phân cách bằng ký tự NUL, và một chuỗi rỗng ở cuối. pFiles của đầu đề là offset bắt đầu của danh sách đường dẫn, và fWide nói các chuỗi có phải Unicode không.4

[DROPFILES header: pFiles=start offset of the path list, fWide=1(Unicode)]
C:\data\a.txt(NUL)C:\data\b.txt(NUL)(NUL)

Trong mã native bạn kéo chúng ra từng cái bằng DragQueryFile; trong .NET bạn nhận chúng như string[] qua DataFormats.FileDrop. Sự thật rằng “thứ đi chỉ là đường dẫn, không phải bản thân các tệp” sẽ lại quan trọng trong D&D của Chương 8 và 9.

Bố cục khối bộ nhớ của CF_HDROPCấu trúc DROPFILES ngồi ở đầu bộ nhớ toàn cục; pFiles là offset bắt đầu của danh sách đường dẫn và fWide nói nó có phải Unicode. Các đường dẫn đầy đủ rồi theo, phân cách bằng NUL, và khối kết thúc bằng chuỗi rỗng(NUL kép). Thứ đi chỉ là đường dẫn, không phải tệpDROPFILES(pFiles / fWide)C:\\data\\a.txt + NULC:\\data\\b.txt + NULChuỗi rỗng(NUL kép)Chỉ đường dẫn đi, không phải tệp

3.3. Định dạng đã đăng ký — RegisterClipboardFormat và “HTML Format”

Với dữ liệu mà định dạng chuẩn không biểu đạt được, ứng dụng có thể chọn một tên và đăng ký định dạng riêng. Truyền một tên cho RegisterClipboardFormat thì bạn nhận lại ID định dạng; đăng ký dưới cùng tên từ ứng dụng khác trả về cùng ID, nên một khi bạn thống nhất tên bạn có thể chia dữ liệu giữa các ứng dụng.1 Khi bạn truyền dữ liệu có cấu trúc giữa bộ ứng dụng của riêng bạn, dùng tên sẽ không va, như KomuraSoft.Report.RowData.

Định dạng đã đăng ký đại diện là “HTML Format”, cho văn bản đã định dạng (cùng với RTF, một trong hai định dạng văn bản giàu lớn). Payload là văn bản UTF-8, nhưng nó có cấu trúc khác thường: một đầu đề liệt kê các offset byte được gắn ở phía trước.5

Version:0.9
StartHTML:<byte offset of the start of the whole HTML>
EndHTML:<byte offset of the end of the whole HTML>
StartFragment:<byte offset of the start of the fragment>
EndFragment:<byte offset of the end of the fragment>
<html><body>
<!--StartFragment--><b>bold</b> fragment text<!--EndFragment-->
</body></html>

Mỗi offset là vị trí byte từ đầu dữ liệu, gồm cả bản thân đầu đề; thực hành thông thường là dành sẵn chiều rộng cố định (ví dụ 10 chữ số) rồi viết các giá trị đã đo lại sau khi bạn đã xây thân. StartFragment/EndFragment đánh dấu đầu và cuối của “mảnh người dùng thực sự đã chọn” theo byte (không theo ký tự). Trong UTF-8 gồm tiếng Nhật, số ký tự và số byte lệch nhau, nên nếu bạn tính sai offset này, dán vào ứng dụng khác làm rơi đầu hoặc cuối. Nếu bạn tự tạo HTML Format, bạn phải điền đầu đề bằng vị trí byte đo sau khi mã hóa sang UTF-8.5

Cách đầu đề HTML Format liên hệ với các offsetStartHTML và EndHTML của đầu đề trỏ toàn bộ HTML, và StartFragment cùng EndFragment trỏ mảnh người dùng đã chọn, cả hai như vị trí byte từ đầu dữ liệu. Vì số ký tự và số byte lệch trong UTF-8, hãy điền đầu đề bằng vị trí byte đo sau khi mã hóaĐầu đề(offset byte)Toàn bộ HTMLMảnh đã chọnOffset là byte sau UTF-8

CSV (DataFormats.CommaSeparatedValue trong .NET) cũng thường được dùng cho dữ liệu dạng bảng. Để tương tác với Excel, cung cấp HTML Format (có định dạng), CSV (chỉ giá trị), và CF_UNICODETEXT (phân cách bằng tab) cùng lúc nghĩa là bạn không phải chọn một đích dán duy nhất.

4. Thực hành phía dán — Ưu tiên định dạng và kiểm tra

4.1. Nhìn từ định dạng giàu xuống

Các định dạng trên clipboard được xếp theo thứ tự phía sao chép đã đặt chúng (tức là từ biểu đạt nhiều hơn xuống ít hơn). Đường cơ sở của phía dán là nhìn, trong các định dạng bạn xử lý được, bắt đầu từ cái có nhiều thông tin nhất. Trong Win32 bạn hoặc liệt kê bằng EnumClipboardFormats rồi dùng định dạng đầu tiên bạn nhận ra, hoặc truyền danh sách ưu tiên riêng cho GetPriorityClipboardFormat và để nó chọn.2

Trong .NET nhánh trông đại khái như sau.

// Pasting a table: look from rich to plain
var data = Clipboard.GetDataObject();
if (data is null) return;

// Advertising a format does not guarantee the payload is a string. Use this
// branch only when the type also checks out; otherwise fall through to the next candidate
if (data.GetDataPresent(DataFormats.Html)
    && data.GetData(DataFormats.Html) is string html)
{
    // Validate the HTML Format header, then import as a table
}
else if (data.GetDataPresent(DataFormats.CommaSeparatedValue))
{
    // Import as CSV
}
else if (data.GetDataPresent(DataFormats.UnicodeText))
{
    // Import as tab-separated text
}

Đó là câu trả lời cho khiếu nại mở đầu, “dán bảng Excel thì tan”. Ứng dụng chỉ đọc văn bản thuần không bao giờ nhận cấu trúc bảng. Bạn chấp nhận xuống bao xa trong danh sách định dạng là quyết định thiết kế phía dán.

Nhánh dán nhìn từ định dạng giàu xuốngNếu HTML Format có mặt và payload cũng là chuỗi, nhập như bảng; nếu không thử CSV; nếu cái đó cũng thiếu, rơi xuống văn bản phân cách bằng tab. Nếu không ứng viên nào có mặt, từ chốikhôngkhôngkhôngBắt đầu dánHTML Format + chuỗi?Kiểm tra đầu đề → bảngCSV có mặt?Nhập như CSVUnicodeText?Văn bản phân cách bằng tabTừ chối

4.2. Dữ liệu đã dán là đầu vào bên ngoài

Dễ bỏ lỡ, nhưng nội dung clipboard là dữ liệu từ bên ngoài, và bạn không biết ứng dụng nào đã đặt chúng. Microsoft cũng cảnh báo, trong tài liệu clipboard OLE, rằng “dữ liệu clipboard không được tin. Phân tích nó cẩn thận trước khi dùng trong ứng dụng”.7

  • Kiểm tra rằng các offset đầu đề HTML Format không trỏ ra ngoài bộ đệm (ứng dụng phát đầu đề hỏng có tồn tại).
  • Giá trị bạn nhập như số, ngày, hoặc mã nên đi qua cùng kiểm tra như đầu vào trên màn hình.
  • Đặt phòng thủ chống dữ liệu khổng lồ. Ngay cả nếu ai đó dán ảnh hàng trăm megabyte hoặc hàng triệu dòng văn bản, đừng chặn UI, và từ chối khi vượt giới hạn. Một lưu ý: GetData của .NET, đúng lúc bạn gọi, hiện thực hóa toàn bộ payload thành chuỗi managed (và kết xuất trì hoãn chạy như một phần của điều đó), nên đặt kiểm tra kích thước sau GetData không phải phòng thủ. Trong Win32, kiểm tra GlobalSize trên HGLOBAL mà GetClipboardData trả về cho bạn phòng thủ ở giai đoạn “đừng đi tiếp vào chuyển và phân tích như chuỗi managed”, nhưng với định dạng kết xuất trì hoãn bản thân GetClipboardData kích hoạt kết xuất, nên bạn vẫn không ngăn được hiện thực hóa phía nguồn sao chép. Để UI không đóng băng, chuyển lần lấy khỏi luồng UI (và ngay cả vậy, vì Clipboard của .NET đòi STA, hãy làm trên luồng chuyên dụng đặt thành STA, không trên luồng thread-pool của Task.Run (MTA) — Mục 5.1).

Ý tưởng rằng “giá trị tới từ bên ngoài, dù đường nào, được kiểm tra trước khi bạn dùng” là cùng ý được trình bày trong “Không bao giờ dùng nguyên giá trị giải mã từ mã QR”. Giả định rằng dán an toàn vì đó là hành động của người dùng là cách tai nạn bắt đầu.

Kiểm tra dữ liệu đã dán trước khi dùngDữ liệu lấy từ clipboard đi qua có-mặt-định-dạng, kiểu-payload, giới-hạn-kích-thước, và kiểm tra nội dung theo thứ tự đó; thất bại cái nào thì bạn từ chối hoặc rơi xuống ứng viên định dạng tiếpsai kiểuquá lớnkhông hợp lệĐịnh dạng có mặt?Kiểu payload ổn?Kích thước trong giới hạn?Kiểm tra nội dungNhậpTừ chối / định dạng tiếp

5. Thực hành phía sao chép — Cung cấp nhiều định dạng cùng lúc, và kết xuất trì hoãn

5.1. Đặt nhiều định dạng cùng lúc

Thực hành phía sao chép là ngược của 4.1: cung cấp định dạng giàu và định dạng thuần cùng lúc. Với DataObject của WinForms/WPF bạn có thể viết trong vài dòng.15

// WinForms (System.Windows.Forms). WPF is the same shape with System.Windows DataObject/Clipboard
var data = new DataObject();
data.SetData(DataFormats.Html, htmlFormatText);       // HTML Format string including the header
data.SetData(DataFormats.CommaSeparatedValue, csv);   // CSV
data.SetData(DataFormats.UnicodeText, plainText);     // Plain text
Clipboard.SetDataObject(data, copy: true);            // copy:true = keep after the app exits

Hai lưu ý. Thứ nhất, lớp Clipboard của .NET chỉ dùng được từ luồng STA.15 Luồng UI WinForms/WPF là STA vì [STAThread], nên điều này thường không thành vấn đề, nhưng chạm nó từ luồng nền thì thất bại (nền tảng STA/MTA nằm trong “Nền tảng COM STA/MTA”). Thứ hai, copy: true nghĩa là gì gắn với kết xuất trì hoãn ở mục tiếp.

5.2. Kết xuất trì hoãn — Vì sao “đóng nguồn thì không dán được”

Xây payload lớn ở nhiều định dạng mỗi lần thì lãng phí, nên clipboard có cơ chế gọi là kết xuất trì hoãn. Truyền NULL làm handle dữ liệu cho SetClipboardData và, thay vì payload, chỉ một lời hứa “tạo khi được hỏi” được đăng ký; khi ai đó yêu cầu định dạng đó, WM_RENDERFORMAT tới nguồn sao chép, và chỉ khi đó dữ liệu mới được tạo.2

Hệ quả của thiết kế này là câu mở đầu “tôi đóng ứng dụng nguồn rồi không còn dán được”. Trước khi thoát, nguồn sao chép nhận WM_RENDERALLFORMATS và chịu trách nhiệm hiện thực hóa mọi định dạng chưa được kết xuất; thoát mà không làm điều đó thì định dạng mất.2

Kết xuất trì hoãn và vì sao đóng-rồi-dán thất bạiNguồn sao chép chỉ đăng ký lời hứa bằng handle NULL, và hiện thực hóa theo yêu cầu qua WM_RENDERFORMAT. Lúc thoát nó chịu trách nhiệm hiện thực hóa mọi định dạng bằng WM_RENDERALLFORMATS; bỏ qua thì định dạng mấtRENDERALLFORMATSBỏ hiện thực hóaSetClipboardData NULL = lời hứaPhía dán yêu cầuWM_RENDERFORMAT → xây ngayNguồn sao chép sắp thoátDán vẫn được sau khi thoátĐịnh dạng mất sau khi đóng

Trên clipboard OLE (kiểu đặt IDataObject bằng OleSetClipboard), quan hệ này còn rõ hơn. Tất cả clipboard giữ là con trỏ tới đối tượng dữ liệu, và gọi OleFlushClipboard lúc ứng dụng thoát hiện thực hóa dữ liệu lên clipboard, nên dán vẫn hoạt động sau khi thoát.6 Clipboard.SetDataObject(data, copy: true) của .NET chính là thứ chỉ định hành vi “giữ sau khi thoát” này.

Khi bạn sao chép một vùng lớn trong Excel rồi cố thoát, lời nhắc “There is a large amount of information on the Clipboard. Do you want to be able to paste this information into another program later?” chính là xác nhận có chạy lần hiện thực hóa này (flush) không. Nếu bạn dùng kết xuất trì hoãn trong ứng dụng của mình, hãy nhớ rằng hiện thực hóa lúc thoát là một phần của cùng bộ. Kết xuất trì hoãn là tối ưu hiệu năng, và vì yêu cầu kết xuất chạy đồng bộ bên trong xử lý thông điệp, dữ liệu mất nhiều thời gian để tạo có sự đánh đổi đóng băng UI.2

6. Thực hành theo dõi clipboard — Listener, thử lại, và loại khỏi lịch sử

6.1. Dùng AddClipboardFormatListener

Các yêu cầu như “chúng tôi muốn phát hiện giá trị đầu đọc mã vạch hoặc lần sao chép từ hệ thống nghiệp vụ rồi nhập tự động” cần bạn theo dõi thay đổi clipboard. Về lịch sử có ba phương pháp; hôm nay câu trả lời đúng là một.8

Phương pháp Đánh giá
Đọc trên bộ hẹn giờ (thăm dò) Lãng phí, và bạn có thể bỏ lỡ cập nhật. Đừng dùng
SetClipboardViewer (chuỗi viewer) Lỗi ở một ứng dụng trong chuỗi làm hỏng cả chuỗi. Chỉ được giữ vì tương thích ngược
AddClipboardFormatListener Được khuyến nghị. WM_CLIPBOARDUPDATE tới cửa sổ đã đăng ký
Luồng theo dõi clipboardĐăng ký bằng AddClipboardFormatListener khi handle được tạo, và WM_CLIPBOARDUPDATE tới dù ứng dụng nào đã sao chép. Đọc với thử lại, và hủy đăng ký đối xứng bằng RemoveClipboardFormatListener khi handle bị hủyhủy đăng kýAddClipboardFormatListenerChờMột ứng dụng nào đó sao chépWM_CLIPBOARDUPDATEĐọc với thử lại(6.2)RemoveClipboardFormatListener
// Minimal WinForms implementation
public partial class MainForm : Form
{
    [DllImport("user32.dll", SetLastError = true)]
    static extern bool AddClipboardFormatListener(IntPtr hwnd);
    [DllImport("user32.dll", SetLastError = true)]
    static extern bool RemoveClipboardFormatListener(IntPtr hwnd);
    const int WM_CLIPBOARDUPDATE = 0x031D;

    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        AddClipboardFormatListener(Handle);
    }

    protected override void OnHandleDestroyed(EventArgs e)
    {
        // Unregister symmetrically to match handle destruction / recreation
        RemoveClipboardFormatListener(Handle);
        base.OnHandleDestroyed(e);
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_CLIPBOARDUPDATE)
        {
            // Read Clipboard.GetDataObject() here and import if the format is one you need
        }
        base.WndProc(ref m);
    }
}

6.2. Thử lại khi bạn không mở được

Chỉ một cửa sổ tại một thời điểm có thể mở clipboard; trong khi tiến trình khác đang mở, OpenClipboard thất bại.2 Ngay sau WM_CLIPBOARDUPDATE, nguồn sao chép hoặc watcher khác thường vẫn đang thao tác, nên lần đọc thất bại tạm thời là sự kiện bình thường. Luôn đặt vài lần thử lại với chờ ngắn (vài chục mili giây) ở giữa. Lưu ý rằng các overload Clipboard của .NET cho phép bạn chỉ định số lần thử lại và khoảng chỉ tồn tại ở phía ghi, SetDataObject. Không có tương đương phía đọc (GetDataObject và bạn), nên bạn tự viết bắt-chờ-thử lại — ExternalException trên WinForms, COMException trên WPF.

Luồng thử lại đọc clipboardChỉ một cửa sổ tại một thời điểm có thể mở clipboard, nên lần đọc ngay sau thông báo thay đổi có thể thất bại vì đua với tiến trình khác. Khi ngoại lệ, chờ vài chục mili giây rồi thử lại; nếu chạm giới hạn, bỏ lần này và nhặt ở cập nhật tiếpthành côngđang dùngthử lạigiới hạnWM_CLIPBOARDUPDATEThử đọcNhập(kiểm tra Ch. 4)Chờ vài chục msBỏ lần này

6.3. Giữ khỏi lịch sử và đồng bộ — Chăm sóc tính năng sao chép xử lý bí mật

Windows có lịch sử clipboard (Win+V) và đồng bộ xuyên thiết bị (clipboard đám mây), và dữ liệu ứng dụng đặt mặc định nằm trong phạm vi cả hai. Ứng dụng đặt bí mật như mật khẩu hoặc số tài khoản trên tính năng sao chép cũng đặt một định dạng đã đăng ký loại nội dung khỏi lịch sử và đồng bộ.1

  • ExcludeClipboardContentFromMonitorProcessing: Đặt cái này và nội dung lần sao chép đó không được gồm vào lịch sử lẫn đồng bộ.
  • CanIncludeInClipboardHistory (DWORD 0): Chỉ ức chế lịch sử.
  • CanUploadToCloudClipboard (DWORD 0): Chỉ ức chế đồng bộ xuyên thiết bị.

Lý do mật khẩu do trình quản lý mật khẩu sao chép không ở lại trên Win+V là cơ chế này. Bạn nhận ID định dạng bằng cách truyền tên cho RegisterClipboardFormat rồi đặt nó cạnh dữ liệu thông thường, nên đáng triển khai trong mọi ứng dụng nghiệp vụ xử lý bí mật.

7. Clipboard từ góc độ IT — Lịch sử, đồng bộ đám mây, và điều khiển RDP

Bước ra một chút khỏi phát triển, đây là các điểm quan trọng với quản trị viên. Lịch sử clipboard tích các lần sao chép gần, và clipboard đám mây đồng bộ các lần sao chép giữa các thiết bị đăng nhập cùng tài khoản Microsoft / tài khoản Microsoft Entra.10 Tiện như vậy, nó cũng tạo cặn và tràn: thông tin cá nhân sao chép từ hệ thống nghiệp vụ tích trong lịch sử, và nội dung sao chép trên PC công việc đồng bộ sang PC cá nhân.

Hai chính sách bạn dùng để điều khiển điều này trong tổ chức là các cái sau.

Thứ bạn điều khiển GPO (Computer Configuration > Administrative Templates > System > OS Policies) Policy CSP (Intune) Mặc định
Lịch sử clipboard Allow Clipboard History Experience/AllowClipboardHistory Được phép
Đồng bộ xuyên thiết bị Allow Clipboard synchronization across devices Privacy/AllowCrossDeviceClipboard Được phép

Cả hai có từ Windows 10 phiên bản 1809 trở đi; tắt chúng thì các mục tương ứng trong ứng dụng Settings bị xám, và chính sách có hiệu lực ngay.910

Món chủ lực kia là chuyển hướng clipboard RDP (Remote Desktop). Mặc định, sao chép-và-dán hoạt động giữa PC cục bộ và phiên từ xa, nên nó có thể trở thành đường lấy bí mật ra khỏi máy chủ. Chính sách “Do not allow clipboard redirection” (giá trị registry fDisableClip) có thể chặn cả hai hướng.11 Các bản Windows Server / Windows 11 gần đây cũng đã thêm chính sách mịn hơn, như hạn chế hướng máy chủ-sang-client chỉ văn bản. Liệu bạn cấm hẳn hay hạn chế theo giai đoạn là sự cân bằng giữa vận hành và bảo mật.

Các đường nội dung clipboard có thể lan, và các điểm điều khiểnNội dung đã sao chép mặc định nằm trong phạm vi lịch sử và đồng bộ đám mây, và trên RDP chúng đi sang phiên khác qua chuyển hướng. Mỗi đường có thể được điều khiển bằng chính sách, và phía ứng dụng có thể tự loại khỏi lịch sử và đồng bộ bằng các định dạng loại trừClipboardLịch sử(Win+V)Đồng bộ đám mâyChuyển hướng RDPAllowClipboardHistoryAllowCrossDeviceClipboardfDisableClipĐịnh dạng loại trừ ứng dụng(6.3)

8. Kéo thả là COM — IDataObject + IDropSource + IDropTarget

8.1. Cùng dữ liệu với clipboard, cách mang khác

Kéo thả OLE chạy với ba vai sau.12

Vai Ai triển khai Việc
IDataObject Nguồn kéo Payload đang được mang. Cùng đối tượng dữ liệu đa định dạng với clipboard
IDropSource Nguồn kéo Quyết liệu lần kéo tiếp tục hay bị hủy, và phản hồi con trỏ
IDropTarget Đích thả Tuyên bố chấp nhận/từ chối trong DragEnter/DragOver/DragLeave/Drop, và nhận lần thả

Nguồn kéo gọi DoDragDrop, vòng kéo bắt đầu, và khi chuột vào cửa sổ đích thả thì IDropTarget đó được thông báo; khi thả, IDataObject được giao. Tài liệu chính thức cũng nói rằng “D&D cung cấp đúng cùng chức năng với sao chép-và-dán clipboard. Nếu ứng dụng đã triển khai sao chép-và-dán, phần thêm là nhỏ”.12 Nói cách khác, DataObject đa định dạng bạn đã xây ở Chương 2 tới 5 trở thành payload D&D nguyên như vậy.

Luồng kéo thả OLENguồn kéo đặt IDataObject vào payload rồi gọi DoDragDrop để bắt đầu vòng kéo; IDropTarget của đích thả tuyên bố chấp nhận/từ chối trong DragEnter và DragOver, và khi Drop chọn một định dạng từ IDataObject rồi tríchDoDragDropchuột vàonhả nútIDataObject + IDropSourceVòng kéoDragEnter/Over: EffectIDropTarget.DropChọn định dạng rồi trích

8.2. OleInitialize (STA) là bắt buộc

Cửa sổ sẽ là đích thả đăng ký bằng RegisterDragDrop, và có một cái bẫy kinh điển ở đây. Nếu bạn khởi tạo COM bằng CoInitialize/CoInitializeEx, RegisterDragDrop luôn thất bại với E_OUTOFMEMORY; bạn phải khởi tạo bằng OleInitialize.13 OleInitialize khởi tạo COM như STA, vì D&D là tính năng bám thế giới STA của cửa sổ và bơm thông điệp. Luồng gọi cũng phải đang chạy bơm thông điệp; bỏ qua thì ứng dụng khác treo trong lúc kéo.13 Bối cảnh ở đây chính là thảo luận mô hình luồng trong “Nền tảng COM STA/MTA”.

Trong ứng dụng WinForms/WPF khung lo việc khởi tạo OLE và các triển khai giao diện, nên nhà phát triển chỉ phải viết các sự kiện.

// WinForms: accept dropped files
listView1.AllowDrop = true;
listView1.DragEnter += (s, e) =>
{
    // Also check that the source allows Copy (some sources only allow Move/Link)
    e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop)
            && (e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy
        ? DragDropEffects.Copy      // Accept: receive as a copy
        : DragDropEffects.None;     // Do not accept
};
listView1.DragDrop += (s, e) =>
{
    // Drag data is also untrusted input. Even if it advertises FileDrop, the payload
    // can be null or a different type, and GetData itself can fail
    object data;
    try { data = e.Data.GetData(DataFormats.FileDrop); }
    catch (COMException) { return; }
    if (data is not string[] paths) return;
    foreach (var path in paths)
    {
        // Validate the path before importing (Section 9.3)
    }
};

Hình dạng giống nhau trong WPF: bạn nhận bằng AllowDrop="True" và các sự kiện DragOver/Drop trên phần tử, và bạn trích mảng đường dẫn bằng e.Data.GetData(DataFormats.FileDrop). Tuyên bố chấp nhận/từ chối (Effect) ở mọi DragEnter/DragOver là quy ước IDropTarget; bỏ qua thì bạn gặp lỗi con trỏ ở lại “không được phép” và không bao giờ đổi.

9. Những cái bẫy D&D — Nâng quyền, Move, và kiểm tra đường dẫn

9.1. Bạn không thể thả lên ứng dụng đã nâng quyền quản trị

Thả tệp từ File Explorer lên ứng dụng khởi chạy bằng “Run as administrator” thì không có gì xảy ra — đây không phải lỗi triển khai, đó là hành vi hệ điều hành. UIPI (User Interface Privilege Isolation) mặc định chặn thông điệp từ tiến trình toàn vẹn thấp hơn tới cửa sổ toàn vẹn cao hơn, nên thông báo thả từ File Explorer đặc quyền thông thường (toàn vẹn trung bình) không bao giờ tới ứng dụng đã nâng quyền.14

Cách UIPI chặn thả lên ứng dụng đã nâng quyềnThông báo thả từ File Explorer toàn vẹn trung bình tới ứng dụng đã nâng quyền toàn vẹn cao mặc định bị UIPI chặn và không bao giờ tới. Giữ UI ở đặc quyền thông thường rồi cô lập việc đặc quyền, và lần thả tớithông báo thảbị chặnđi quaủy thác việc đặc quyềnExplorer(trung bình)UIPIỨng dụng đã nâng quyền: không thảUI thông thường: thả tớiTiến trình nâng quyền đã cô lập

Cách vòng cho phép riêng các thông điệp cụ thể như WM_DROPFILES bằng ChangeWindowMessageFilterEx được biết rõ,14 nhưng thứ điều đó cho qua là thông báo thả cũ hơn (WM_DROPFILES); nó không giải D&D OLE như một khối. Hướng dẫn thực tiễn rõ: thôi thiết kế ứng dụng chạy nâng quyền suốt. Cô lập chỉ việc cần nâng quyền vào tiến trình riêng, và bản thân UI có thể ở lại đặc quyền thông thường rồi nhận D&D (thiết kế cô lập được trình bày chi tiết trong “Cách cô lập cụ thể “chỉ những thao tác cần đặc quyền quản trị” trong ứng dụng Windows”).

9.2. DragDropEffects nghĩa là gì — Move là hợp đồng rằng “bản gốc biến mất”

Copy/Move/Link trên DragDropEffects không phải trang trí; chúng là hợp đồng giữa nguồn kéo và đích thả. Nguồn kéo tuyên bố tập hiệu ứng nó cho phép trong DoDragDrop, đích thả chọn hiệu ứng thật, và khi Move thành công, nguồn kéo xóa dữ liệu (tệp) — đó là quy ước. Nếu phía nhận vô tư trả về Move, bạn gặp tai nạn “tôi thả rồi tệp gốc biến mất”. Với mục đích nhập của ứng dụng nghiệp vụ, phía nhận tuyên bố Copy là mặc định an toàn.

Hợp đồng DragDropEffects — Move xóa bản gốcNguồn kéo tuyên bố tập hiệu ứng được phép trong DoDragDrop, và đích thả chọn hiệu ứng thật. Khi Move thành công nguồn kéo xóa tệp, nên với nhập phía nhận nên tuyên bố CopyCopyMoveNguồn: hiệu ứng được phépĐích: chọn EffectBản gốc còn(nhập)Nguồn xóa tệp

9.3. Kiểm tra đường dẫn đã thả

Thứ đi trong CF_HDROP/FileDrop chỉ là đường dẫn (Mục 3.2). Trước khi nhập, hãy đưa nó qua cùng kiểm tra đầu vào không tin cậy như dán.

  • Tệp hay thư mục: Quyết như đặc tả điều gì xảy ra khi cả thư mục được thả (đệ quy rồi nhập, hoặc từ chối).
  • Placeholder OneDrive: Đường dẫn có thể tồn tại trong khi thân tệp không cục bộ — tệp theo yêu cầu. Khoảnh khắc bạn mở nó một lần tải bắt đầu, và khi ngoại tuyến thì thất bại. Hành vi và đối sách nằm trong “OneDrive “Files On-Demand” và ứng dụng nghiệp vụ”.
  • Đường dẫn dài và đường dẫn khác thường: Đường dẫn vượt MAX_PATH, đường dẫn mạng (UNC), và đường dẫn trên phương tiện tháo được chỉ nên được chấp nhận sau khi bạn đã xác nhận xử lý phía sau xử lý được chúng.
  • Số lượng và tổng kích thước: Để thả hàng nghìn tệp không đóng băng UI, hãy làm việc nhập bất đồng bộ và đặt giới hạn cùng hiển thị tiến độ.

10. Tóm tắt

  • Clipboard là cơ chế đặt cùng nội dung ở nhiều định dạng cùng lúc trong một vùng dùng chung trong cùng desktop (window station). Phía dán chọn định dạng, nên cùng lần sao chép cho kết quả khác.
  • Văn bản là CF_UNICODETEXT, tệp là CF_HDROP, và văn bản đã định dạng là định dạng đã đăng ký HTML Format (đầu đề offset byte + UTF-8).
  • Phía dán nhìn từ giàu xuống thuần và coi payload như đầu vào bên ngoài. Phía sao chép cung cấp nhiều định dạng cùng lúc, và nếu dùng kết xuất trì hoãn thì cũng triển khai hiện thực hóa lúc thoát (WM_RENDERALLFORMATS / OleFlushClipboard).
  • Theo dõi là AddClipboardFormatListener + WM_CLIPBOARDUPDATE. Chuẩn bị cho đua OpenClipboard bằng thử lại, và giữ bí mật khỏi lịch sử và đồng bộ bằng ExcludeClipboardContentFromMonitorProcessing và bạn.
  • IT có thể điều khiển lịch sử clipboard, đồng bộ đám mây, và chuyển hướng RDP bằng GPO / Intune. Mặc định là được phép cho tất cả, nên hãy quyết có chủ ý trong môi trường xử lý bí mật.
  • D&D là COM: IDropSource/IDropTarget giao cùng IDataObject với clipboard. RegisterDragDrop đòi OleInitialize (STA).
  • Lần thả lên ứng dụng đã nâng quyền bị UIPI chặn. Move trên DragDropEffects là hợp đồng rằng “bản gốc biến mất”; kiểm tra đường dẫn đã thả trước khi nhập.

Sao chép-và-dán và D&D, với người dùng, là tính năng nên cảm thấy như không khí. Chính vì vậy “tôi không dán được”, “nó tan”, và “nó biến mất” làm tổn thương trải nghiệm nhiều đến thế — và vì sao ứng dụng cung cấp nhiều định dạng và xử lý thả đúng tự nó làm thao tác hàng ngày mượt hơn. Tôi hy vọng đây là tư liệu hữu ích khi bạn quyết sửa gì trước.

Bài viết liên quan

Lĩnh vực tư vấn liên quan

KomuraSoft LLC đảm nhận thiết kế và triển khai hỗ trợ sao chép-và-dán cùng kéo thả trong ứng dụng nghiệp vụ (cung cấp nhiều định dạng, tương tác Excel, nhập tệp đã thả), điều tra nguyên nhân gốc rễ của vấn đề như “nó tan khi tôi dán” hoặc “bản sao biến mất”, tự động hóa đầu vào theo dõi clipboard, và triển khai giữ dữ liệu mật khỏi lịch sử và đồng bộ. Các vụ liên quan tầng thấp hơn của COM và OLE được chào đón ngay cả nếu bạn bắt đầu từ cô lập triệu chứng.

Liên kết tham khảo

  1. Microsoft Learn, Clipboard Formats. Về việc một cửa sổ có thể đặt cùng thông tin ở nhiều định dạng clipboard; định dạng đã đăng ký qua RegisterClipboardFormat (đăng ký cùng tên trả về cùng giá trị, nên các ứng dụng có thể chia); định dạng tổng hợp; và loại nội dung khỏi lịch sử clipboard / đồng bộ đám mây bằng ExcludeClipboardContentFromMonitorProcessing, CanIncludeInClipboardHistory và CanUploadToCloudClipboard.  2 3 4 5

  2. Microsoft Learn, Clipboard Operations. Về chỉ một cửa sổ tại một thời điểm có thể mở clipboard; đặt định dạng từ biểu đạt nhiều hơn xuống ít hơn lúc sao chép; chọn định dạng lúc dán bằng EnumClipboardFormats / GetPriorityClipboardFormat; kết xuất trì hoãn bằng cách truyền NULL cho SetClipboardData và trách nhiệm WM_RENDERFORMAT / WM_RENDERALLFORMATS; và các sự đánh đổi của kết xuất trì hoãn.  2 3 4 5 6 7 8

  3. Microsoft Learn, Standard Clipboard Formats. Về định nghĩa các định dạng chuẩn CF_TEXT (ANSI), CF_UNICODETEXT, CF_HDROP, CF_DIB và CF_LOCALE, và về hệ thống chuyển ngầm CF_TEXT và CF_UNICODETEXT dùng trang mã gắn với CF_LOCALE.  2 3

  4. Microsoft Learn, Shell Clipboard Formats. Về CF_HDROP gồm cấu trúc DROPFILES cộng mảng chuỗi đường dẫn đầy đủ kết thúc bằng NUL kép; lấy từng đường dẫn bằng DragQueryFile; và các định dạng shell CFSTR_ đòi đăng ký qua RegisterClipboardFormat.  2

  5. Microsoft Learn, HTML Clipboard Format. Về tên đã đăng ký là “HTML Format”; cấu trúc đầu đề với offset byte như Version, StartHTML, EndHTML, StartFragment và EndFragment; mã hóa luôn là UTF-8; và quy ước chú thích StartFragment/EndFragment.  2 3

  6. Microsoft Learn, OleFlushClipboard function (ole2.h). Về OleSetClipboard làm clipboard chỉ giữ con trỏ tới đối tượng dữ liệu; OleFlushClipboard hiện thực hóa dữ liệu lên clipboard nên dán vẫn hoạt động sau khi ứng dụng thoát; và làm trống clipboard bằng OleSetClipboard(NULL) khi bạn không cần giữ lúc thoát.  2

  7. Microsoft Learn, OleGetClipboard function (ole2.h). Về cách lấy IDataObject từ clipboard, và cảnh báo rằng dữ liệu clipboard không được tin và nên được phân tích cẩn thận trước khi ứng dụng dùng.  2

  8. Microsoft Learn, Using the clipboard. Về so sánh ba cách theo dõi clipboard (cửa sổ viewer, số thứ tự, và listener định dạng); chương trình mới được kỳ vọng dùng listener qua AddClipboardFormatListener; chuỗi viewer mong manh khi bảo trì chuỗi không đầy đủ; và số thứ tự không phải thứ bạn nên thăm dò.  2

  9. Microsoft Learn, Policy CSP - Experience. Về cho phép hoặc từ chối lịch sử clipboard bằng chính sách Experience/AllowClipboardHistory; có từ Windows 10 phiên bản 1809 trở đi; mặc định là được phép; và ánh xạ GPO dưới “System > OS Policies” với thay đổi có hiệu lực ngay.  2

  10. Microsoft Learn, Policy CSP - Privacy. Về cho phép hoặc từ chối đồng bộ clipboard xuyên thiết bị bằng chính sách Privacy/AllowCrossDeviceClipboard; đồng bộ xảy ra giữa các thiết bị đăng nhập cùng tài khoản Microsoft / tài khoản Microsoft Entra; và mặc định là được phép.  2 3

  11. Microsoft Learn, Policy CSP - ADMX_TerminalServer. Về TS_CLIENT_CLIPBOARD (“Do not allow clipboard redirection”, giá trị registry fDisableClip) có thể cấm chia sẻ clipboard giữa cục bộ và từ xa trong phiên Remote Desktop, và về chuyển hướng được phép mặc định.  2

  12. Microsoft Learn, Drag and Drop (COM). Về kéo thả OLE chạy với ba thứ IDropSource (nguồn kéo), IDropTarget (đích thả) và DoDragDrop (vòng OLE cung cấp); cung cấp cùng chức năng với sao chép-và-dán clipboard, nên ứng dụng đã triển khai sao chép-và-dán chỉ cần phần thêm nhỏ; và các loại phản hồi.  2 3

  13. Microsoft Learn, RegisterDragDrop function (ole2.h). Về đăng ký cửa sổ đích thả bằng IDropTarget; luôn thất bại với E_OUTOFMEMORY nếu COM được khởi tạo bằng CoInitialize/CoInitializeEx, nên OleInitialize là bắt buộc; và ứng dụng nguồn kéo treo nếu luồng gọi không đang chạy bơm thông điệp.  2 3

  14. Microsoft Learn, ChangeWindowMessageFilterEx function (winuser.h). Về UIPI là cơ chế bảo mật mặc định chặn nhận thông điệp từ bên gửi toàn vẹn thấp hơn, và về cho phép thông điệp cụ thể theo từng cửa sổ bằng bộ lọc thông điệp (MSGFLT_ALLOW).  2 3

  15. Microsoft Learn, How to add data to the Clipboard (Windows Forms). Về đặt dữ liệu ở nhiều định dạng cùng lúc bằng DataObject và Clipboard.SetDataObject; thêm ở nhiều định dạng để ứng dụng khác nhận ra; và lớp Clipboard chỉ dùng được từ luồng STA, nên [STAThread] là bắt buộc.  2

Các bài viết gần đây có cùng thẻ để tìm hiểu sâu hơn những chủ đề lân cận.

Các trang này đặt chủ đề trong bối cảnh rộng hơn của dịch vụ và quyết định.

Bài viết liên quan trực tiếp đến các dịch vụ sau.

Câu hỏi thường gặp

Các câu hỏi thường gặp khi tư vấn về chủ đề của bài viết.

Vì sao định dạng bảng sao chép từ Excel tan khi tôi dán vào ứng dụng của mình?
Clipboard không giữ "một mảnh dữ liệu". Cùng nội dung được đặt ở vài định dạng cùng lúc (định dạng riêng của ứng dụng nguồn, HTML Format, CSV, văn bản Unicode, và tương tự), và ứng dụng đích chọn một định dạng nó hiểu rồi trích cái đó. Khi định dạng tan, nguyên nhân điển hình là đích chỉ đọc văn bản thuần (CF_UNICODETEXT). Nếu bạn muốn cả cấu trúc bảng, hãy triển khai phía dán sao cho nó ưu tiên HTML Format hoặc CSV. Ngược lại, nếu bạn muốn ứng dụng khác dán đúng từ lần sao chép trong ứng dụng của bạn, hãy cung cấp cả định dạng giàu lẫn định dạng thuần lúc sao chép.
Vì sao tôi không còn dán được sau khi đóng ứng dụng tôi đã sao chép từ đó?
Vì nguồn đang dùng kết xuất trì hoãn. Ứng dụng xử lý dữ liệu lớn không đặt payload lúc sao chép; chúng chỉ đăng ký trên clipboard một lời hứa rằng chúng sẽ "tạo khi được hỏi". Nếu nguồn rồi thoát mà không hiện thực hóa dữ liệu để đáp WM_RENDERALLFORMATS lúc tắt, mọi định dạng chưa được kết xuất đều mất. Ứng dụng dùng clipboard OLE (IDataObject) có thể giữ dán hoạt động sau khi thoát bằng cách gọi OleFlushClipboard lúc tắt để hiện thực hóa dữ liệu.
Ứng dụng của tôi theo dõi thay đổi clipboard bằng cách nào?
Phương pháp hiện được khuyến nghị là đăng ký cửa sổ của bạn làm listener bằng AddClipboardFormatListener và xử lý thông điệp WM_CLIPBOARDUPDATE tới mỗi khi nội dung đổi. Thăm dò nội dung trên bộ hẹn giờ lãng phí việc và có thể bỏ lỡ cập nhật, và chuỗi viewer cũ dựa trên SetClipboardViewer chỉ được giữ vì tương thích ngược, vì lỗi ở một ứng dụng trong chuỗi làm hỏng cả chuỗi. Cũng lưu ý rằng OpenClipboard lúc đọc có thể thất bại vì tiến trình khác đang giữ clipboard, nên hãy triển khai thử lại với chờ ngắn nếu bạn muốn lần đọc ổn định.
Có cách nào giữ bí mật như mật khẩu khỏi lịch sử clipboard (Win+V) không?
Có hai đòn bẩy, một phía ứng dụng và một phía chính sách. Phía ứng dụng, nếu bạn cũng đặt định dạng đã đăng ký ExcludeClipboardContentFromMonitorProcessing khi sao chép, nội dung đó không được gồm vào lịch sử lẫn đồng bộ xuyên thiết bị. Bạn cũng có thể điều khiển từng cái độc lập bằng CanIncludeInClipboardHistory (chỉ lịch sử) và CanUploadToCloudClipboard (chỉ đồng bộ). Đây là cơ chế trình quản lý mật khẩu dùng. Nếu bạn muốn tắt cho cả tổ chức, bạn có thể tắt bản thân lịch sử và đồng bộ đám mây bằng AllowClipboardHistory và AllowCrossDeviceClipboard qua Group Policy hoặc Intune (Policy CSP).
Vì sao tôi không thể kéo thả tệp lên ứng dụng đang chạy với quyền quản trị?
Vì cơ chế bảo mật gọi là UIPI (User Interface Privilege Isolation) chặn gửi thông điệp từ tiến trình toàn vẹn thấp hơn tới cửa sổ toàn vẹn cao hơn. File Explorer chạy ở đặc quyền thông thường (toàn vẹn trung bình), nên thông báo kéo thả không bao giờ tới cửa sổ của ứng dụng đã nâng quyền. Cách vòng được biết rõ là cho phép riêng các thông điệp như WM_DROPFILES bằng ChangeWindowMessageFilterEx, nhưng nó chỉ áp dụng cho thông báo thả cũ hơn. Sửa thật sự là thôi thiết kế ứng dụng chạy nâng quyền suốt, và cô lập chỉ việc cần nâng quyền vào tiến trình riêng.

Hồ sơ tác giả

Trang giới thiệu tác giả bài viết.

Go Komura

Đại diện của KomuraSoft LLC

Chuyên về phát triển phần mềm Windows, tư vấn kỹ thuật và điều tra lỗi, đặc biệt trong các dự án có hệ thống hiện hữu và lỗi khó tái hiện.

Liên kết công khai

Quay lại blog