Revision history (1 updates, last updated Sep 8, 2026)
A log of the changes made to this article. Where a pre-update version was archived, it stays readable at a permanent DOI link.
- Retranslated as a full translation of the current Japanese original. The previous English version was an abridgement that dropped subsections, tables, diagrams, and paragraphs; all of them have been restored to match the Japanese article, and the knowledge map section has been added where the Japanese article has one. The technical claims are the same as in the Japanese version. Read the version before this update (DOI: 10.5281/zenodo.22170891)
- First published
Cite this article(DOI: 10.5281/zenodo.22170890)
This article is archived on Zenodo. Below are both the DOI that always resolves to the latest version and the DOI pinned to the version you are reading.
Go Komura (2026). How the Clipboard and Drag & Drop Work — Handling OLE Data Transfer Correctly in Business Apps. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22170890 https://comcomponent.com/en/blog/windows-clipboard-drag-drop-ole-data-transfer/
- DOI (latest version)
- 10.5281/zenodo.22170890
- DOI (this version)
- 10.5281/zenodo.22652531
“The formatting falls apart when I paste a table from Excel.” “Content copied from our app does not come out the way we intended when pasted into Word.” “We want to take files in by drag and drop.” — Requests like these come up often when business apps are being revised.
All of these are everyday operations, but if you think of the clipboard as “a box that holds one piece of data,” you will misread how it works. In reality, it is a mechanism that places the same content in several formats at once and lets the paste side pick the format it understands. That is why the same copy produces different results depending on where it is pasted.
The problem of paste failing once the source is closed involves “delayed rendering,” which produces the data only when it is needed. And OLE drag & drop (D&D) hands over IDataObject, the same data representation the OLE clipboard uses, across COM interfaces. Copy and paste and D&D are related features: they share the data format and differ in how it is carried.
This article is written for IT staff at small and midsize companies and Windows app developers. It first covers how formats work, then looks at the paste-side, copy-side, and monitoring implementations, and then moves on to managing history, sync, and RDP and to the caveats of OLE D&D.
1. The Bottom Line First
There are three axes to keep in mind.
- The copy side offers several formats, and the paste side picks from among them. The basics are
CF_UNICODETEXTfor text,CF_HDROPfor a list of file paths, and the registered format “HTML Format” for formatted text.1234 - Design not only the data’s format but also its validation and its lifetime. Validate pasted data as untrusted external input. A copy side that uses delayed rendering also implements the materialization step at exit. Monitor with
AddClipboardFormatListenerandWM_CLIPBOARDUPDATE, and prepare for read contention with retries.5678 - Make clear how far the data travels and what happens once it is handed over. History, cloud sync, and RDP redirection are things to manage. OLE D&D requires STA initialization through
OleInitialize, and a drop from ordinary privilege onto an elevated app is blocked by UIPI. Also,Moveis a contract under which the original data disappears.910111213
If you want to read by goal, start from the chapter below.
| Problem or goal | What to check first | Chapter |
|---|---|---|
| A pasted Excel table falls apart | The formats the copy side offers and the format the paste side picks | Chapters 2–4 |
| Make a copy from our app usable in other apps | Offering several formats, and keeping the data after exit | Chapter 5 |
| Detect a copy and import it automatically | Listener registration and read retries | Chapter 6 |
| Keep secrets out of history, sync, and RDP | The app-side exclusion formats and organizational policy | Section 6.3, Chapter 7 |
| Implement D&D; it fails only when elevated | OLE initialization, the privilege boundary, effects and path validation | Chapters 8–9 |
In the diagram a solid line marks a relation that always holds and a dashed line marks a conditional one (the conditions are given per relation on the detail page). The full list of relations (20 in total, with evidence and certainty) and the definitions of the main concepts are collected on the knowledge map detail page (in Japanese). Data: JSON-LD / Turtle
2. What the Clipboard Really Is — Not “One Piece of Data” but “the Same Content in Several Formats”
The clipboard is a mechanism for sharing data between apps. Apps on the same desktop can use it, but the precise unit of sharing is the window station. A different user session or RDP session has its own separate clipboard. Copy and paste works over RDP because the redirection feature bridges the two (Chapter 7).
The overriding principle is that use is user-driven. The official design stance is that an app does not put data in or take data out without the user’s knowledge.1
After emptying the clipboard, the copy side places the same content in several formats, from the most expressive downward.6 Conceptually, copying a table in a spreadsheet application lines up like this.
| Priority | Format | Contents |
|---|---|---|
| 1 | App-private format | A complete internal representation including formulas and formatting (for pasting back into the same app) |
| 2 | HTML Format | An HTML fragment that keeps the table structure and formatting |
| 3 | CSV | Cell-delimited text |
| 4 | CF_UNICODETEXT | Tab-separated plain text |
| 5 | Image format | A bitmap of how the table looks |
What the paste side takes out is the format it understands from this list. Word gets a formatted table and Notepad gets tab-separated text because the two chose different formats.
In other words, “the result depends on the paste destination” is not in itself a bug. You need to look at the copy side’s offering and the paste side’s choice in combination.
flowchart TB
accTitle: How the same copy produces different results depending on where it is pasted
accDescr: The copy side places the same content on the clipboard in several formats and the paste side picks the format it understands, so Word gets a formatted table and Notepad gets tab-separated text
copy["Copy side: spreadsheet application"] --> cb["Clipboard (the same content in several formats)"]
cb --> f1["App-private format"]
cb --> f2["HTML Format"]
cb --> f3["CSV"]
cb --> f4["CF_UNICODETEXT"]
f2 -->|"Word picks this"| word["Formatted table"]
f4 -->|"Notepad picks this"| notepad["Tab-separated text"]
Put the other way around, the opening complaints — “the formatting falls apart,” “something odd gets pasted” — can almost all be reduced to how one of the two sides chooses or offers formats. Chapter 4 covers the paste side and Chapter 5 the copy side.
3. Standard Formats and Registered Formats — CF_UNICODETEXT, CF_HDROP, HTML Format
3.1. Standard Formats — Use the Unicode Side for Text
Formats the OS defines in advance are called standard formats. These are the ones that appear most often in business apps.2
| Format | Value | Contents |
|---|---|---|
| CF_TEXT | 1 | ANSI text (code-page dependent) |
| CF_UNICODETEXT | 13 | Unicode text. This is the canonical format for text |
| CF_HDROP | 15 | A list of file paths (an HDROP handle) |
| CF_DIB | 8 | A device-independent bitmap |
| CF_LOCALE | 16 | The locale identifier associated with the text |
CF_TEXT and CF_UNICODETEXT are “synthesized formats” that the system converts between implicitly. The conversion uses the code page associated with CF_LOCALE.2
However, characters that ANSI cannot represent are lost in the conversion. If you handle Unicode-only symbols, combining characters, and the like, leaving it to the conversion leads to mojibake. The rule is to standardize the app’s reads and writes on CF_UNICODETEXT, or DataFormats.UnicodeText in .NET.
flowchart LR
accTitle: Implicit conversion between CF_UNICODETEXT and CF_TEXT
accDescr: The app reads and writes only CF_UNICODETEXT; the system synthesizes CF_TEXT by implicit conversion using the CF_LOCALE code page. Characters that ANSI cannot represent are dropped in this conversion
apprw["App reads and writes"] --> uni["CF_UNICODETEXT (canonical)"]
uni <-->|"System converts implicitly (CF_LOCALE code page)"| ansi["CF_TEXT (ANSI, code-page dependent)"]
ansi -.-> loss["Unrepresentable characters are dropped in conversion (a breeding ground for mojibake)"]
3.2. CF_HDROP — Files Travel as “a List of Paths”
CF_HDROP is what File Explorer uses for file copies and D&D. The first thing to remember is that what travels is a list of full paths, not the files themselves.
A DROPFILES structure sits at the start of the memory block, followed by path strings separated by NUL characters. An empty string is placed last, so the block ends in a “double NUL.” In the header, pFiles is the start offset of the path list and fWide indicates whether the strings are Unicode.3
[DROPFILES header: pFiles=start offset of the path list, fWide=1 (Unicode)]
C:\data\a.txt(NUL)C:\data\b.txt(NUL)(NUL)
In native code you retrieve them one at a time with DragQueryFile; in .NET you receive them as a string[] through DataFormats.FileDrop. The point that “only paths travel, not the files themselves” matters again for D&D in Chapters 8 and 9.
flowchart TB
accTitle: Memory block structure of CF_HDROP
accDescr: A DROPFILES structure sits at the start of the global memory; pFiles gives the start offset of the path list and fWide indicates whether it is Unicode. NUL-separated full paths follow, ending with an empty string as a double-NUL terminator. Only paths travel, not the files themselves
hdr["DROPFILES structure (pFiles = list start offset / fWide = 1)"] --> p1["C:\\data\\a.txt + NUL"]
p1 --> p2["C:\\data\\b.txt + NUL"]
p2 --> tail["Terminating empty string (double NUL)"]
hdr -.-> note["Only paths travel, not the files themselves"]
3.3. Registered Formats — RegisterClipboardFormat and “HTML Format”
Data that standard formats cannot express can be shared as a registered format under a name you choose. Pass a name to RegisterClipboardFormat and you get a format ID back. Registering the same name from another app yields the same ID, so agreeing on a name between apps is the point of contact.1
When passing structured data among your own suite of apps, use a name that will not collide, such as KomuraSoft.Report.RowData.
HTML Format Is “UTF-8 with a Header”
The representative registered format is “HTML Format,” a formatted-text format that ranks alongside RTF. The body is UTF-8 text, but a header listing byte offsets is attached at the front.4
Version:0.9
StartHTML:<byte position where the whole HTML starts>
EndHTML:<byte position where the whole HTML ends>
StartFragment:<byte position where the fragment starts>
EndFragment:<byte position where the fragment ends>
<html><body>
<!--StartFragment--><b>Bold</b> fragment text<!--EndFragment-->
</body></html>
Each offset is measured from the start of the data, including the header itself. StartHTML / EndHTML point to the whole HTML, and StartFragment / EndFragment to the start and end of the fragment the user selected. The unit is bytes, not characters.
When generating it, assemble it in this order.
- Reserve the offset fields at a fixed width (for example, 10 digits).
- Build the HTML body and encode it as UTF-8.
- Measure the byte positions after encoding and write them back into the header.
In UTF-8 that contains Japanese, the character count and the byte count do not match. Get this wrong and the beginning or the end is cut off when pasting into another app.4
flowchart TB
accTitle: How the HTML Format header relates to the offsets
accDescr: In the header, StartHTML and EndHTML point to the whole HTML and StartFragment and EndFragment to the fragment the user selected, all as byte positions from the start of the data. Because character count and byte count diverge in UTF-8, fill them with byte positions measured after encoding
header["Header (Version / StartHTML / EndHTML / StartFragment / EndFragment)"] --> html["Whole HTML (StartHTML to EndHTML)"]
html --> frag["Selected fragment (StartFragment to EndFragment)"]
header -.-> byte["Each offset = byte position from the start of the data (measured after UTF-8 encoding and written back)"]
Besides these, CSV (DataFormats.CommaSeparatedValue in .NET) is also commonly used for tabular data. For interoperability with Excel, offering HTML Format (formatted), CSV (values only), and CF_UNICODETEXT (tab-separated) together works whatever the paste destination.
4. Practices on the Paste Side — Format Priority and Validation
4.1. Search from the Rich Formats Down
The paste side searches the formats it can handle, starting from the one with the most information. You can either use the order in which the copy side placed the formats, from most expressive down, or specify your own priority order.6
| Win32 API | How it chooses |
|---|---|
EnumClipboardFormats |
Enumerates in the order the copy side placed them; use the first format you recognize |
GetPriorityClipboardFormat |
Picks an available format from the priority list the paste side passes in |
In .NET the branching looks like this.
// Pasting a table: search 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 checks out too; 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
}
This is the answer to the opening complaint that “a pasted Excel table falls apart.” Table structure never reaches an app that reads only plain text. How far down the list of formats you accept is a design decision for the paste side.
flowchart TB
accTitle: Paste branching that searches from rich formats down
accDescr: If HTML Format is present and the payload is a string, import as a table; otherwise CSV; failing that, tab-separated text, falling from the most expressive format downward. If no candidate is present, refuse
startsel["Start paste"] --> h{"HTML Format present and payload is a string?"}
h -->|"Yes"| useh["Validate the header and import as a table"]
h -->|"No"| c{"CSV present?"}
c -->|"Yes"| usec["Import as CSV"]
c -->|"No"| t{"UnicodeText present?"}
t -->|"Yes"| uset["Import as tab-separated text"]
t -->|"No"| giveup["Refuse"]
4.2. Pasted Data Is External Input
It is easy to overlook, but the contents of the clipboard are data of external origin, and you do not know which app placed them. Microsoft itself warns in the OLE clipboard documentation that “clipboard data is not trusted; parse it carefully before using it in the app.”5
The presence of a format alone does not tell you the data can be imported. Check the type of the actual payload as well, and run the following checks.
| What to validate | What to check |
|---|---|
| The HTML Format header | Whether the offsets point outside the range. Some apps emit broken headers |
| Values such as numbers, dates, and codes | Whether they pass the same validation as on-screen input |
| The size of the data | Whether it exceeds the acceptance limit, as with images of hundreds of megabytes or text of millions of lines |
Note That Materialization Starts Before You Can Check the Size
For defenses against huge data, distinguish which stage’s load you can prevent. .NET’s GetData also triggers delayed rendering when it is called, and for text it materializes all the way to a managed string. Checking the size only after retrieval does not prevent the load of that materialization.
If you check the HGLOBAL returned by Win32 GetClipboardData with GlobalSize, you can defend against pushing huge data on into conversion to a managed string and parsing. For delayed-rendering formats, however, GetClipboardData itself triggers rendering. You cannot prevent the copy source from materializing the data itself.
To keep the UI from freezing, move the retrieval off the UI thread and refuse data that exceeds the limit. Even then, .NET’s Clipboard requires STA. Use a dedicated thread set to STA, not the thread pool (MTA) of Task.Run (Section 5.1).
The idea that “a value that comes from outside is validated before use, whatever its path” is the same one laid out in “Never Use a QR Code’s Decoded Value As-Is”. The assumption that paste is safe because it is a user action is how things go wrong.
flowchart LR
accTitle: Validating pasted data before using it
accDescr: Data taken from the clipboard passes through checks for format presence, payload type, size limit, and content in that order; if any check fails, refuse or fall back to the next candidate format
present["Check that the format is present (GetDataPresent)"] --> type["Check the payload type (is string / string[])"]
type --> size["Check the size limit"]
size --> content["Validate the content (header, paths, values)"]
content --> ok["Import"]
type -.->|"Wrong type"| rej["Refuse / next candidate format"]
size -.->|"Too large"| rej
content -.->|"Invalid"| rej
5. Practices on the Copy Side — Offering Several Formats at Once, and Delayed Rendering
5.1. Place Several Formats at Once
The copy side’s practice is the mirror image of 4.1: offer a rich format and a plain format at the same time. With the WinForms/WPF DataObject it takes a few lines.14
// WinForms (System.Windows.Forms). WPF has the same shape with the System.Windows DataObject/Clipboard
var data = new DataObject();
data.SetData(DataFormats.Html, htmlFormatText); // HTML Format string with 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
Here we look separately at the thread requirement and the lifetime after exit.
Thread requirement: .NET’s Clipboard class can be used only from an STA thread.14 The WinForms/WPF UI thread is STA because of [STAThread], so this is normally not a problem, but it cannot be used from a background thread that is not STA. The background is explained in “COM STA/MTA Fundamentals”.
Lifetime after exit: copy: true means “keep it after the app exits.” Understanding it together with delayed rendering, next, makes clear why this specification is necessary.
5.2. Delayed Rendering — Why “Close It and You Cannot Paste”
Generating every one of many formats on each copy means doing work even for formats that are never used. The mechanism that avoids this is delayed rendering.6
At copy time, pass NULL as the data handle to SetClipboardData, registering not the data itself but a promise to “produce it when asked.” When that format is requested, WM_RENDERFORMAT arrives at the copy source, and only then is the data generated.
At Exit, Turn the “Promise” into Data
If the copy source exits leaving formats unrendered, the paste side can no longer receive that data. This is the “close the source and you cannot paste” problem.
Before exiting, the copy source is responsible for responding to WM_RENDERALLFORMATS and materializing every unrendered format. Formats that were not materialized are lost when the copy source exits.6
flowchart TB
accTitle: The delayed rendering flow and "close it and you cannot paste"
accDescr: The copy source registers only a promise with a NULL handle and materializes the data with WM_RENDERFORMAT when a request arrives. At exit it is responsible for materializing every format with WM_RENDERALLFORMATS; neglect that and the format is lost
promise["Copy source: register only a promise with SetClipboardData(format, NULL)"] --> req["Paste side requests that format"]
req --> render["WM_RENDERFORMAT → generate the data on the spot"]
promise --> quit["Copy source is about to exit"]
quit -->|"Materialize with WM_RENDERALLFORMATS"| ok["Paste still works after exit"]
quit -->|"Neglect materialization"| lost["That format is lost (close it and you cannot paste)"]
On the OLE clipboard, you place an IDataObject with OleSetClipboard. At that point, what the clipboard holds is a pointer to the data object.
Calling OleFlushClipboard at exit materializes the data on the clipboard, so pasting still works after exit.7 .NET’s Clipboard.SetDataObject(data, copy: true) specifies this “keep after exit” behavior.
When you copy a large range in Excel and then exit, the prompt asking whether you want to keep the large amount of information on the clipboard is the confirmation of whether to perform this materialization (the flush). In your own app as well, design delayed rendering and the materialization at exit as a set.
Delaying Does Not Guarantee the UI Stays Responsive
Delayed rendering is a performance optimization, but generating the requested data runs synchronously inside message processing. The trade-off is that the UI freezes if generation takes a long time.6
6. Practices for Monitoring the Clipboard — Listener, Retry, and History Exclusion
6.1. Use AddClipboardFormatListener
Requirements such as “detect a barcode reader’s value or a copy from the core business system and import it automatically” call for monitoring clipboard changes. Historically there are three methods, but today there is one right answer.8
| Method | Assessment |
|---|---|
| Read periodically on a timer (polling) | Wasteful, and can miss changes. Do not use |
| SetClipboardViewer (viewer chain) | A defect in one app in the chain breaks the whole thing. Kept only for backward compatibility |
| AddClipboardFormatListener | Recommended. WM_CLIPBOARDUPDATE is delivered to the registered window |
flowchart LR
accTitle: Clipboard monitoring flow
accDescr: Register with AddClipboardFormatListener when the handle is created, and WM_CLIPBOARDUPDATE arrives whichever app copies. Read with retries, and unregister symmetrically with RemoveClipboardFormatListener when the handle is destroyed
created["OnHandleCreated: AddClipboardFormatListener"] --> wait["Wait"]
anyapp["Some app copies"] --> notify["WM_CLIPBOARDUPDATE arrives"]
wait --> notify
notify --> readtry["Read with retries (Section 6.2)"]
readtry --> wait
destroyed["OnHandleDestroyed: RemoveClipboardFormatListener"] -.->|"Unregister symmetrically"| 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, in step with handle destruction and 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. Retry When It Cannot Be Opened
Only one window at a time can open the clipboard. While another process has it open, OpenClipboard fails.6
Right after WM_CLIPBOARDUPDATE, too, the copy source or another monitoring app may still be operating on it. Treat a temporary read failure as a normal event, and add logic that waits a few tens of milliseconds and retries a few times.
A point to note in .NET is that the overloads that let you specify a retry count and interval exist only on the write side, SetDataObject. The read side, GetDataObject and the like, has none.
| Read side | Handling contention |
|---|---|
| WinForms | Catch ExternalException, wait, and retry |
| WPF | Catch COMException, wait, and retry |
The wait-and-retry for reads is implemented on the app side.
flowchart LR
accTitle: Clipboard read retry flow
accDescr: Only one window at a time can open the clipboard, so a read right after the change notification can fail through contention with another process. On an exception, wait a few tens of milliseconds and retry; once the limit is reached, give up this time and pick it up on the next update
upd["WM_CLIPBOARDUPDATE"] --> tryread["Attempt the read"]
tryread -->|"Success"| useok["Import (Chapter 4 validation)"]
tryread -->|"Failure (another window is using it)"| waitretry["Wait a few tens of milliseconds"]
waitretry -->|"Retry up to a few times"| tryread
waitretry -->|"Limit reached"| giveup2["Give up this time (pick it up on the next update)"]
6.3. Keep It Out of History and Sync — Considerations for Copy Features That Handle Secrets
Windows has clipboard history (Win+V) and cross-device sync (the cloud clipboard), and data an app places is subject to both by default. An app whose copy feature carries secrets such as passwords or account numbers also places a registered format that excludes the data from history and sync.1
| Registered format | What it suppresses |
|---|---|
ExcludeClipboardContentFromMonitorProcessing |
Excludes the entire copied content from both history and cross-device sync |
CanIncludeInClipboardHistory (DWORD 0) |
History only |
CanUploadToCloudClipboard (DWORD 0) |
Cross-device sync only |
Password managers keep copied passwords out of Win+V through this mechanism. All it takes is passing the name to RegisterClipboardFormat to get a format ID and setting it alongside the ordinary data, so it is worth implementing in business apps that handle secrets.
7. The Clipboard from an IT Perspective — Controlling History, Cloud Sync, and RDP
Controlling copied content on the app side and deciding what the organization as a whole permits are separate questions. What an administrator looks at are three things: history, cloud sync, and RDP redirection.
Clipboard history accumulates recently copied content. The cloud clipboard syncs it between devices signed in with the same Microsoft account or Microsoft Entra account.10
Convenient as this is, it leads to residue and spillover: personal information copied from the core business system stays in history, or content copied on a work PC syncs to a personal PC.
7.1. Controlling History and Cross-Device Sync
The two policies for organizational control are these.
| What is controlled | GPO (Computer Configuration > Administrative Templates > System > OS Policies) | Policy CSP (Intune) | Default |
|---|---|---|---|
| Clipboard history | Allow Clipboard History | Experience/AllowClipboardHistory | Allowed |
| Cross-device sync | Allow Clipboard synchronization across devices | Privacy/AllowCrossDeviceClipboard | Allowed |
Both are available from Windows 10 version 1809 onward; when disabled, the corresponding items in the Settings app are grayed out, and the policy takes effect immediately.910
7.2. Controlling Transfer Between RDP Sessions
RDP (Remote Desktop) clipboard redirection bridges the local PC and the remote session. Because copy and paste works by default, it can also become a path for taking secrets off a server.
The setting that blocks both directions is the “Do not allow Clipboard redirection” policy (registry value fDisableClip).11
Recent Windows Server and Windows 11 releases have also added policies for finer control, such as limiting only the server-to-client direction to text. Whether to ban it outright or restrict direction and format in stages is a balance between operations and security.
flowchart LR
accTitle: Paths along which clipboard content spreads, and the control points
accDescr: Copied content is subject to history and cloud sync by default, and over RDP it passes to another session through redirection. Each can be controlled by policy, and the app side can exclude content from history and sync with the exclusion formats
cb["Clipboard"] --> hist["History (Win+V)"]
cb --> cloud["Cloud sync → another device"]
cb --> rdp["RDP redirection → another session"]
hist -.-> p1["Control: AllowClipboardHistory"]
cloud -.-> p2["Control: AllowCrossDeviceClipboard"]
rdp -.-> p3["Control: fDisableClip"]
cb -.-> p4["App side: exclude with ExcludeClipboardContentFromMonitorProcessing and the like (Section 6.3)"]
8. Drag & Drop Is COM — IDataObject + IDropSource + IDropTarget
8.1. The Same Data as the Clipboard, Carried Differently
OLE drag & drop runs on the following three parties.15
| Role | Implemented by | Job |
|---|---|---|
| IDataObject | Drag source | The data being carried. The same multi-format data object as the clipboard |
| IDropSource | Drag source | Deciding whether the drag continues or is canceled, and cursor feedback |
| IDropTarget | Drop target | Declaring acceptance and receiving the data in DragEnter/DragOver/DragLeave/Drop |
The operation flows as follows.
- The drag source calls
DoDragDrop, starting the drag loop. - When the mouse enters a drop-target window, the
IDropTargetis notified. - The drop target declares whether it accepts, and on drop retrieves the format it needs from the
IDataObject.
The official documentation also explains that D&D provides the same functionality as clipboard copy and paste, and that an app that already implements copy and paste needs only a small addition.15 In other words, the multi-format DataObject prepared in Chapters 2 through 5 is also the data that D&D carries.
flowchart LR
accTitle: OLE drag & drop flow
accDescr: The drag source calls DoDragDrop with an IDataObject as the payload and the drag loop begins; the drop target's IDropTarget declares acceptance in DragEnter and DragOver, and on Drop picks and retrieves a format from the IDataObject
src["Drag source: IDataObject + IDropSource"] -->|"DoDragDrop"| loop["Drag loop"]
loop -->|"Mouse enters the window"| enter["IDropTarget.DragEnter/DragOver (declare the Effect every time)"]
enter -->|"Button released"| drop["IDropTarget.Drop"]
drop --> data["Pick and retrieve a format from the IDataObject"]
8.2. OleInitialize (STA) Is Required
A drop-target window is registered with RegisterDragDrop. As prerequisites, check two things: OLE initialization and message processing.
Use OleInitialize for initialization. If you use CoInitialize / CoInitializeEx instead, RegisterDragDrop fails with E_OUTOFMEMORY. OleInitialize initializes COM as an STA.12
The registering thread must run a message pump. OLE D&D is a feature rooted in windows and message processing; neglect this and other apps hang during a drag.12 The background is the same threading model as in “COM STA/MTA Fundamentals”.
In WinForms/WPF, Receive Through Events
In WinForms/WPF, the framework takes care of OLE initialization and the interface implementations. The developer writes the acceptance declaration and the actual import in events.
// WinForms: accept dropped files
listView1.AllowDrop = true;
listView1.DragEnter += (s, e) =>
{
// Also check that the source allows Copy (some sources allow only 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 external input too. Even if it advertises FileDrop, the payload
// can be null or a different type, and the retrieval 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)
}
};
The picture is the same in WPF. Receive with AllowDrop="True" on the element and the DragOver / Drop events, and retrieve the path array with e.Data.GetData(DataFormats.FileDrop).
Declaring acceptance (the Effect) every time in DragEnter / DragOver is the IDropTarget convention. Omit it and the cursor stays at “not allowed.” Check not only whether the format is present but also, as in the code sample, whether the drag source allows Copy.
9. D&D Pitfalls — Elevation, Move, and Path Validation
9.1. You Cannot Drop onto an App Elevated to Administrator
Drop a file from File Explorer onto an app launched with “Run as administrator” and nothing happens — this is not an implementation mistake but OS design. UIPI (User Interface Privilege Isolation) blocks messages from a lower-integrity process to a higher-integrity window by default, so drop notifications from File Explorer running at ordinary privilege (medium integrity) never reach an elevated app.13
flowchart LR
accTitle: How UIPI blocks a drop onto an elevated app
accDescr: UIPI blocks by default the drop notification from medium-integrity File Explorer to a high-integrity elevated app, so it never arrives. Keep the UI at ordinary privilege and isolate privileged work, and the drop arrives
explorer["File Explorer (medium integrity)"] -->|"Drop notification"| uipi{"UIPI"}
uipi -->|"Blocked (default)"| elevated["Elevated app (high integrity): no response"]
uipi -->|"Passes"| normal["UI at ordinary privilege: the drop arrives"]
normal -.->|"Delegate only privileged work"| broker["Separate process isolating the work that needs elevation"]
There is also a workaround that individually allows WM_DROPFILES and similar messages with ChangeWindowMessageFilterEx.13 However, this is a countermeasure for the old-style drop notification via WM_DROPFILES, and it does not solve OLE D&D as a whole.
The fundamental guidance is not to run the app elevated all the time. If you isolate only the work that needs elevation into a separate process, the UI itself can stay at ordinary privilege and accept D&D. The isolation design is described in detail in “Administrator Privileges and Broker Processes in Windows Apps”.
9.2. What DragDropEffects Means — Move Is a Contract That “the Original Disappears”
Copy / Move / Link in DragDropEffects are not cursor decoration; they are a contract between the drag source and the drop target.
The drag source declares the set of effects it allows in DoDragDrop, and the drop target picks the actual effect. By convention, when Move is agreed, the drag source deletes the data (the file).
If the receiving side returns Move without thinking, you end up with “I dropped it and the original file is gone.” For imports in business apps, make the receiving side explicitly stating Copy the safe default.
flowchart LR
accTitle: The DragDropEffects contract — with Move, the original disappears
accDescr: The drag source declares the set of effects it allows in DoDragDrop, and the drop target picks the actual effect. Because the convention is that the drag source deletes the file when Move is agreed, a receiving side that imports should state Copy explicitly
srcdecl["Drag source: declare the allowed effects (Copy | Move | Link)"] --> tgtsel["Drop target: pick the actual effect"]
tgtsel -->|"Copy"| copyok["Original file remains (the safe side for imports)"]
tgtsel -->|"Move"| moveact["Drag source deletes the file — where 'the original is gone' comes from"]
9.3. Validating Dropped Paths
What travels in CF_HDROP/FileDrop is only paths (Section 3.2). Before importing, run them through the same validation as external input, just as with paste.
- File or folder: Decide as a specification what happens when a whole folder is dropped (import recursively, or refuse).
- OneDrive placeholders: If the path exists but the file body is not local — an on-demand file — a download starts the moment you open it, and it fails when offline. For the behavior and countermeasures, see OneDrive “Files On-Demand” and Business Apps.
- Long and special paths: Accept paths beyond MAX_PATH, network (UNC) paths, and paths on removable media only after confirming that downstream processing can handle them.
- Count and total size: So that dropping thousands of files does not freeze the UI, make the import asynchronous and add a limit and a progress display.
10. Summary
- The clipboard is a mechanism that places the same content in several formats at once in a single area shared within the same desktop (window station). Because the paste destination picks the format, the same copy produces different results.
- Text is CF_UNICODETEXT, files are CF_HDROP, and formatted text is the registered format HTML Format (byte-offset header + UTF-8).
- The paste side searches formats from rich to plain and validates the content as external input. The copy side offers several formats at once, and if it uses delayed rendering, implements the materialization at exit (WM_RENDERALLFORMATS / OleFlushClipboard) as well.
- Monitoring is AddClipboardFormatListener + WM_CLIPBOARDUPDATE. Prepare for OpenClipboard contention with retries, and exclude secrets from history and sync with ExcludeClipboardContentFromMonitorProcessing and the like.
- IT staff can control clipboard history, cloud sync, and RDP redirection through GPO/Intune. All of them default to allowed, so decide deliberately in environments that handle secrets.
- D&D is COM: IDropSource/IDropTarget hand over the same IDataObject as the clipboard. RegisterDragDrop requires OleInitialize (STA).
- Drops onto an elevated app are blocked by UIPI. Move in DragDropEffects is a contract that “the original disappears,” and dropped paths are validated before import.
To users, copy and paste and D&D are features as unremarkable as air. That is exactly why the experience suffers so much when “cannot paste,” “falls apart,” or “disappeared” happens — and conversely, an app that offers several formats and handles drops thoroughly makes everyday operation smoother by that alone. I hope this serves as input when you weigh the priority of revisions.
Related Articles
- What Are COM / ActiveX / OCX? - The Differences and Relationships Explained
- COM STA/MTA Fundamentals - Threading Models and How to Avoid Hangs
- Windows Shell Integration Today — Context Menus, File Associations, and What Changed in Windows 11
- Why EXCEL.EXE Processes Remain After C# Excel COM Automation — Reference Release Patterns and the Replacement Decision
- Windows App UX Design - Priorities by Usage Environment
- OneDrive “Files On-Demand” and Business Apps — The Assumptions Placeholders Break and How to Deal with Them
Related Consulting Areas
KomuraSoft LLC handles the design and implementation of copy-and-paste and drag-and-drop support in business apps (offering several formats, Excel integration, importing dropped files), root-cause investigation of defects such as “it falls apart when pasted” or “the copy disappears,” input automation using clipboard monitoring, and implementing history and sync protections for confidential information. Cases that involve the lower layers of COM and OLE are welcome even if they start from isolating the symptom.
- Windows Application Development
- COM Component Development
- Technical Consulting & Design Review
- Contact Us
References
-
Microsoft Learn, Clipboard Formats. On a window being able to place the same information in several clipboard formats; registered formats via RegisterClipboardFormat (registering the same name returns the same value, so apps can share it); synthesized formats; and excluding content from clipboard history and cloud sync with ExcludeClipboardContentFromMonitorProcessing, CanIncludeInClipboardHistory, and CanUploadToCloudClipboard. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Standard Clipboard Formats. On the definitions of standard formats such as CF_TEXT (ANSI), CF_UNICODETEXT, CF_HDROP, CF_DIB, and CF_LOCALE, and on the system implicitly converting between CF_TEXT and CF_UNICODETEXT using the code page associated with CF_LOCALE. ↩ ↩2 ↩3
-
Microsoft Learn, Shell Clipboard Formats. On CF_HDROP consisting of a DROPFILES structure plus a double-NUL-terminated array of full-path strings; retrieving individual paths with DragQueryFile; and the CFSTR_ shell formats requiring registration via RegisterClipboardFormat. ↩ ↩2
-
Microsoft Learn, HTML Clipboard Format. On the registered name being “HTML Format”; the header structure with offsets (in bytes) such as Version, StartHTML, EndHTML, StartFragment, and EndFragment; the encoding always being UTF-8; and the StartFragment/EndFragment comment convention. ↩ ↩2 ↩3
-
Microsoft Learn, OleGetClipboard function (ole2.h). On how to obtain an IDataObject from the clipboard, and on the warning that clipboard data is not trusted and should be parsed carefully before the app uses it. ↩ ↩2
-
Microsoft Learn, Clipboard Operations. On only one window at a time being able to open the clipboard; placing formats from the most expressive down at copy time; format selection at paste time with EnumClipboardFormats / GetPriorityClipboardFormat; delayed rendering by passing NULL to SetClipboardData and the WM_RENDERFORMAT / WM_RENDERALLFORMATS responsibilities; and the trade-offs of delayed rendering. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Microsoft Learn, OleFlushClipboard function (ole2.h). On the clipboard holding only a pointer to the data object after OleSetClipboard; OleFlushClipboard materializing the data on the clipboard so that pasting remains possible after the app exits; and emptying the clipboard with OleSetClipboard(NULL) when there is no need to keep the data at exit. ↩ ↩2
-
Microsoft Learn, Using the clipboard. On the comparison of the three ways of monitoring the clipboard (viewer windows, sequence numbers, and format listeners); new programs being expected to use a listener via AddClipboardFormatListener; the viewer chain being vulnerable to lapses in chain maintenance; and sequence numbers not being something to poll. ↩ ↩2
-
Microsoft Learn, Policy CSP - Experience. On allowing or blocking clipboard history with the Experience/AllowClipboardHistory policy; availability from Windows 10 version 1809 onward; the default being allowed; and the GPO mapping under “System > OS Policies” with changes taking effect immediately. ↩ ↩2
-
Microsoft Learn, Policy CSP - Privacy. On allowing or blocking cross-device clipboard sync with the Privacy/AllowCrossDeviceClipboard policy; sync taking place between devices signed in with the same Microsoft account or Microsoft Entra account; and the default being allowed. ↩ ↩2 ↩3
-
Microsoft Learn, Policy CSP - ADMX_TerminalServer. On TS_CLIENT_CLIPBOARD (“Do not allow Clipboard redirection,” registry value fDisableClip) being able to prohibit clipboard sharing between local and remote in a Remote Desktop session, and on redirection being allowed by default. ↩ ↩2
-
Microsoft Learn, RegisterDragDrop function (ole2.h). On how to register a drop-target window and its IDropTarget; the call always failing with E_OUTOFMEMORY when COM was initialized with CoInitialize/CoInitializeEx, so that OleInitialize is required; and the drag-source app hanging when the calling thread does not run a message pump. ↩ ↩2 ↩3
-
Microsoft Learn, ChangeWindowMessageFilterEx function (winuser.h). On UIPI being a security mechanism that by default blocks receiving messages from a lower-integrity sender, and on allowing specific messages (MSGFLT_ALLOW) with a per-window message filter. ↩ ↩2 ↩3
-
Microsoft Learn, How to add data to the Clipboard (Windows Forms). On placing data in several formats at once with DataObject and Clipboard.SetDataObject; adding data in several formats so that other apps can recognize it; and the Clipboard class being usable only from an STA thread, so that [STAThread] is required. ↩ ↩2
-
Microsoft Learn, Drag and Drop (COM). On OLE drag & drop running on the three parties IDropSource (drag source), IDropTarget (drop target), and DoDragDrop (the loop OLE provides); providing the same functionality as clipboard copy and paste, so that an app that already implements copy and paste needs only a small addition; and the kinds of feedback. ↩ ↩2
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
Windows App Outsourcing and Custom Software Development: What to Sort Out Before You Ask
Before commissioning Windows app outsourcing or custom software development, here is how to sort out existing software modification, devi...
End of Servicing for Windows Printer Drivers — How Business Apps Should Prepare Their Report and Label Printing
Microsoft is phasing out v3/v4 printer drivers. What Windows protected print mode removes, and how to inventory and prepare report and la...
What Is an OLE Object? — How Embedding and Linking Work and the Pitfalls in Business Documents
An OLE object is what embeds an Excel table in Word. Learn embedding vs. linking, compound files, In-Place Activation, broken links, bloa...
What "Not Responding" Really Is — How Windows Decides an App Has Hung, and How to Design Apps That Don't
Windows marks a window Not Responding after 5 seconds without message retrieval and shows a ghost window: the check, hang causes, UI-thre...
Dark Mode and Contrast Themes in Windows Apps — DWM Dark Title Bars, System Theme Tracking in WinForms/WPF, and Drawing under High Contrast
How to make WinForms/WPF apps follow the dark mode and contrast themes of Windows 11. Covers DWM dark title bars, SetColorMode and ThemeM...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
ActiveX Migration
Topic page for staged decisions around keeping, wrapping, or replacing COM / ActiveX / OCX assets.
UI Threading & Timers
Topic page for WPF / WinForms UI threading, async flow, Dispatcher usage, and timer decisions.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.
Legacy Asset Reuse & Migration Support
We help plan staged migration while continuing to reuse COM / ActiveX / OCX assets, native code, and 32-bit dependencies.
Frequently Asked Questions
Common questions about the topic of this article.
- Why does the formatting fall apart when I paste a table copied from Excel into my app?
- Because the clipboard does not hold "one piece of data": the same content is placed in several formats at once (the app's private format, HTML Format, CSV, Unicode text, and so on), and the destination app picks the format it understands and takes that out. When formatting falls apart, the typical cause is that the destination reads only plain text (CF_UNICODETEXT). If you want the table structure as well, implement the paste side so that it prefers HTML Format or CSV. Conversely, if you want other apps to paste correctly from a copy made in your own app, offer a rich format and a plain format together at copy time.
- Why can I no longer paste after closing the app I copied from?
- Because the source uses delayed rendering. Apps that handle large data do not place the data itself at copy time; they register only a promise to "produce it when asked" on the clipboard. If the source then exits without materializing the data in response to WM_RENDERALLFORMATS at shutdown, the unrendered formats are lost. An app that uses the OLE clipboard (IDataObject) can keep pasting possible after exit by calling OleFlushClipboard at shutdown to materialize the data.
- How can my own app monitor the clipboard for changes?
- The currently recommended method is to register your window as a listener with AddClipboardFormatListener and handle the WM_CLIPBOARDUPDATE message that arrives every time the contents change. Polling the contents on a timer wastes work, and the old viewer chain based on SetClipboardViewer is kept only for backward compatibility, because a defect in one app in the chain breaks the whole thing. Note also that OpenClipboard on a read can fail through contention with another process, so implementing a retry with a short wait makes it stable.
- Is there a way to keep secrets such as passwords out of clipboard history (Win+V)?
- There are two means, one on the app side and one on the policy side. On the app side, if you also place a registered format named ExcludeClipboardContentFromMonitorProcessing when you copy, that content is included in neither history nor cross-device sync. You can also control each separately with CanIncludeInClipboardHistory (history only) and CanUploadToCloudClipboard (sync only). This is the mechanism password managers use. To stop it for the whole organization, you can disable history and cloud sync themselves with AllowClipboardHistory and AllowCrossDeviceClipboard through Group Policy or Intune (Policy CSP).
- Why can't I drag and drop a file onto an app that is running as administrator?
- Because a security mechanism called UIPI (User Interface Privilege Isolation) blocks messages sent from a lower-integrity process to a higher-integrity window. File Explorer runs at ordinary privilege (medium integrity), so drag-and-drop notifications never reach the window of an elevated app. A workaround that individually allows messages such as WM_DROPFILES with ChangeWindowMessageFilterEx is known, but it is limited to the old-style drop notification. The proper fix is to stop designing the app to run elevated all the time and to isolate only the work that needs elevation into a separate process.