Cara kerja clipboard dan seret-dan-lepas — menangani transfer data OLE dengan benar di aplikasi bisnis
· Go Komura · Windows, Clipboard, Seret dan lepas, OLE, COM, Pengembangan Windows, WinForms, WPF
“Ketika kita menempel tabel yang disalin dari Excel, pemformatan berantakan. Kita ingin ia menempel sebagai tabel.” “Konten yang kita salin di aplikasi kita menjadi sesuatu yang aneh ketika kita menempelnya ke Word.” “Kita ingin dapat menerima berkas lewat seret dan lepas.” — Dalam percakapan konsultasi tentang perubahan aplikasi bisnis, permintaan seputar salin-dan-tempel serta seret dan lepas (D&D) adalah makanan pokok.
Justru karena ini adalah “fitur yang semua orang anggap sudah tentu”, cara kerjanya sebenarnya mengejutkan sedikit diketahui. Jika Anda menganggap clipboard sebagai “kotak tempat Anda menaruh satu potong data”, Anda tidak dapat menjelaskan mengapa salinan yang sama menghasilkan hasil berbeda tergantung ke mana Anda menempel, atau mengapa tempel berhenti bekerja setelah Anda menutup aplikasi sumber. Clipboard yang sungguhan adalah mekanisme yang menaruh konten yang sama dalam beberapa format sekaligus, dan membiarkan sisi tempel memilih format yang dipahaminya.
Dan seret dan lepas, pada dasarnya, adalah transfer data OLE yang menyerahkan persis representasi data yang sama dengan clipboard (IDataObject), lewat antarmuka COM. Dengan kata lain, salin-dan-tempel dan D&D adalah saudara: pahami satu dengan benar dan yang lain ada di situ.
Artikel ini ditujukan kepada staf IT di perusahaan kecil dan menengah serta pengembang aplikasi Windows. Ia mengikat, dalam satu gambaran, cara kerja format clipboard, praktik di sisi tempel dan sisi salin, cara yang benar mengawasi clipboard, kebijakan administratif untuk riwayat clipboard, sinkronisasi awan, dan RDP, serta struktur dan jebakan seret-dan-lepas OLE.
1. Kesimpulan lebih dulu
- Clipboard adalah satu area yang dibagi oleh aplikasi di desktop yang sama (window station), dan yang duduk di sana bukan “satu potong data” melainkan konten yang sama dalam beberapa format sekaligus. Sesi berbeda, seperti RDP, semula punya clipboard berbeda; fitur pengalihan-lah yang menjembatani keduanya. Karena tujuan memilih format yang dipahaminya, salinan yang sama menghasilkan hasil berbeda tergantung ke mana Anda menempel.12
- Untuk teks, pakai CF_UNICODETEXT. CF_TEXT adalah ANSI dan bergantung pada code page, dan pada sistem Jepang ia adalah tempat berkembang biaknya mojibake. Sistem mengonversi antara keduanya secara implisit, tetapi sisi kanoniknya adalah Unicode.3
- Berkas berjalan sebagai CF_HDROP (larik path yang diakhiri double-NUL), dan teks terformat memakai format terdaftar “HTML Format”. HTML Format punya struktur yang tidak biasa: teks UTF-8 dengan header offset byte.45
- Penyebab sungguhan “saya menutup aplikasi sumber dan tidak dapat menempel lagi” adalah delayed rendering. Itu mekanisme yang menaruh bukan payload melainkan hanya janji untuk “menghasilkannya ketika diminta”; jika Anda melewatkan mewujudkannya saat keluar (merespons WM_RENDERALLFORMATS, atau OleFlushClipboard untuk OLE), tempel berhenti bekerja.26
- Perlakukan data yang ditempel sebagai input tidak tepercaya dari luar. Microsoft sendiri menyatakan dengan gamblang bahwa “data clipboard tidak tepercaya. Parse dengan hati-hati”.7
- Untuk mengawasi clipboard, AddClipboardFormatListener + WM_CLIPBOARDUPDATE adalah satu-satunya opsi. Jangan pakai polling, dan jangan pakai SetClipboardViewer lama (rantai viewer). Format terdaftar yang menjaga rahasia keluar dari riwayat dan sinkronisasi (ExcludeClipboardContentFromMonitorProcessing dan kawan-kawannya) juga disediakan.81
- Riwayat clipboard (Win+V) dan sinkronisasi awan adalah perhatian manajemen IT. Anda dapat mengendalikannya dengan AllowClipboardHistory dan AllowCrossDeviceClipboard lewat GPO / Intune (Policy CSP), dan pengalihan clipboard RDP punya kebijakan khusus sendiri.91011
- Seret dan lepas adalah COM. IDataObject yang sama dengan clipboard diserahkan antara IDropSource (sumber seret) dan IDropTarget (target lepas) lewat loop DoDragDrop. RegisterDragDrop mensyaratkan inisialisasi dengan OleInitialize (STA).1213
- Anda tidak dapat melepas dari File Explorer berhak biasa ke aplikasi yang di-elevate. UIPI (pemblokiran pesan menurut tingkat integritas) adalah penyebabnya, dan itu kendala yang harus Anda ketahui pada waktu desain.14
Di bawah kita menelusuri ini dari fondasi clipboard ke atas.
2. Apa sebenarnya clipboard — bukan “satu potong data” melainkan “konten yang sama dalam beberapa format”
Clipboard adalah mekanisme berbagi data bersama yang setiap aplikasi yang berbagi desktop yang sama dapat jangkau (lebih tepatnya, ia per window station: sesi pengguna berbeda atau sesi RDP masing-masing punya clipboard sendiri. Salin-dan-tempel bekerja lewat RDP karena fitur pengalihan menjembatani keduanya — Bab 7). Prinsip pertama adalah ia didorong pengguna: sikap desain resmi adalah bahwa Anda tidak menaruh data masuk atau mengambil data keluar di belakang punggung pengguna.1
Poin pentingnya adalah bahwa salinan tidak menaruh “satu potong data”. Jendela yang menyalin mengosongkan clipboard lalu menaruh beberapa format berurutan, mengekspresikan konten yang sama dari format yang lebih mampu ke yang kurang mampu.2 Misalnya, ketika Anda menyalin tabel di spreadsheet, secara konseptual sesuatu seperti berikut ada di clipboard pada saat yang sama.
| Prioritas | Format | Isi |
|---|---|---|
| 1 | Format privat aplikasi | Representasi internal lengkap, termasuk rumus dan pemformatan (untuk tempel kembali ke aplikasi yang sama) |
| 2 | HTML Format | Fragmen HTML yang menjaga struktur tabel dan pemformatan |
| 3 | CSV | Teks yang dibatasi sel |
| 4 | CF_UNICODETEXT | Teks biasa yang dipisah tab |
| 5 | Format gambar | Bitmap bagaimana tabel terlihat |
Sisi tempel memilih format yang dipahaminya dari daftar ini dan mengekstraknya. Tempel ke Word dan Anda mendapat tabel terformat; tempel ke Notepad dan Anda mendapat teks yang dipisah tab — karena keduanya memilih format berbeda. “Hasilnya tergantung ke mana Anda menempel” bukan bug; itu konsekuensi normal desain ini.
flowchart TB
accTitle: Mengapa salinan yang sama menghasilkan hasil berbeda tergantung ke mana Anda menempel
accDescr: Sisi salin menaruh konten yang sama di clipboard dalam beberapa format, dan sisi tempel memilih format yang dipahaminya, jadi Word mendapat tabel terformat dan Notepad mendapat teks yang dipisah tab
copy["Salin: spreadsheet"] --> cb["Clipboard (banyak format)"]
cb --> rich["Format lebih kaya"]
cb --> plain["Format lebih biasa"]
rich --> f1["Privat aplikasi"]
rich --> f2["HTML Format"]
plain --> f3["CSV"]
plain --> f4["CF_UNICODETEXT"]
f2 -->|"Word"| word["Tabel terformat"]
f4 -->|"Notepad"| notepad["Teks yang dipisah tab"]
Dibalik, keluhan pembuka — “pemformatan berantakan”, “sesuatu yang aneh tertempel” — hampir semuanya turun ke masalah bagaimana satu sisi memilih format, atau bagaimana sisi lain menawarkannya. Bab 4 membahas sisi tempel; Bab 5 membahas sisi salin.
3. Format standar dan format terdaftar — CF_UNICODETEXT, CF_HDROP, HTML Format
3.1. Format standar — pakai sisi Unicode untuk teks
Format yang OS tetapkan di muka disebut format standar. Yang terus muncul di aplikasi bisnis adalah berikut.3
| Format | Nilai | Isi |
|---|---|---|
| CF_TEXT | 1 | Teks ANSI (bergantung code page) |
| CF_UNICODETEXT | 13 | Teks Unicode. Ini format kanonik untuk teks |
| CF_HDROP | 15 | Daftar path berkas (handle HDROP) |
| CF_DIB | 8 | Bitmap independen perangkat |
| CF_LOCALE | 16 | Pengidentifikasi locale yang terkait dengan teks |
CF_TEXT dan CF_UNICODETEXT dikonversi satu sama lain secara implisit oleh sistem (format yang disintesis). Konversi kode karakter memakai code page yang terkait dengan CF_LOCALE.3 Mengandalkan konversi itu menjatuhkan karakter yang ANSI tidak dapat wakili (misalnya simbol hanya-Unicode dan karakter penggabung), jadi aturannya adalah satukan apa yang aplikasi baca dan tulis pada CF_UNICODETEXT (DataFormats.UnicodeText di .NET).
flowchart TB
accTitle: Konversi implisit antara CF_UNICODETEXT dan CF_TEXT
accDescr: Aplikasi membaca dan menulis hanya CF_UNICODETEXT; sistem mensintesis CF_TEXT lewat konversi implisit dengan code page CF_LOCALE. Karakter yang ANSI tidak dapat wakili dijatuhkan dalam konversi itu
apprw["Aplikasi membaca dan menulis"] --> uni["CF_UNICODETEXT"]
uni <-->|"Konversi CF_LOCALE"| ansi["CF_TEXT (ANSI)"]
ansi -.-> loss["Karakter yang tidak dapat diwakili dijatuhkan"]
3.2. CF_HDROP — berkas berjalan sebagai “daftar path”
CF_HDROP adalah yang dipakai ketika Anda menyalin berkas di File Explorer, atau ketika Anda menyeret dan melepas berkas. Payload-nya bukan berkas itu sendiri; itu blok memori yang menata larik “diakhiri double-NUL”: setelah header struktur DROPFILES, string path lengkap yang dipisah karakter NUL, dan string kosong di akhir. pFiles header adalah offset awal daftar path, dan fWide mengatakan apakah string-nya Unicode.4
[DROPFILES header: pFiles=start offset of the path list, fWide=1(Unicode)]
C:\data\a.txt(NUL)C:\data\b.txt(NUL)(NUL)
Di kode native Anda menariknya satu per satu dengan DragQueryFile; di .NET Anda menerimanya sebagai string[] lewat DataFormats.FileDrop. Fakta bahwa “yang berjalan hanya path, bukan berkas itu sendiri” akan penting lagi di D&D Bab 8 dan 9.
flowchart TB
accTitle: Tata letak blok memori CF_HDROP
accDescr: Struktur DROPFILES duduk di awal memori global; pFiles adalah offset awal daftar path dan fWide mengatakan apakah Unicode. Path lengkap kemudian mengikuti, dipisah NUL, dan blok berakhir dengan string kosong (double NUL). Yang berjalan hanya path, bukan berkas itu sendiri
hdr["DROPFILES (pFiles / fWide)"] --> p1["C:\\data\\a.txt + NUL"]
p1 --> p2["C:\\data\\b.txt + NUL"]
p2 --> tail["String kosong (double NUL)"]
hdr -.-> note["Hanya path yang berjalan, bukan berkas"]
3.3. Format terdaftar — RegisterClipboardFormat dan “HTML Format”
Untuk data yang format standar tidak dapat ekspresikan, aplikasi dapat memilih nama dan mendaftarkan formatnya sendiri. Lewatkan nama ke RegisterClipboardFormat dan Anda mendapat ID format kembali; mendaftar di bawah nama yang sama dari aplikasi berbeda mengembalikan ID yang sama, jadi begitu Anda sepakat pada nama Anda dapat berbagi data antar aplikasi.1 Ketika Anda melewatkan data terstruktur di antara rangkaian aplikasi Anda sendiri, pakai nama yang tidak akan bertabrakan, seperti KomuraSoft.Report.RowData.
Format terdaftar representatif adalah “HTML Format”, untuk teks terformat (bersama RTF, salah satu dari dua format teks kaya utama). Payload-nya adalah teks UTF-8, tetapi punya struktur yang tidak biasa: header yang mencantumkan offset byte dilekatkan di depan.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>
Setiap offset adalah posisi byte dari awal data, termasuk header itu sendiri; praktik biasa adalah mencadangkan lebar tetap (misalnya 10 digit) dan menulis nilai terukur kembali setelah Anda membangun body. StartFragment/EndFragment menandai awal dan akhir “fragmen yang pengguna sebenarnya pilih” dalam byte (bukan dalam karakter). Di UTF-8 yang mencakup Jepang, jumlah karakter dan jumlah byte menyimpang, jadi jika Anda salah menghitung offset ini, tempel ke aplikasi lain menjatuhkan awal atau akhir. Jika Anda menghasilkan HTML Format sendiri, Anda harus mengisi header dengan posisi byte yang diukur setelah encoding ke UTF-8.5
flowchart TB
accTitle: Bagaimana header HTML Format berhubungan dengan offset
accDescr: StartHTML dan EndHTML header menunjuk ke seluruh HTML, dan StartFragment serta EndFragment menunjuk ke fragmen yang pengguna pilih, keduanya sebagai posisi byte dari awal data. Karena jumlah karakter dan jumlah byte menyimpang di UTF-8, isi header dengan posisi byte yang diukur setelah encoding
header["Header (offset byte)"] --> html["Seluruh HTML"]
html --> frag["Fragmen yang dipilih"]
header -.-> byte["Offset adalah byte setelah UTF-8"]
CSV (DataFormats.CommaSeparatedValue di .NET) juga umum dipakai untuk data tabular. Untuk interop dengan Excel, menawarkan HTML Format (dengan pemformatan), CSV (hanya nilai), dan CF_UNICODETEXT (dipisah tab) bersama berarti Anda tidak harus memilih satu tujuan tempel.
4. Praktik di sisi tempel — prioritas format dan validasi
4.1. Lihat dari format kaya ke bawah
Format di clipboard berjajar dalam urutan sisi salin menaruhnya (yaitu, dari lebih ekspresif ke kurang). Garis dasar sisi tempel adalah melihat, di antara format yang dapat Anda tangani, mulai dari yang punya informasi paling banyak. Di Win32 Anda mengenumerasi dengan EnumClipboardFormats dan memakai format pertama yang Anda kenali, atau Anda melewatkan daftar prioritas sendiri ke GetPriorityClipboardFormat dan membiarkannya memilih.2
Di .NET cabangnya tampak seperti berikut.
// 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
}
Itulah jawaban untuk keluhan pembuka, “menempel tabel Excel berantakan”. Aplikasi yang hanya membaca teks biasa tidak pernah menerima struktur tabel. Seberapa jauh ke bawah daftar format Anda terima adalah keputusan desain di sisi tempel.
flowchart TB
accTitle: Percabangan tempel yang melihat dari format kaya ke bawah
accDescr: Jika HTML Format ada dan payload juga string, impor sebagai tabel; jika tidak coba CSV; jika itu juga hilang, jatuh ke teks yang dipisah tab. Jika tidak ada kandidat yang ada, tolak
startsel["Mulai tempel"] --> h{"HTML Format + string?"}
h -->|"ya"| useh["Validasi header → tabel"]
h -->|"tidak"| c{"CSV ada?"}
c -->|"ya"| usec["Impor sebagai CSV"]
c -->|"tidak"| t{"UnicodeText?"}
t -->|"ya"| uset["Teks yang dipisah tab"]
t -->|"tidak"| giveup["Tolak"]
4.2. Data yang ditempel adalah input eksternal
Mudah terlewat, tetapi isi clipboard adalah data dari luar, dan Anda tidak tahu aplikasi mana yang menaruhnya. Microsoft juga memperingatkan, di dokumentasi clipboard OLE, bahwa “data clipboard tidak tepercaya. Parse dengan hati-hati sebelum Anda memakainya di aplikasi”.7
- Validasi bahwa offset header HTML Format tidak menunjuk ke luar buffer (aplikasi yang mengeluarkan header rusak memang ada).
- Nilai yang Anda impor sebagai angka, tanggal, atau kode harus melalui validasi yang sama seperti input di layar.
- Pasang pertahanan terhadap data raksasa. Bahkan jika seseorang menempel gambar ratusan megabyte atau jutaan baris teks, jangan blokir UI, dan tolak begitu batas terlampaui. Catatan: GetData .NET, pada saat Anda memanggilnya, mewujudkan seluruh payload menjadi string managed (dan delayed rendering berjalan sebagai bagian dari itu), jadi menaruh pemeriksaan ukuran setelah GetData bukan pertahanan. Di Win32, memeriksa GlobalSize pada HGLOBAL yang GetClipboardData kembalikan memang memberi Anda pertahanan pada tahap “jangan lanjut ke konversi dan parsing sebagai string managed”, tetapi untuk format delayed-rendering GetClipboardData itu sendiri memulai rendering, jadi Anda tetap tidak dapat mencegah perwujudan di sisi sumber salin. Untuk menjaga UI tidak membeku, pindahkan pengambilan dari thread UI (dan bahkan kemudian, karena Clipboard .NET mensyaratkan STA, lakukan di thread khusus yang ditetapkan ke STA, bukan di thread thread-pool Task.Run (MTA) — Bagian 5.1).
Gagasan bahwa “nilai yang datang dari luar, apa pun jalurnya, divalidasi sebelum Anda memakainya” sama dengan yang dijabarkan di “Jangan pernah memakai nilai terdekode kode QR apa adanya”. Asumsi bahwa tempel aman karena itu tindakan pengguna adalah bagaimana kecelakaan dimulai.
flowchart TB
accTitle: Validasi data yang ditempel sebelum Anda memakainya
accDescr: Data yang diambil dari clipboard melalui format-ada, jenis-payload, batas-ukuran, dan validasi isi secara berurutan; gagal salah satunya dan Anda menolak atau jatuh ke format kandidat berikutnya
present["Format ada?"] --> type["Jenis payload OK?"]
type --> size["Ukuran dalam batas?"]
size --> content["Validasi isi"]
content --> ok["Impor"]
type -.->|"jenis salah"| rej["Tolak / format berikutnya"]
size -.->|"terlalu besar"| rej
content -.->|"tidak valid"| rej
5. Praktik di sisi salin — menawarkan beberapa format sekaligus, dan delayed rendering
5.1. Taruh beberapa format sekaligus
Praktik sisi salin adalah kebalikan dari 4.1: tawarkan format kaya dan format biasa pada saat yang sama. Dengan DataObject WinForms/WPF Anda dapat menulisnya dalam beberapa baris.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
Dua catatan. Pertama, kelas Clipboard .NET hanya dapat dipakai dari thread STA.15 Thread UI WinForms/WPF adalah STA karena [STAThread], jadi ini biasanya bukan masalah, tetapi menyentuhnya dari thread latar belakang gagal (dasar STA/MTA ada di “Dasar COM STA/MTA”). Kedua, arti copy: true terikat pada delayed rendering di subbagian berikutnya.
5.2. Delayed rendering — mengapa “tutup sumber dan Anda tidak dapat menempel”
Membangun payload besar dalam banyak format setiap kali adalah pemborosan, jadi clipboard punya mekanisme bernama delayed rendering. Lewatkan NULL sebagai handle data ke SetClipboardData dan, alih-alih payload, hanya janji untuk “menghasilkannya ketika diminta” yang didaftarkan; ketika seseorang meminta format itu, WM_RENDERFORMAT datang ke sumber salin, dan baru kemudian data dihasilkan.2
Konsekuensi desain ini adalah “saya menutup aplikasi sumber dan tidak dapat menempel lagi” di pembuka. Sebelum keluar, sumber salin menerima WM_RENDERALLFORMATS dan bertanggung jawab mewujudkan setiap format yang belum dirender; keluar tanpa melakukan itu dan format hilang.2
flowchart TB
accTitle: Delayed rendering dan mengapa tutup-lalu-tempel gagal
accDescr: Sumber salin mendaftarkan hanya janji dengan handle NULL, dan mewujudkan sesuai permintaan lewat WM_RENDERFORMAT. Saat keluar ia bertanggung jawab mewujudkan setiap format dengan WM_RENDERALLFORMATS; lewati itu dan format hilang
promise["SetClipboardData NULL = janji"] --> req["Sisi tempel memintanya"]
req --> render["WM_RENDERFORMAT → bangun sekarang"]
promise --> quit["Sumber salin akan keluar"]
quit -->|"RENDERALLFORMATS"| ok["Tempel bekerja setelah keluar"]
quit -->|"Lewati wujudkan"| lost["Format hilang setelah tutup"]
Di clipboard OLE (gaya yang menaruh IDataObject dengan OleSetClipboard), hubungan ini bahkan lebih jelas. Semua yang clipboard pegang adalah pointer ke objek data, dan memanggil OleFlushClipboard saat keluar aplikasi mewujudkan data ke clipboard, jadi tempel tetap bekerja setelah keluar.6 Clipboard.SetDataObject(data, copy: true) .NET adalah yang menentukan perilaku “pertahankan setelah keluar” ini.
Ketika Anda menyalin rentang besar di Excel dan mencoba keluar, prompt “There is a large amount of information on the Clipboard. Do you want to be able to paste this information into another program later?” persis adalah konfirmasi apakah menjalankan perwujudan ini (flush). Jika Anda memakai delayed rendering di aplikasi Anda sendiri, ingat bahwa perwujudan saat keluar adalah bagian dari set yang sama. Delayed rendering adalah optimasi performa, dan karena permintaan render berjalan secara sinkron di dalam pemrosesan pesan, data yang memakan waktu lama untuk dihasilkan punya trade-off membekukan UI.2
6. Praktik mengawasi clipboard — listener, coba ulang, dan pengecualian riwayat
6.1. Pakai AddClipboardFormatListener
Persyaratan seperti “kita ingin mendeteksi nilai pembaca barcode atau salinan dari sistem bisnis dan mengimpornya secara otomatis” membutuhkan Anda mengawasi perubahan clipboard. Secara historis ada tiga metode; hari ini jawaban yang benar adalah satu.8
| Metode | Penilaian |
|---|---|
| Baca pada timer (polling) | Boros, dan Anda dapat melewatkan pembaruan. Jangan pakai |
| SetClipboardViewer (rantai viewer) | Bug di satu aplikasi dalam rantai merusak seluruh rantai. Dipertahankan hanya untuk kompatibilitas mundur |
| AddClipboardFormatListener | Direkomendasikan. WM_CLIPBOARDUPDATE datang ke jendela yang didaftarkan |
flowchart TB
accTitle: Alur mengawasi clipboard
accDescr: Daftar dengan AddClipboardFormatListener ketika handle dibuat, dan WM_CLIPBOARDUPDATE datang tidak peduli aplikasi mana yang menyalin. Baca dengan coba ulang, dan batalkan pendaftaran secara simetris dengan RemoveClipboardFormatListener ketika handle dihancurkan
created["AddClipboardFormatListener"] --> wait["Tunggu"]
anyapp["Beberapa aplikasi menyalin"] --> notify["WM_CLIPBOARDUPDATE"]
wait --> notify
notify --> readtry["Baca dengan coba ulang (6.2)"]
readtry --> wait
destroyed["RemoveClipboardFormatListener"] -.->|"batalkan pendaftaran"| created
// 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. Coba ulang ketika Anda tidak dapat membukanya
Hanya satu jendela pada suatu waktu yang dapat membuka clipboard; sementara proses lain membukanya, OpenClipboard gagal.2 Tepat setelah WM_CLIPBOARDUPDATE, sumber salin atau pengawas lain sering masih beroperasi, jadi kegagalan baca sementara adalah event normal. Selalu pasang beberapa coba ulang dengan tunggu singkat (puluhan milidetik) di antaranya. Perhatikan bahwa overload Clipboard .NET yang membiarkan Anda menentukan jumlah coba ulang dan interval ada hanya di sisi tulis, SetDataObject. Tidak ada yang setara di sisi baca (GetDataObject dan kawan-kawannya), jadi Anda menulis catch-tunggu-coba-ulang sendiri — ExternalException di WinForms, COMException di WPF.
flowchart TB
accTitle: Alur coba ulang baca clipboard
accDescr: Hanya satu jendela pada suatu waktu yang dapat membuka clipboard, jadi baca tepat setelah notifikasi perubahan dapat gagal karena balapan dengan proses lain. Pada pengecualian, tunggu puluhan milidetik dan coba ulang; jika Anda menabrak batas, menyerah kali ini dan ambil pada pembaruan berikutnya
upd["WM_CLIPBOARDUPDATE"] --> tryread["Coba baca"]
tryread -->|"berhasil"| useok["Impor (pemeriksaan Bab 4)"]
tryread -->|"sedang dipakai"| waitretry["Tunggu puluhan md"]
waitretry -->|"coba ulang"| tryread
waitretry -->|"batas"| giveup2["Menyerah kali ini"]
6.3. Jaga keluar dari riwayat dan sinkronisasi — peduli pada fitur salin yang menangani rahasia
Windows punya riwayat clipboard (Win+V) dan sinkronisasi lintas perangkat (clipboard awan), dan data yang aplikasi taruh termasuk dalam cakupan keduanya secara bawaan. Aplikasi yang menaruh rahasia seperti kata sandi atau nomor rekening pada fitur salin juga menaruh format terdaftar yang mengecualikan konten dari riwayat dan sinkronisasi.1
- ExcludeClipboardContentFromMonitorProcessing: Taruh ini dan isi salinan itu tidak termasuk baik di riwayat maupun sinkronisasi.
- CanIncludeInClipboardHistory (DWORD 0): Tekan hanya riwayat.
- CanUploadToCloudClipboard (DWORD 0): Tekan hanya sinkronisasi lintas perangkat.
Alasan kata sandi yang disalin pengelola kata sandi tidak tertinggal di Win+V adalah mekanisme ini. Anda mendapat ID format dengan melewatkan nama ke RegisterClipboardFormat dan menetapkannya di samping data biasa, jadi layak diimplementasikan di setiap aplikasi bisnis yang menangani rahasia.
7. Clipboard dari sudut pandang IT — riwayat, sinkronisasi awan, dan kontrol RDP
Melangkah sedikit menjauh dari pengembangan, berikut poin yang penting bagi administrator. Riwayat clipboard menumpuk salinan terbaru, dan clipboard awan menyinkronkan salinan lintas perangkat yang masuk dengan akun Microsoft / akun Microsoft Entra yang sama.10 Nyaman seperti itu, ia juga menghasilkan residu dan tumpahan: informasi pribadi yang disalin dari sistem bisnis menumpuk di riwayat, dan konten yang disalin di PC kerja tersinkron ke PC pribadi.
Dua kebijakan yang Anda pakai untuk mengendalikan ini di organisasi adalah berikut.
| Yang Anda kendalikan | GPO (Computer Configuration > Administrative Templates > System > OS Policies) | Policy CSP (Intune) | Bawaan |
|---|---|---|---|
| Riwayat clipboard | Allow Clipboard History | Experience/AllowClipboardHistory | Diizinkan |
| Sinkronisasi lintas perangkat | Allow Clipboard synchronization across devices | Privacy/AllowCrossDeviceClipboard | Diizinkan |
Keduanya tersedia dari Windows 10 versi 1809 ke atas; nonaktifkan dan item yang sesuai di aplikasi Settings menjadi abu-abu, dan kebijakan berlaku segera.910
Makanan pokok lain adalah pengalihan clipboard RDP (Remote Desktop). Secara bawaan, salin-dan-tempel bekerja antara PC lokal dan sesi jarak jauh, jadi ia dapat menjadi jalur untuk mengambil rahasia dari server. Kebijakan “Do not allow clipboard redirection” (nilai registri fDisableClip) dapat memblokir kedua arah.11 Rilis Windows Server / Windows 11 terbaru juga telah menambahkan kebijakan yang lebih halus, seperti membatasi arah server-ke-klien hanya ke teks. Apakah Anda melarangnya mentah-mentah atau membatasinya secara bertahap adalah keseimbangan operasi dan keamanan.
flowchart TB
accTitle: Jalur isi clipboard dapat menyebar, dan titik kontrol
accDescr: Isi yang disalin termasuk dalam cakupan riwayat dan sinkronisasi awan secara bawaan, dan di RDP mereka berjalan ke sesi lain lewat pengalihan. Setiap jalur dapat dikendalikan oleh kebijakan, dan sisi aplikasi dapat mengecualikan dirinya dari riwayat dan sinkronisasi dengan format pengecualian
cb["Clipboard"] --> hist["Riwayat (Win+V)"]
cb --> cloud["Sinkronisasi awan"]
cb --> rdp["Pengalihan RDP"]
hist -.-> p1["AllowClipboardHistory"]
cloud -.-> p2["AllowCrossDeviceClipboard"]
rdp -.-> p3["fDisableClip"]
cb -.-> p4["Format pengecualian aplikasi (6.3)"]
8. Seret dan lepas adalah COM — IDataObject + IDropSource + IDropTarget
8.1. Data yang sama dengan clipboard, cara membawa yang berbeda
Seret-dan-lepas OLE berjalan dengan tiga peran berikut.12
| Peran | Siapa yang mengimplementasikan | Pekerjaan |
|---|---|---|
| IDataObject | Sumber seret | Payload yang dibawa. Objek data multi-format yang sama dengan clipboard |
| IDropSource | Sumber seret | Memutuskan apakah seret berlanjut atau dibatalkan, dan umpan balik kursor |
| IDropTarget | Target lepas | Menyatakan terima/tolak di DragEnter/DragOver/DragLeave/Drop, dan menerima lepas |
Sumber seret memanggil DoDragDrop, loop seret dimulai, dan ketika mouse masuk jendela target-lepas IDropTarget itu diberi tahu; pada lepas, IDataObject diserahkan. Dokumentasi resmi juga mengatakan bahwa “D&D menyediakan persis fungsionalitas yang sama dengan salin-dan-tempel clipboard. Jika aplikasi sudah mengimplementasikan salin-dan-tempel, tambahannya kecil”.12 Dengan kata lain, DataObject multi-format yang Anda bangun di Bab 2 sampai 5 menjadi payload D&D apa adanya.
flowchart TB
accTitle: Alur seret dan lepas OLE
accDescr: Sumber seret menaruh IDataObject di payload dan memanggil DoDragDrop untuk memulai loop seret; IDropTarget target lepas menyatakan terima/tolak di DragEnter dan DragOver, dan pada Drop memilih format dari IDataObject dan mengekstraknya
src["IDataObject + IDropSource"] -->|"DoDragDrop"| loop["Loop seret"]
loop -->|"mouse masuk"| enter["DragEnter/Over: Effect"]
enter -->|"tombol naik"| drop["IDropTarget.Drop"]
drop --> data["Pilih format dan ekstrak"]
8.2. OleInitialize (STA) diperlukan
Jendela yang akan menjadi target lepas mendaftar dengan RegisterDragDrop, dan ada jebakan klasik di sini. Jika Anda menginisialisasi COM dengan CoInitialize/CoInitializeEx, RegisterDragDrop selalu gagal dengan E_OUTOFMEMORY; Anda harus menginisialisasi dengan OleInitialize.13 OleInitialize menginisialisasi COM sebagai STA, karena D&D adalah fitur yang berakar di dunia STA jendela dan message pump. Thread pemanggil juga harus menjalankan message pump; lewati itu dan aplikasi lain hang selama seret.13 Latar belakang di sini persis adalah pembahasan model threading di “Dasar COM STA/MTA”.
Di aplikasi WinForms/WPF kerangka merawat inisialisasi OLE dan implementasi antarmuka, jadi pengembang hanya perlu menulis event.
// 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)
}
};
Bentuknya sama di WPF: Anda menerima dengan AllowDrop="True" dan event DragOver/Drop pada elemen, dan Anda mengekstrak larik path dengan e.Data.GetData(DataFormats.FileDrop). Menyatakan terima/tolak (Effect) pada setiap DragEnter/DragOver adalah konvensi IDropTarget; lewati itu dan Anda mendapat bug di mana kursor tetap pada “tidak diizinkan” dan tidak pernah berubah.
9. Jebakan D&D — elevasi, Move, dan validasi path
9.1. Anda tidak dapat melepas ke aplikasi yang di-elevate sebagai administrator
Lepas berkas dari File Explorer ke aplikasi yang diluncurkan dengan “Run as administrator” dan tidak terjadi apa-apa — ini bukan bug implementasi, ini perilaku OS. UIPI (User Interface Privilege Isolation) memblokir pesan dari proses integritas lebih rendah ke jendela integritas lebih tinggi secara bawaan, jadi notifikasi lepas dari File Explorer berhak biasa (integritas sedang) tidak pernah sampai ke aplikasi yang di-elevate.14
flowchart TB
accTitle: Bagaimana UIPI memblokir lepas ke aplikasi yang di-elevate
accDescr: Notifikasi lepas dari File Explorer integritas sedang ke aplikasi yang di-elevate integritas tinggi diblokir oleh UIPI secara bawaan dan tidak pernah datang. Pertahankan UI pada hak biasa dan isolasi pekerjaan berhak istimewa, dan lepas datang
explorer["Explorer (sedang)"] -->|"notifikasi lepas"| uipi{"UIPI"}
uipi -->|"diblokir"| elevated["Aplikasi di-elevate: tidak ada lepas"]
uipi -->|"lolos"| normal["UI biasa: lepas datang"]
normal -.->|"delegasikan pekerjaan berhak istimewa"| broker["Proses di-elevate yang terisolasi"]
Solusi yang secara individual mengizinkan pesan tertentu seperti WM_DROPFILES dengan ChangeWindowMessageFilterEx terkenal,14 tetapi yang diloloskannya adalah notifikasi lepas yang lebih lama (WM_DROPFILES); ia tidak menyelesaikan D&D OLE secara keseluruhan. Panduan praktisnya jelas: berhenti merancang aplikasi agar berjalan elevated sepanjang waktu. Isolasi hanya pekerjaan yang membutuhkan elevasi ke proses terpisah, dan UI itu sendiri dapat tetap pada hak biasa dan menerima D&D (desain isolasi dibahas secara rinci di “Cara mengisolasi secara konkret “hanya operasi yang membutuhkan hak administrator” di aplikasi Windows”).
9.2. Apa arti DragDropEffects — Move adalah kontrak bahwa “aslinya hilang”
Copy/Move/Link pada DragDropEffects bukan dekorasi; mereka adalah kontrak antara sumber seret dan target lepas. Sumber seret menyatakan himpunan efek yang diizinkannya di DoDragDrop, target lepas memilih efek aktual, dan ketika Move berhasil, sumber seret menghapus data (berkas) — itu konvensinya. Jika sisi penerima tanpa pikir mengembalikan Move, Anda mendapat kecelakaan “saya melepasnya dan berkas asli hilang”. Untuk pemakaian impor aplikasi bisnis, sisi penerima menyatakan Copy adalah bawaan yang aman.
flowchart TB
accTitle: Kontrak DragDropEffects — Move menghapus yang asli
accDescr: Sumber seret menyatakan himpunan efek yang diizinkan di DoDragDrop, dan target lepas memilih efek aktual. Ketika Move berhasil sumber seret menghapus berkas, jadi untuk impor sisi penerima harus menyatakan Copy
srcdecl["Sumber: efek yang diizinkan"] --> tgtsel["Target: pilih Effect"]
tgtsel -->|"Copy"| copyok["Asli tetap (impor)"]
tgtsel -->|"Move"| moveact["Sumber menghapus berkas"]
9.3. Memvalidasi path yang dilepas
Yang berjalan di CF_HDROP/FileDrop hanya path (Bagian 3.2). Sebelum Anda mengimpor, lewatkan melalui validasi input tidak tepercaya yang sama seperti tempel.
- Berkas atau folder: Putuskan sebagai spesifikasi apa yang terjadi ketika seluruh folder dilepas (rekursi dan impor, atau tolak).
- Placeholder OneDrive: Path mungkin ada sementara body berkas tidak lokal — berkas on-demand. Saat Anda membukanya unduhan dimulai, dan offline ia gagal. Perilaku dan penanggulangan ada di “OneDrive “Files On-Demand” dan aplikasi bisnis”.
- Path panjang dan path tidak biasa: Path di atas MAX_PATH, path jaringan (UNC), dan path di media lepas-pasang sebaiknya diterima hanya setelah Anda mengonfirmasi bahwa pemrosesan hilir dapat menanganinya.
- Jumlah dan ukuran total: Agar melepas ribuan berkas tidak membekukan UI, buat impor asinkron dan pasang batas serta tampilan kemajuan.
10. Ringkasan
- Clipboard adalah mekanisme yang menaruh konten yang sama dalam beberapa format sekaligus di satu area yang dibagi dalam desktop yang sama (window station). Sisi tempel memilih format, jadi salinan yang sama menghasilkan hasil berbeda.
- Teks adalah CF_UNICODETEXT, berkas adalah CF_HDROP, dan teks terformat adalah format terdaftar HTML Format (header offset byte + UTF-8).
- Sisi tempel melihat dari kaya ke biasa dan memperlakukan payload sebagai input eksternal. Sisi salin menawarkan beberapa format sekaligus, dan jika memakai delayed rendering ia mengimplementasikan perwujudan saat keluar (WM_RENDERALLFORMATS / OleFlushClipboard) juga.
- Pengawasan adalah AddClipboardFormatListener + WM_CLIPBOARDUPDATE. Bersiaplah untuk balapan OpenClipboard dengan coba ulang, dan jaga rahasia keluar dari riwayat dan sinkronisasi dengan ExcludeClipboardContentFromMonitorProcessing dan kawan-kawannya.
- IT dapat mengendalikan riwayat clipboard, sinkronisasi awan, dan pengalihan RDP dengan GPO / Intune. Bawaan adalah diizinkan untuk semuanya, jadi putuskan dengan sengaja di lingkungan yang menangani rahasia.
- D&D adalah COM: IDropSource/IDropTarget menyerahkan IDataObject yang sama dengan clipboard. RegisterDragDrop mensyaratkan OleInitialize (STA).
- Lepas ke aplikasi yang di-elevate diblokir oleh UIPI. Move pada DragDropEffects adalah kontrak bahwa “aslinya hilang”; validasi path yang dilepas sebelum Anda mengimpornya.
Salin-dan-tempel dan D&D, bagi pengguna, adalah fitur yang harus terasa seperti udara. Justru karena itu “saya tidak dapat menempel”, “berantakan”, dan “hilang” sangat menyakiti pengalaman — dan mengapa aplikasi yang menawarkan beberapa format dan menangani lepas dengan benar membuat operasi sehari-hari lebih lancar dengan sendirinya. Saya harap ini bahan yang berguna ketika Anda memutuskan apa yang diperbaiki lebih dulu.
Artikel terkait
- Apa itu COM / ActiveX / OCX? - Perbedaan dan hubungan dijelaskan
- Dasar COM STA/MTA - model threading dan cara menghindari hang
- Integrasi Windows Shell hari ini — menu konteks, asosiasi berkas, dan apa yang berubah di Windows 11
- Mengapa proses EXCEL.EXE tertinggal setelah otomasi COM Excel C# — pola pelepasan referensi dan keputusan penggantian
- Desain UX aplikasi Windows - prioritas menurut lingkungan pemakaian
- OneDrive “Files On-Demand” dan aplikasi bisnis — asumsi yang placeholder pecahkan dan cara mengatasinya
Area konsultasi terkait
KomuraSoft LLC menangani desain dan implementasi dukungan salin-dan-tempel serta seret-dan-lepas di aplikasi bisnis (menawarkan beberapa format, interop Excel, mengimpor berkas yang dilepas), investigasi akar masalah seperti “berantakan ketika saya menempel” atau “salinan hilang”, otomasi input yang mengawasi clipboard, dan implementasi yang menjaga data rahasia keluar dari riwayat dan sinkronisasi. Kasus yang melibatkan lapisan bawah COM dan OLE dipersilakan bahkan jika Anda mulai dari mengisolasi gejala.
- Pengembangan aplikasi Windows
- Pengembangan komponen COM
- Konsultasi teknis dan tinjauan desain
- Hubungi kami
Tautan referensi
-
Microsoft Learn, Clipboard Formats. Tentang jendela yang dapat menaruh informasi yang sama dalam beberapa format clipboard; format terdaftar lewat RegisterClipboardFormat (mendaftar nama yang sama mengembalikan nilai yang sama, jadi aplikasi dapat membaginya); format yang disintesis; dan mengecualikan konten dari riwayat clipboard / sinkronisasi awan dengan ExcludeClipboardContentFromMonitorProcessing, CanIncludeInClipboardHistory, dan CanUploadToCloudClipboard. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Clipboard Operations. Tentang hanya satu jendela pada suatu waktu yang dapat membuka clipboard; menaruh format dari lebih ekspresif ke kurang ekspresif pada waktu salin; pemilihan format pada waktu tempel dengan EnumClipboardFormats / GetPriorityClipboardFormat; delayed rendering dengan melewatkan NULL ke SetClipboardData dan tanggung jawab WM_RENDERFORMAT / WM_RENDERALLFORMATS; dan trade-off delayed rendering. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, Standard Clipboard Formats. Tentang definisi format standar CF_TEXT (ANSI), CF_UNICODETEXT, CF_HDROP, CF_DIB, dan CF_LOCALE, dan tentang sistem yang secara implisit mengonversi CF_TEXT dan CF_UNICODETEXT memakai code page yang terkait dengan CF_LOCALE. ↩ ↩2 ↩3
-
Microsoft Learn, Shell Clipboard Formats. Tentang CF_HDROP tersusun dari struktur DROPFILES plus larik string path lengkap yang diakhiri double-NUL; mengambil path individual dengan DragQueryFile; dan format shell CFSTR_ mensyaratkan pendaftaran lewat RegisterClipboardFormat. ↩ ↩2
-
Microsoft Learn, HTML Clipboard Format. Tentang nama terdaftar adalah “HTML Format”; struktur header dengan offset byte seperti Version, StartHTML, EndHTML, StartFragment, dan EndFragment; encoding selalu UTF-8; dan konvensi komentar StartFragment/EndFragment. ↩ ↩2 ↩3
-
Microsoft Learn, OleFlushClipboard function (ole2.h). Tentang OleSetClipboard membuat clipboard hanya memegang pointer ke objek data; OleFlushClipboard mewujudkan data ke clipboard sehingga tempel tetap bekerja setelah aplikasi keluar; dan mengosongkan clipboard dengan OleSetClipboard(NULL) ketika Anda tidak perlu mempertahankannya saat keluar. ↩ ↩2
-
Microsoft Learn, OleGetClipboard function (ole2.h). Tentang cara memperoleh IDataObject dari clipboard, dan peringatan bahwa data clipboard tidak tepercaya dan harus di-parse dengan hati-hati sebelum aplikasi memakainya. ↩ ↩2
-
Microsoft Learn, Using the clipboard. Tentang membandingkan tiga cara mengawasi clipboard (jendela viewer, nomor urut, dan listener format); program baru diharapkan memakai listener lewat AddClipboardFormatListener; rantai viewer rapuh ketika pemeliharaan rantai tidak lengkap; dan nomor urut bukan sesuatu yang harus Anda poll. ↩ ↩2
-
Microsoft Learn, Policy CSP - Experience. Tentang mengizinkan atau menolak riwayat clipboard dengan kebijakan Experience/AllowClipboardHistory; ketersediaan dari Windows 10 versi 1809 ke atas; bawaan diizinkan; dan pemetaan GPO di bawah “System > OS Policies” dengan perubahan berlaku segera. ↩ ↩2
-
Microsoft Learn, Policy CSP - Privacy. Tentang mengizinkan atau menolak sinkronisasi clipboard lintas perangkat dengan kebijakan Privacy/AllowCrossDeviceClipboard; sinkronisasi terjadi antara perangkat yang masuk dengan akun Microsoft / akun Microsoft Entra yang sama; dan bawaan diizinkan. ↩ ↩2 ↩3
-
Microsoft Learn, Policy CSP - ADMX_TerminalServer. Tentang TS_CLIENT_CLIPBOARD (“Do not allow clipboard redirection”, nilai registri fDisableClip) yang dapat melarang berbagi clipboard antara lokal dan jarak jauh dalam sesi Remote Desktop, dan tentang pengalihan diizinkan secara bawaan. ↩ ↩2
-
Microsoft Learn, Drag and Drop (COM). Tentang seret-dan-lepas OLE berjalan dengan tiga IDropSource (sumber seret), IDropTarget (target lepas), dan DoDragDrop (loop yang OLE sediakan); menyediakan fungsionalitas yang sama dengan salin-dan-tempel clipboard, sehingga aplikasi yang sudah mengimplementasikan salin-dan-tempel hanya membutuhkan tambahan kecil; dan jenis umpan balik. ↩ ↩2 ↩3
-
Microsoft Learn, RegisterDragDrop function (ole2.h). Tentang mendaftarkan jendela target-lepas dengan IDropTarget; selalu gagal dengan E_OUTOFMEMORY jika COM diinisialisasi dengan CoInitialize/CoInitializeEx, sehingga OleInitialize diperlukan; dan aplikasi sumber-seret hang jika thread pemanggil tidak menjalankan message pump. ↩ ↩2 ↩3
-
Microsoft Learn, ChangeWindowMessageFilterEx function (winuser.h). Tentang UIPI sebagai mekanisme keamanan yang secara bawaan memblokir menerima pesan dari pengirim integritas lebih rendah, dan tentang mengizinkan pesan tertentu per jendela dengan filter pesan (MSGFLT_ALLOW). ↩ ↩2 ↩3
-
Microsoft Learn, How to add data to the Clipboard (Windows Forms). Tentang menaruh data dalam beberapa format sekaligus dengan DataObject dan Clipboard.SetDataObject; menambah dalam beberapa format agar aplikasi lain dapat mengenalinya; dan kelas Clipboard hanya dapat dipakai dari thread STA, sehingga [STAThread] diperlukan. ↩ ↩2
Artikel terkait
Artikel terbaru dengan tag yang sama untuk mendalami topik-topik terdekat.
Apa sebenarnya "Tidak Merespons" — cara Windows memutuskan aplikasi hang, dan cara merancang aplikasi yang tidak hang
"Tidak Merespons" Windows adalah mekanisme di mana OS menilai bahwa jendela belum mengambil pesan selama 5 detik dan menggantinya dengan ...
Win32 Thread Pool API — konkurensi tanpa membuat thread, lewat CreateThreadpoolWork
Apakah Anda menebar panggilan CreateThread di seluruh kode native? Artikel ini menjelaskan Win32 thread pool API yang didesain ulang di V...
Named pipes dalam praktik — IPC standar Windows dari desain hingga keamanan
Panduan praktis tentang named pipe, komunikasi antarpproses standar di Windows. Artikel ini menata, dari sumber primer, pilihan antara mo...
Aplikasi yang rusak saat bangun dari tidur — cara kerja event daya Windows dan cara membangun aplikasi bisnis yang bertahan
Anda membuka laptop dan koneksi aplikasi bisnis sudah mati — penyebabnya adalah desain yang tidak pernah memperhitungkan tidur. Artikel i...
DllMain dan loader lock — alasan sebenarnya Anda diminta "jangan lakukan apa pun di inisialisasi DLL"
Mengapa Anda tidak boleh memanggil LoadLibrary atau menyinkronkan dengan thread lain dari DllMain. Berdasarkan sumber primer, artikel ini...
Topik terkait
Halaman-halaman ini menempatkan topik dalam konteks layanan dan keputusan yang lebih luas.
Topik teknis Windows
Portal tentang pengembangan Windows, investigasi bug, dan pemanfaatan aset yang ada.
Migrasi ActiveX
Keputusan untuk mempertahankan, membungkus, atau mengganti komponen COM / ActiveX / OCX.
Thread UI dan timer
Thread UI WPF / WinForms, alur asinkron, Dispatcher, dan desain timer.
Layanan yang terkait dengan topik ini
Artikel ini berkaitan langsung dengan layanan berikut.
Pengembangan aplikasi Windows
Aplikasi bisnis, integrasi perangkat, dan alat komunikasi, dari kebutuhan hingga pengembangan.
Pemanfaatan ulang dan migrasi aset yang ada
Pemanfaatan ulang dan migrasi aset COM / ActiveX / OCX serta dependensi 32/64-bit.
Pertanyaan yang sering diajukan
Pertanyaan yang sering muncul dalam konsultasi tentang topik artikel ini.
- Mengapa pemformatan tabel yang disalin dari Excel berantakan ketika saya menempelnya ke aplikasi saya?
- Clipboard tidak menahan "satu potong data". Konten yang sama ditaruh dalam beberapa format sekaligus (format privat aplikasi sumber, HTML Format, CSV, teks Unicode, dan sebagainya), dan aplikasi tujuan memilih format yang dipahaminya dan mengekstraknya. Ketika pemformatan berantakan, penyebab khasnya adalah tujuan hanya membaca teks biasa (CF_UNICODETEXT). Jika Anda ingin struktur tabel juga, implementasikan sisi tempel agar lebih memilih HTML Format atau CSV. Sebaliknya, jika Anda ingin aplikasi lain menempel dengan benar dari salinan yang dibuat di aplikasi Anda sendiri, tawarkan baik format kaya maupun format biasa pada waktu salin.
- Mengapa saya tidak dapat menempel lagi setelah menutup aplikasi yang saya salin darinya?
- Karena sumber memakai delayed rendering. Aplikasi yang menangani data besar tidak menaruh payload pada waktu salin; mereka mendaftarkan hanya janji di clipboard bahwa mereka akan "menghasilkannya ketika diminta". Jika sumber kemudian keluar tanpa mewujudkan data sebagai respons terhadap WM_RENDERALLFORMATS saat shutdown, setiap format yang belum dirender hilang. Aplikasi yang memakai clipboard OLE (IDataObject) dapat menjaga tempel tetap bekerja setelah keluar dengan memanggil OleFlushClipboard saat shutdown untuk mewujudkan data.
- Bagaimana aplikasi saya sendiri dapat mengawasi perubahan clipboard?
- Metode yang saat ini direkomendasikan adalah mendaftarkan jendela Anda sebagai listener dengan AddClipboardFormatListener dan menangani pesan WM_CLIPBOARDUPDATE yang datang setiap kali isi berubah. Polling isi pada timer membuang pekerjaan dan dapat melewatkan pembaruan, dan rantai viewer lama berbasis SetClipboardViewer dipertahankan hanya untuk kompatibilitas mundur, karena bug di satu aplikasi dalam rantai merusak seluruh rantai. Perhatikan juga bahwa OpenClipboard pada baca dapat gagal karena proses lain memegang clipboard, jadi implementasikan coba ulang dengan tunggu singkat jika Anda ingin baca tetap stabil.
- Apakah ada cara menjaga rahasia seperti kata sandi keluar dari riwayat clipboard (Win+V)?
- Ada dua tuas, satu di sisi aplikasi dan satu di sisi kebijakan. Di sisi aplikasi, jika Anda juga menaruh format terdaftar ExcludeClipboardContentFromMonitorProcessing ketika Anda menyalin, konten itu tidak termasuk baik di riwayat maupun sinkronisasi lintas perangkat. Anda juga dapat mengendalikan masing-masing secara independen dengan CanIncludeInClipboardHistory (hanya riwayat) dan CanUploadToCloudClipboard (hanya sinkronisasi). Ini adalah mekanisme yang dipakai pengelola kata sandi. Jika Anda ingin mematikannya untuk seluruh organisasi, Anda dapat menonaktifkan riwayat dan sinkronisasi awan itu sendiri dengan AllowClipboardHistory dan AllowCrossDeviceClipboard lewat Group Policy atau Intune (Policy CSP).
- Mengapa saya tidak dapat menyeret dan melepas berkas ke aplikasi yang berjalan sebagai administrator?
- Karena mekanisme keamanan bernama UIPI (User Interface Privilege Isolation) memblokir pengiriman pesan dari proses integritas lebih rendah ke jendela integritas lebih tinggi. File Explorer berjalan pada hak biasa (integritas sedang), jadi notifikasi seret-dan-lepas tidak pernah sampai ke jendela aplikasi yang di-elevate. Solusi yang secara individual mengizinkan pesan seperti WM_DROPFILES dengan ChangeWindowMessageFilterEx terkenal, tetapi itu hanya berlaku untuk notifikasi drop yang lebih lama. Perbaikan sungguhan adalah berhenti merancang aplikasi agar berjalan elevated sepanjang waktu, dan mengisolasi hanya pekerjaan yang membutuhkan elevasi ke proses terpisah.
Profil penulis
Halaman perkenalan penulis artikel.
Go Komura
Direktur KomuraSoft LLC
Berspesialisasi dalam pengembangan perangkat lunak Windows, konsultasi teknis, dan investigasi bug, terutama pada proyek dengan sistem yang sudah ada dan bug yang sulit direproduksi.