app.dhover×600 error×271all
// Win32 F16 — clipboard + drag-and-drop (../../../features/f16-clipboard-dnd.md),
// built on the scaffold (../scaffold/app.d). One process, two roles:
//
//   * default role: a window that walks a timer-driven phase schedule —
//       1. paste_startup    enumerate + read whatever is on the clipboard at
//                           launch (the host-bridging probe: preload with
//                           wl-copy/xclip and see if Wine imported it);
//       2. copy_immediate   OpenClipboard/EmptyClipboard/SetClipboardData
//                           (CF_UNICODETEXT, GMEM_MOVEABLE HGLOBAL) with
//                           "é漢🎈", then read it back and log the formats
//                           Windows synthesizes around it;
//       3. spawn a reader   (--reader child in the same prefix) proving two
//                           Wine processes share the prefix clipboard;
//       4. copy_delayed     SetClipboardData(CF_UNICODETEXT, NULL) — delayed
//                           rendering; a reader THREAD demands the data 300 ms
//                           later and the WM_RENDERFORMAT arrival is timed;
//       5. copy_delayed2    delayed again, demanded by a spawned reader
//                           PROCESS — cross-process WM_RENDERFORMAT;
//       6. grab             a worker takes clipboard ownership ->
//                           WM_DESTROYCLIPBOARD (ownership loss) +
//                           WM_CLIPBOARDUPDATE (AddClipboardFormatListener);
//       7. dnd              OLE drag-and-drop entirely in-process: a
//                           hand-rolled (raw vtable) IDataObject + IDropSource
//                           DoDragDrop'd onto this window's own hand-rolled
//                           IDropTarget (RegisterDragDrop), CF_HDROP carrying
//                           a real temp file; the full DragEnter/DragOver/
//                           Drop negotiation and DROPEFFECT_* are logged;
//       8. destroy with an unrendered delayed format still owned ->
//                           WM_RENDERALLFORMATS (who pays when the source
//                           exits) — then clean exit 0.
//   * --reader role: opens the clipboard (retry loop), logs the offered
//     formats and the CF_UNICODETEXT byte count + content match, exits 0.
//
// COM is done with hand-declared vtables (no druntime ole2 imports): the
// binary layout is the contract under test. OleInitialize — NOT CoInitialize —
// is required for RegisterDragDrop/DoDragDrop (clipboard+DnD live in ole32).
// A watchdog thread + SEH filter guarantee the log survives and the process
// exits 0 whatever OLE does under a headless Wine driver.
module 
(module) app
app
;
import
(package) core
core
.
(package) core.stdc
stdc
.
(module) core.stdc.stdio

D header file for C99 <stdio.h>

pubs.opengroup.org/onlinepubs/009695399/basedefs/stdio.h.html, stdio.h

Source

core/stdc/stdio.d

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseDistributed under the Boost Software License 1.0. (See accompanying file LICENSE)@authorsSean Kelly, Alex Rønne Petersen@standardsISO/IEC 9899:1999 (E)
stdio
:
(alias) app.snprintf = int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogc
snprintf
;
import
(package) core
core
.
(package) core.stdc
stdc
.
(module) core.stdc.string

D header file for C99.

pubs.opengroup.org/onlinepubs/009695399/basedefs/string.h.html, string.h

Source

core/stdc/string.d

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseDistributed under the Boost Software License 1.0. (See accompanying file LICENSE)@authorsSean Kelly@standardsISO/IEC 9899:1999 (E)
string
:
(alias) app.memcmp = int core.stdc.string.memcmp(scope const(void*) s1, scope const(void*) s2, ulong n) pure nothrow @nogc
memcmp
,
(alias) app.memcpy = void* core.stdc.string.memcpy(return scope void* s1, scope const(void*) s2, ulong n) pure nothrow @nogc
memcpy
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.windows
windows
.
(module) core.sys.windows.windows

Windows API header module

Translated from MinGW API for MS-Windows 4.0

Source

core/sys/windows/windows.d

windows
;
import instrument;
unable to read module `instrument` Expected 'instrument.d' or 'instrument/package.d' in one of the following import paths:
unable to read module `instrument` Expected 'instrument.d' or 'instrument/package.d' in one of the following import paths:
pragma(lib, "ole32"); pragma(lib, "shell32"); // --------------------------------------------------------------------------- // Win32/OLE declarations missing from druntime's core.sys.windows. enum UINT
(constant) _error_ app.WM_CLIPBOARDUPDATE = __error
WM_CLIPBOARDUPDATE
= 0x031D;
undefined identifier `UINT`
enum DWORD
(constant) _error_ app.DROPEFFECT_NONE = __error
DROPEFFECT_NONE
= 0,
(constant) _error_ app.DROPEFFECT_COPY = __error
DROPEFFECT_COPY
= 1,
(constant) _error_ app.DROPEFFECT_MOVE = __error
DROPEFFECT_MOVE
= 2,
undefined identifier `DWORD`
undefined identifier `DWORD`
undefined identifier `DWORD`
undefined identifier `DWORD`
(constant) _error_ app.DROPEFFECT_LINK = __error
DROPEFFECT_LINK
= 4;
enum HRESULT
(constant) _error_ app.DRAGDROP_S_DROP = __error
DRAGDROP_S_DROP
= 0x00040100,
(constant) _error_ app.DRAGDROP_S_CANCEL = __error
DRAGDROP_S_CANCEL
= 0x00040101,
undefined identifier `HRESULT`
undefined identifier `HRESULT`
undefined identifier `HRESULT`
(constant) _error_ app.DRAGDROP_S_USEDEFAULTCURSORS = __error
DRAGDROP_S_USEDEFAULTCURSORS
= 0x00040102;
enum HRESULT
(constant) _error_ app.E_NOINTERFACE = __error__
E_NOINTERFACE
= cast(HRESULT) 0x80004002,
undefined identifier `HRESULT`
undefined identifier `HRESULT`
undefined identifier `HRESULT`
undefined identifier `HRESULT`
undefined identifier `HRESULT`
(constant) _error_ app.E_NOTIMPL = __error__
E_NOTIMPL
= cast(HRESULT) 0x80004001,
undefined identifier `HRESULT`
(constant) _error_ app.DV_E_FORMATETC = __error__
DV_E_FORMATETC
= cast(HRESULT) 0x80040064,
undefined identifier `HRESULT`
(constant) _error_ app.OLE_E_ADVISENOTSUPPORTED = __error__
OLE_E_ADVISENOTSUPPORTED
= cast(HRESULT) 0x80040003;
undefined identifier `HRESULT`
enum DWORD
(constant) _error_ app.DVASPECT_CONTENT = __error
DVASPECT_CONTENT
= 1,
(constant) _error_ app.TYMED_HGLOBAL = __error
TYMED_HGLOBAL
= 1,
(constant) _error_ app.DATADIR_GET = __error
DATADIR_GET
= 1;
undefined identifier `DWORD`
undefined identifier `DWORD`
undefined identifier `DWORD`
struct
(struct) app.FORMATETC
FORMATETC
{ ushort
(field) ushort app.FORMATETC.cfFormat
cfFormat
; // CLIPFORMAT
void*
(field) void* app.FORMATETC.ptd
ptd
;
DWORD
(field) _error_ app.FORMATETC.dwAspect
dwAspect
;
undefined identifier `DWORD`
int
(field) int app.FORMATETC.lindex
lindex
;
DWORD
(field) _error_ app.FORMATETC.tymed
tymed
;
undefined identifier `DWORD`
} struct
(struct) app.STGMEDIUM
STGMEDIUM
{ DWORD
(field) _error_ app.STGMEDIUM.tymed
tymed
;
undefined identifier `DWORD`
void*
(field) void* app.STGMEDIUM.hGlobal
hGlobal
; // the union collapsed to the one arm used here
void*
(field) void* app.STGMEDIUM.pUnkForRelease
pUnkForRelease
;
} struct
(struct) app.DROPFILES
DROPFILES
{ DWORD
(field) _error_ app.DROPFILES.pFiles
pFiles
; // offset of the file list from the struct start
undefined identifier `DWORD`
POINT
(field) _error_ app.DROPFILES.pt
pt
;
undefined identifier `POINT`, did you mean struct `POINTL`?
BOOL
(field) _error_ app.DROPFILES.fNC
fNC
;
undefined identifier `BOOL`
BOOL
(field) _error_ app.DROPFILES.fWide
fWide
; // TRUE: the list is UTF-16
undefined identifier `BOOL`
} struct
(struct) app.POINTL
POINTL
{ LONG
(field) _error_ app.POINTL.x
x
,
(field) _error_ app.POINTL.y
y
;
undefined identifier `LONG`
undefined identifier `LONG`
} extern (Windows) nothrow @nogc { HRESULT
app.OleInitialize
OleInitialize
(void*);
undefined identifier `HRESULT`
void
void app.OleUninitialize() nothrow @nogc
OleUninitialize
();
HRESULT
app.RegisterDragDrop
RegisterDragDrop
(HWND, void*);
undefined identifier `HRESULT`
undefined identifier `HWND`
HRESULT
app.RevokeDragDrop
RevokeDragDrop
(HWND);
undefined identifier `HRESULT`
undefined identifier `HWND`
HRESULT
app.DoDragDrop
DoDragDrop
(void*
(parameter) void* dataObj
dataObj
, void* dropSource, DWORD okEffects,
undefined identifier `HRESULT`
undefined identifier `DWORD`
DWORD*
(parameter) DWORD* effect
effect
);
undefined identifier `DWORD`
void
app.ReleaseStgMedium
ReleaseStgMedium
(
(struct) app.STGMEDIUM
STGMEDIUM
*);
UINT
app.DragQueryFileW
DragQueryFileW
(void* hDrop, UINT iFile, wchar*
(parameter) wchar* buf
buf
, UINT cch);
undefined identifier `UINT`
undefined identifier `UINT`
undefined identifier `UINT`
BOOL
app.AddClipboardFormatListener
AddClipboardFormatListener
(HWND);
undefined identifier `BOOL`
undefined identifier `HWND`
DWORD
app.GetClipboardSequenceNumber
GetClipboardSequenceNumber
();
undefined identifier `DWORD`
} // IIDs (hand-declared; values from the platform SDK headers). __gshared immutable GUID
_error_ app.IID_IUnknown
IID_IUnknown
=
undefined identifier `GUID`
GUID(0, 0, 0, [0xC0, 0, 0, 0, 0, 0, 0, 0x46]);
undefined identifier `GUID`
__gshared immutable GUID
_error_ app.IID_IDataObject
IID_IDataObject
=
undefined identifier `GUID`
GUID(0x10e, 0, 0, [0xC0, 0, 0, 0, 0, 0, 0, 0x46]);
undefined identifier `GUID`
__gshared immutable GUID
_error_ app.IID_IDropSource
IID_IDropSource
=
undefined identifier `GUID`
GUID(0x121, 0, 0, [0xC0, 0, 0, 0, 0, 0, 0, 0x46]);
undefined identifier `GUID`
__gshared immutable GUID
_error_ app.IID_IDropTarget
IID_IDropTarget
=
undefined identifier `GUID`
GUID(0x122, 0, 0, [0xC0, 0, 0, 0, 0, 0, 0, 0x46]);
undefined identifier `GUID`
__gshared immutable GUID
_error_ app.IID_IEnumFORMATETC
IID_IEnumFORMATETC
=
undefined identifier `GUID`
GUID(0x103, 0, 0, [0xC0, 0, 0, 0, 0, 0, 0, 0x46]);
undefined identifier `GUID`
bool
app.sameGuid
sameGuid
(const(GUID)*
(parameter) const(GUID)* a
a
, const(GUID)* b) nothrow @nogc
undefined identifier `GUID`
undefined identifier `GUID`
{ return memcmp(a, b, GUID.sizeof) == 0; } // --------------------------------------------------------------------------- // Hand-declared COM vtables. Every interface here is { vtbl*, refs } with a // static vtbl of extern(Windows) function pointers — the raw ABI a framework // binding has to reproduce. Refcounting is a formality (static lifetime). extern (Windows) nothrow { alias
(alias) app.FnQI = _error_
FnQI
= HRESULT function(void*, const(GUID)*, void**);
undefined identifier `HRESULT`
undefined identifier `GUID`
alias
(alias) app.FnRef = _error_
FnRef
= ULONG function(void*);
undefined identifier `ULONG`
// IDropTarget alias
(alias) app.FnDragEnter = _error_
FnDragEnter
= HRESULT function(void*, void*, DWORD,
(struct) app.POINTL
POINTL
, DWORD*);
undefined identifier `HRESULT`
undefined identifier `DWORD`
undefined identifier `DWORD`
alias
(alias) app.FnDragOver = _error_
FnDragOver
= HRESULT function(void*, DWORD,
(struct) app.POINTL
POINTL
, DWORD*);
undefined identifier `HRESULT`
undefined identifier `DWORD`
undefined identifier `DWORD`
alias
(alias) app.FnDragLeave = _error_
FnDragLeave
= HRESULT function(void*);
undefined identifier `HRESULT`
// IDropSource alias
(alias) app.FnQueryContinueDrag = _error_
FnQueryContinueDrag
= HRESULT function(void*, BOOL, DWORD);
undefined identifier `HRESULT`
undefined identifier `BOOL`
undefined identifier `DWORD`
alias
(alias) app.FnGiveFeedback = _error_
FnGiveFeedback
= HRESULT function(void*, DWORD);
undefined identifier `HRESULT`
undefined identifier `DWORD`
// IDataObject alias
(alias) app.FnGetData = _error_
FnGetData
= HRESULT function(void*,
(struct) app.FORMATETC
FORMATETC
*,
(struct) app.STGMEDIUM
STGMEDIUM
*);
undefined identifier `HRESULT`
alias
(alias) app.FnQueryGetData = _error_
FnQueryGetData
= HRESULT function(void*,
(struct) app.FORMATETC
FORMATETC
*);
undefined identifier `HRESULT`
alias
(alias) app.FnGetCanonical = _error_
FnGetCanonical
= HRESULT function(void*,
(struct) app.FORMATETC
FORMATETC
*,
(struct) app.FORMATETC
FORMATETC
*);
undefined identifier `HRESULT`
alias
(alias) app.FnSetData = _error_
FnSetData
= HRESULT function(void*,
(struct) app.FORMATETC
FORMATETC
*,
(struct) app.STGMEDIUM
STGMEDIUM
*, BOOL);
undefined identifier `HRESULT`
undefined identifier `BOOL`
alias
(alias) app.FnEnumFormatEtc = _error_
FnEnumFormatEtc
= HRESULT function(void*, DWORD, void**);
undefined identifier `HRESULT`
undefined identifier `DWORD`
alias
(alias) app.FnDAdvise = _error_
FnDAdvise
= HRESULT function(void*,
(struct) app.FORMATETC
FORMATETC
*, DWORD, void*, DWORD*);
undefined identifier `HRESULT`
undefined identifier `DWORD`
undefined identifier `DWORD`
alias
(alias) app.FnDUnadvise = _error_
FnDUnadvise
= HRESULT function(void*, DWORD);
undefined identifier `HRESULT`
undefined identifier `DWORD`
alias
(alias) app.FnEnumDAdvise = _error_
FnEnumDAdvise
= HRESULT function(void*, void**);
undefined identifier `HRESULT`
// IEnumFORMATETC alias
(alias) app.FnEnumNext = _error_
FnEnumNext
= HRESULT function(void*, ULONG,
(struct) app.FORMATETC
FORMATETC
*, ULONG*);
undefined identifier `HRESULT`
undefined identifier `ULONG`
undefined identifier `ULONG`
alias
(alias) app.FnEnumSkip = _error_
FnEnumSkip
= HRESULT function(void*, ULONG);
undefined identifier `HRESULT`
undefined identifier `ULONG`
alias
(alias) app.FnEnumReset = _error_
FnEnumReset
= HRESULT function(void*);
undefined identifier `HRESULT`
alias
(alias) app.FnEnumClone = _error_
FnEnumClone
= HRESULT function(void*, void**);
undefined identifier `HRESULT`
} struct
(struct) app.DropTargetVtbl
DropTargetVtbl
{
(alias) app.FnQI = _error_
FnQI
(field) _error_ app.DropTargetVtbl.QueryInterface
QueryInterface
;
(alias) app.FnRef = _error_
FnRef
(field) _error_ app.DropTargetVtbl.AddRef
AddRef
,
(field) _error_ app.DropTargetVtbl.Release
Release
;
(alias) app.FnDragEnter = _error_
FnDragEnter
(field) _error_ app.DropTargetVtbl.DragEnter
DragEnter
;
(alias) app.FnDragOver = _error_
FnDragOver
(field) _error_ app.DropTargetVtbl.DragOver
DragOver
;
(alias) app.FnDragLeave = _error_
FnDragLeave
(field) _error_ app.DropTargetVtbl.DragLeave
DragLeave
;
(alias) app.FnDragEnter = _error_
FnDragEnter
(field) _error_ app.DropTargetVtbl.Drop
Drop
; // same signature as DragEnter
} struct
(struct) app.DropSourceVtbl
DropSourceVtbl
{
(alias) app.FnQI = _error_
FnQI
(field) _error_ app.DropSourceVtbl.QueryInterface
QueryInterface
;
(alias) app.FnRef = _error_
FnRef
(field) _error_ app.DropSourceVtbl.AddRef
AddRef
,
(field) _error_ app.DropSourceVtbl.Release
Release
;
(alias) app.FnQueryContinueDrag = _error_
FnQueryContinueDrag
(field) _error_ app.DropSourceVtbl.QueryContinueDrag
QueryContinueDrag
;
(alias) app.FnGiveFeedback = _error_
FnGiveFeedback
(field) _error_ app.DropSourceVtbl.GiveFeedback
GiveFeedback
;
} struct
(struct) app.DataObjectVtbl
DataObjectVtbl
{
(alias) app.FnQI = _error_
FnQI
(field) _error_ app.DataObjectVtbl.QueryInterface
QueryInterface
;
(alias) app.FnRef = _error_
FnRef
(field) _error_ app.DataObjectVtbl.AddRef
AddRef
,
(field) _error_ app.DataObjectVtbl.Release
Release
;
(alias) app.FnGetData = _error_
FnGetData
(field) _error_ app.DataObjectVtbl.GetData
GetData
;
(alias) app.FnGetData = _error_
FnGetData
(field) _error_ app.DataObjectVtbl.GetDataHere
GetDataHere
;
(alias) app.FnQueryGetData = _error_
FnQueryGetData
(field) _error_ app.DataObjectVtbl.QueryGetData
QueryGetData
;
(alias) app.FnGetCanonical = _error_
FnGetCanonical
(field) _error_ app.DataObjectVtbl.GetCanonicalFormatEtc
GetCanonicalFormatEtc
;
(alias) app.FnSetData = _error_
FnSetData
(field) _error_ app.DataObjectVtbl.SetData
SetData
;
(alias) app.FnEnumFormatEtc = _error_
FnEnumFormatEtc
(field) _error_ app.DataObjectVtbl.EnumFormatEtc
EnumFormatEtc
;
(alias) app.FnDAdvise = _error_
FnDAdvise
(field) _error_ app.DataObjectVtbl.DAdvise
DAdvise
;
(alias) app.FnDUnadvise = _error_
FnDUnadvise
(field) _error_ app.DataObjectVtbl.DUnadvise
DUnadvise
;
(alias) app.FnEnumDAdvise = _error_
FnEnumDAdvise
(field) _error_ app.DataObjectVtbl.EnumDAdvise
EnumDAdvise
;
} struct
(struct) app.EnumFmtVtbl
EnumFmtVtbl
{
(alias) app.FnQI = _error_
FnQI
(field) _error_ app.EnumFmtVtbl.QueryInterface
QueryInterface
;
(alias) app.FnRef = _error_
FnRef
(field) _error_ app.EnumFmtVtbl.AddRef
AddRef
,
(field) _error_ app.EnumFmtVtbl.Release
Release
;
(alias) app.FnEnumNext = _error_
FnEnumNext
(field) _error_ app.EnumFmtVtbl.Next
Next
;
(alias) app.FnEnumSkip = _error_
FnEnumSkip
(field) _error_ app.EnumFmtVtbl.Skip
Skip
;
(alias) app.FnEnumReset = _error_
FnEnumReset
(field) _error_ app.EnumFmtVtbl.Reset
Reset
;
(alias) app.FnEnumClone = _error_
FnEnumClone
(field) _error_ app.EnumFmtVtbl.Clone
Clone
;
} struct
(struct) app.ComObj!(app.DropSourceVtbl)
ComObj
(Vtbl)
{
(alias) Vtbl = app.DropSourceVtbl
Vtbl
*
(field) app.DropSourceVtbl* app.ComObj!(app.DropSourceVtbl).vtbl
vtbl
;
LONG
(field) _error_ app.ComObj!(app.DropSourceVtbl).refs
refs
= 1;
undefined identifier `LONG`
undefined identifier `LONG`
undefined identifier `LONG`
undefined identifier `LONG`
uint
(field) uint app.ComObj!(app.DropSourceVtbl).cursor
cursor
; // EnumFORMATETC position / DropSource call counter
} // --------------------------------------------------------------------------- // Demo state. struct
(struct) app.Demo
Demo
{ HINSTANCE
(field) _error_ app.Demo.inst
inst
;
undefined identifier `HINSTANCE`
HWND
(field) _error_ app.Demo.hwnd
hwnd
;
undefined identifier `HWND`
uint
(field) uint app.Demo.ticks
ticks
;
bool
(field) bool app.Demo.autoExit
autoExit
;
int
(field) int app.Demo.phase
phase
; // index into the schedule, for log readability
long
(field) long app.Demo.delayedSetAt
delayedSetAt
; // when SetClipboardData(fmt, NULL) was issued
int
(field) int app.Demo.renderDemands
renderDemands
; // WM_RENDERFORMAT count
int
(field) int app.Demo.renderAll
renderAll
; // WM_RENDERALLFORMATS count
bool
(field) bool app.Demo.dndDone
dndDone
;
HANDLE
(field) _error_ app.Demo.hDropFiles
hDropFiles
; // CF_HDROP HGLOBAL template (owned by the source)
undefined identifier `HANDLE`
WCHAR[MAX_PATH]
(field) _error_ app.Demo.dropPath
dropPath
;
undefined identifier `MAX_PATH`
ComObj!
(struct) app.DropTargetVtbl
DropTargetVtbl
(field) _error_ app.Demo.target
target
;
template instance `app.ComObj!(DropTargetVtbl)` error instantiating
ComObj!
(struct) app.DropSourceVtbl
DropSourceVtbl
(field) _error_ app.Demo.source
source
;
template instance `app.ComObj!(DropSourceVtbl)` error instantiating
ComObj!
(struct) app.DataObjectVtbl
DataObjectVtbl
(field) _error_ app.Demo.data
data
;
template instance `app.ComObj!(DataObjectVtbl)` error instantiating
} __gshared
(struct) app.Demo
Demo
_error_ app.g
g
;
__gshared immutable wchar[]
(immutable global) immutable(wstring) app.PAYLOAD
PAYLOAD
= "é漢🎈\0"w; // 4 UTF-16 units + NUL
enum
(constant) ulong app.PAYLOAD_BYTES = 10LU
PAYLOAD_BYTES
=
(immutable global) immutable(wstring) app.PAYLOAD
PAYLOAD
.
(field) ulong immutable(wstring).length
length
* 2; // 10
__gshared
(struct) app.DropTargetVtbl
DropTargetVtbl
(__gshared global) app.DropTargetVtbl app.gTargetVtbl
gTargetVtbl
;
__gshared
(struct) app.DropSourceVtbl
DropSourceVtbl
(__gshared global) app.DropSourceVtbl app.gSourceVtbl
gSourceVtbl
;
__gshared
(struct) app.DataObjectVtbl
DataObjectVtbl
(__gshared global) app.DataObjectVtbl app.gDataVtbl
gDataVtbl
;
__gshared
(struct) app.EnumFmtVtbl
EnumFmtVtbl
(__gshared global) app.EnumFmtVtbl app.gEnumVtbl
gEnumVtbl
;
// --------------------------------------------------------------------------- // Crash/hang capture: verdict lines must survive anything OLE does headless. extern (Windows) LONG
app.sehFilter
sehFilter
(EXCEPTION_POINTERS* ep) nothrow
undefined identifier `LONG`
undefined identifier `EXCEPTION_POINTERS`
{ const
_error_ code
code
= ep && ep.ExceptionRecord ? ep.ExceptionRecord.ExceptionCode : 0;
logEvent("crash seh code=0x%08lx phase=%d", code, g.
g.phase
phase
);
ExitProcess(0); return 1; } extern (Windows) uint
uint app.watchdogProc(void* arg) nothrow
watchdogProc
(void*
(parameter) void* arg
arg
) nothrow
{ Sleep(cast(DWORD) cast(
(unresolved type) size_t
size_t
) arg);
undefined identifier `Sleep`
logEvent("watchdog_fired phase=%d dnd_done=%d", g.
g.phase
phase
, g.
g.dndDone
dndDone
? 1 : 0);
undefined identifier `logEvent`
ExitProcess(0);
undefined identifier `ExitProcess`
return 0; } // --------------------------------------------------------------------------- // Clipboard helpers. const(char)*
app.cfName
cfName
(UINT
(parameter) UINT fmt
fmt
, char*
(parameter) char* scratch
scratch
,
(alias) object.size_t = ulong
size_t
(parameter) size_t n
n
) nothrow
undefined identifier `UINT`
{ switch (fmt) { case CF_TEXT: return "CF_TEXT"; case CF_BITMAP: return "CF_BITMAP"; case CF_OEMTEXT: return "CF_OEMTEXT"; case CF_UNICODETEXT: return "CF_UNICODETEXT"; case CF_LOCALE: return "CF_LOCALE"; case CF_HDROP: return "CF_HDROP"; case CF_DIB: return "CF_DIB"; case CF_DIBV5: return "CF_DIBV5"; default: WCHAR[64]
_error_ wname
wname
;
const
_error_ len
len
= GetClipboardFormatNameW(fmt, wname.
wname.ptr
ptr
, 64);
if (len > 0) { int
_error_ p
p
;
foreach (
(parameter) i
i
; 0 .. len)
if (p < n - 1) scratch[p++] = wname[i] < 0x80 ? cast(char) wname[i] : '?'; scratch[p] = 0; } else snprintf(scratch, n, "0x%04x", fmt); return scratch; } } // Log `clip_offer formats=[...]` for the currently-open clipboard. void
void app.logOfferedFormats(const(char)* who) nothrow
logOfferedFormats
(const(char)*
(parameter) const(char)* who
who
) nothrow
{ char[512]
(local variable) char[512] line
line
= 0;
char[80]
(local variable) char[80] scratch
scratch
;
int
(local variable) int p
p
;
UINT
(local variable) _error_ fmt
fmt
= 0;
undefined identifier `UINT`
int
(local variable) int count
count
;
while ((fmt = EnumClipboardFormats(fmt)) != 0) { const
(local variable) const(_error_) nm
nm
= cfName(fmt, scratch.
scratch.ptr
ptr
, scratch.
scratch.length
length
);
(local variable) int p
p
+=
int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogc
snprintf
(
(local variable) char[512] line
line
.
(constant) char* char[512].ptr = &line
ptr
+
(local variable) int p
p
,
(local variable) char[512] line
line
.
(constant) ulong char[512].length = 512LU
length
-
(local variable) int p
p
, "%s%s",
(local variable) int count
count
? ",".
(constant) immutable(char)* ",".ptr = ","
ptr
: "".
(constant) immutable(char)* "".ptr = ""
ptr
, nm);
++
(local variable) int count
count
;
} logEvent("clip_offer who=%s n=%d formats=[%s] seq=%lu",
undefined identifier `logEvent`
who, count, line.
line.ptr
ptr
, GetClipboardSequenceNumber());
} // The GMEM_MOVEABLE contract: SetClipboardData takes an HGLOBAL allocated // with GMEM_MOVEABLE and the *system* owns it afterwards (never free it, // never use it again after a successful SetClipboardData). HANDLE
app.makeTextHGlobal
makeTextHGlobal
(const(wchar)[] s) nothrow
undefined identifier `HANDLE`
{ HANDLE
_error_ h
h
= GlobalAlloc(GMEM_MOVEABLE, s.
s.length
length
* 2);
if (h is null) return null; void*
_error_ p
p
= GlobalLock(h);
memcpy(p, s.
s.ptr
ptr
, s.
s.length
length
* 2);
GlobalUnlock(h); return h; } bool
app.openClipboardRetry
openClipboardRetry
(HWND owner, int tries = 20) nothrow
undefined identifier `HWND`
{ foreach (
(parameter) i
i
; 0 .. tries)
{ if (OpenClipboard(owner)) return true; Sleep(50); } return false; } // Read CF_UNICODETEXT from the open clipboard; log bytes + payload match. void
void app.readUnicodeText(const(char)* who) nothrow
readUnicodeText
(const(char)*
(parameter) const(char)* who
who
) nothrow
{ HANDLE
(local variable) _error_ h
h
= GetClipboardData(CF_UNICODETEXT);
undefined identifier `HANDLE`
undefined identifier `GetClipboardData`
if (h is null) { logEvent("clip_read who=%s fmt=CF_UNICODETEXT result=null err=%lu",
undefined identifier `logEvent`
who, GetLastError()); return; } auto
(local variable) _error_ p
p
= cast(const(wchar)*) GlobalLock(h);
undefined identifier `GlobalLock`
const
(local variable) const(_error_) total
total
= GlobalSize(h); // includes the NUL (and any slack)
undefined identifier `GlobalSize`
(alias) object.size_t = ulong
size_t
(local variable) ulong len
len
;
while (p[len]) ++
(local variable) ulong len
len
;
const
(local variable) const(_error_) match
match
=
(local variable) ulong len
len
+ 1 ==
(immutable global) immutable(wstring) app.PAYLOAD
PAYLOAD
.
(field) ulong immutable(wstring).length
length
&&
int core.stdc.string.memcmp(scope const(void*) s1, scope const(void*) s2, ulong n) pure nothrow @nogc
memcmp
(p,
(immutable global) immutable(wstring) app.PAYLOAD
PAYLOAD
.
(field) immutable(wchar)* immutable(wstring).ptr
ptr
,
(constant) ulong app.PAYLOAD_BYTES = 10LU
PAYLOAD_BYTES
) == 0;
logEvent("clip_read who=%s fmt=CF_UNICODETEXT bytes=%zu wchars=%zu payload_match=%d",
undefined identifier `logEvent`
who, total, len, match ? 1 : 0); GlobalUnlock(h);
undefined identifier `GlobalUnlock`
} // --------------------------------------------------------------------------- // Phases 1-2: paste at startup, immediate copy + readback. void
void app.phasePasteStartup() nothrow
phasePasteStartup
() nothrow
{ logEvent("phase name=paste_startup");
undefined identifier `logEvent`
if (!openClipboardRetry(null)) return logEvent("error what=OpenClipboard code=%lu", GetLastError());
undefined identifier `logEvent`
void app.logOfferedFormats(const(char)* who) nothrow
logOfferedFormats
("owner_startup");
if (IsClipboardFormatAvailable(CF_UNICODETEXT))
undefined identifier `IsClipboardFormatAvailable`
void app.readUnicodeText(const(char)* who) nothrow
readUnicodeText
("owner_startup");
else logEvent("clip_read who=owner_startup fmt=CF_UNICODETEXT result=absent");
undefined identifier `logEvent`
CloseClipboard();
undefined identifier `CloseClipboard`
} void
void app.phaseCopyImmediate() nothrow
phaseCopyImmediate
() nothrow
{ logEvent("phase name=copy_immediate");
undefined identifier `logEvent`
if (!OpenClipboard(g.
g.hwnd
hwnd
))
undefined identifier `OpenClipboard`
return logEvent("error what=OpenClipboard code=%lu", GetLastError());
undefined identifier `logEvent`
EmptyClipboard(); // we become the owner
undefined identifier `EmptyClipboard`
HANDLE
(local variable) _error_ h
h
= makeTextHGlobal(PAYLOAD);
undefined identifier `HANDLE`
const
(local variable) const(_error_) ok
ok
= SetClipboardData(CF_UNICODETEXT, h);
undefined identifier `SetClipboardData`
logEvent("clip_send fmt=CF_UNICODETEXT bytes=%zu delayed=0 ok=%d owner=%p",
undefined identifier `logEvent`
PAYLOAD_BYTES, ok !is null ? 1 : 0, GetClipboardOwner()); CloseClipboard();
undefined identifier `CloseClipboard`
// Read-back: what did Windows synthesize around CF_UNICODETEXT? if (openClipboardRetry(null)) {
void app.logOfferedFormats(const(char)* who) nothrow
logOfferedFormats
("owner_readback");
void app.readUnicodeText(const(char)* who) nothrow
readUnicodeText
("owner_readback");
CloseClipboard();
undefined identifier `CloseClipboard`
} } // --------------------------------------------------------------------------- // Phase 4: delayed rendering. SetClipboardData(fmt, NULL) promises the data; // WM_RENDERFORMAT arrives only when somebody calls GetClipboardData. void
void app.phaseCopyDelayed(const(char)* tag) nothrow
phaseCopyDelayed
(const(char)*
(parameter) const(char)* tag
tag
) nothrow
{ logEvent("phase name=%s", tag);
undefined identifier `logEvent`
if (!OpenClipboard(g.
g.hwnd
hwnd
))
undefined identifier `OpenClipboard`
return logEvent("error what=OpenClipboard code=%lu", GetLastError());
undefined identifier `logEvent`
EmptyClipboard();
undefined identifier `EmptyClipboard`
const
(local variable) const(_error_) ok
ok
= SetClipboardData(CF_UNICODETEXT, null); // the promise
undefined identifier `SetClipboardData`
g.
g.delayedSetAt
delayedSetAt
= nowUs();
logEvent("clip_send fmt=CF_UNICODETEXT bytes=0 delayed=1 ok=%d", ok is null ? 0 : 1);
undefined identifier `logEvent`
// NOTE: SetClipboardData(fmt, NULL) returns NULL on success here — NULL is // also the "no data" success answer for delayed rendering (check GetLastError). logEvent("clip_send_delayed err=%lu", GetLastError());
undefined identifier `logEvent`
CloseClipboard();
undefined identifier `CloseClipboard`
} // The in-process demand: a thread reads the clipboard 300 ms after the // delayed SetClipboardData, forcing a WM_RENDERFORMAT into the main pump. extern (Windows) uint
uint app.demandThread(void* arg) nothrow
demandThread
(void*
(parameter) void* arg
arg
) nothrow
{ Sleep(300);
undefined identifier `Sleep`
logEvent("thread=demand action=GetClipboardData t=%lld", nowUs());
undefined identifier `logEvent`
if (!openClipboardRetry(null)) return 0;
void app.readUnicodeText(const(char)* who) nothrow
readUnicodeText
("demand_thread");
CloseClipboard();
undefined identifier `CloseClipboard`
return 0; } // The ownership grab: another "app" (a thread opening with no owner window) // empties the clipboard -> the owner window gets WM_DESTROYCLIPBOARD. extern (Windows) uint
uint app.grabThread(void* arg) nothrow
grabThread
(void*
(parameter) void* arg
arg
) nothrow
{ if (!openClipboardRetry(null)) return 0; logEvent("thread=grab action=EmptyClipboard t=%lld", nowUs());
undefined identifier `logEvent`
EmptyClipboard();
undefined identifier `EmptyClipboard`
HANDLE
(local variable) _error_ h
h
= makeTextHGlobal("grabbed\0"w);
undefined identifier `HANDLE`
SetClipboardData(CF_UNICODETEXT, h);
undefined identifier `SetClipboardData`
CloseClipboard();
undefined identifier `CloseClipboard`
return 0; } void
void app.spawnReader(int n) nothrow
spawnReader
(int
(parameter) int n
n
) nothrow
{ WCHAR[MAX_PATH]
(local variable) _error_ exe
exe
;
undefined identifier `MAX_PATH`
GetModuleFileNameW(null, exe.
exe.ptr
ptr
, MAX_PATH);
undefined identifier `GetModuleFileNameW`
WCHAR[MAX_PATH + 16]
(local variable) _error_ cmd
cmd
;
undefined identifier `WCHAR`
int
(local variable) int p
p
;
cmd[p++] = '"'; for (int
(local variable) int i
i
= 0; exe[i]; ++i)
cmd[p++] = exe[i]; cmd[p++] = '"'; foreach (
(parameter) immutable(wchar) ch
ch
; " --reader"w)
cmd[p++] = ch; cmd[p] = 0; STARTUPINFOW
(local variable) _error_ si
si
;
undefined identifier `STARTUPINFOW`
si.cb = STARTUPINFOW.sizeof; PROCESS_INFORMATION
(local variable) _error_ pi
pi
;
undefined identifier `PROCESS_INFORMATION`
logEvent("spawn_reader n=%d", n);
undefined identifier `logEvent`
if (!CreateProcessW(null, cmd.
cmd.ptr
ptr
, null, null, TRUE, 0, null, null, &si, &pi))
undefined identifier `CreateProcessW`
return logEvent("error what=CreateProcessW code=%lu", GetLastError());
undefined identifier `logEvent`
CloseHandle(pi.hThread);
undefined identifier `CloseHandle`
CloseHandle(pi.hProcess); // it logs on its own; the pump must keep running
undefined identifier `CloseHandle`
} // --------------------------------------------------------------------------- // COM implementations — IDropTarget (the window side). extern (Windows) HRESULT
app.dtQueryInterface
dtQueryInterface
(void* self, const(GUID)* riid, void** ppv) nothrow
undefined identifier `HRESULT`
undefined identifier `GUID`
{ if (sameGuid(riid, &IID_IUnknown) || sameGuid(riid, &IID_IDropTarget)) { *ppv = self; return S_OK; } *ppv = null; return E_NOINTERFACE; } extern (Windows) ULONG
app.comAddRef
comAddRef
(void* self) nothrow
undefined identifier `ULONG`
{ return cast(ULONG)++(cast(ComObj!
(unresolved type) DropTargetVtbl
DropTargetVtbl
*) self).
(cast(ComObj!DropTargetVtbl*)self).refs
refs
;
} extern (Windows) ULONG
app.comRelease
comRelease
(void* self) nothrow
undefined identifier `ULONG`
{ return cast(ULONG)--(cast(ComObj!
(unresolved type) DropTargetVtbl
DropTargetVtbl
*) self).
(cast(ComObj!DropTargetVtbl*)self).refs
refs
; // static lifetime
} // Probe the offered formats through the foreign IDataObject pointer. void
void app.logDataObjectFormats(void* dataObj) nothrow
logDataObjectFormats
(void*
(parameter) void* dataObj
dataObj
) nothrow
{ auto
(local variable) _error_ obj
obj
= cast(ComObj!
(struct) app.DataObjectVtbl
DataObjectVtbl
*) dataObj;
const
(local variable) const(_error_) sameObject
sameObject
=
(parameter) void* dataObj
dataObj
is &g.
(field) _error_ g.data
data
;
logEvent("dnd_dataobject ptr=%p is_our_source_object=%d", dataObj, sameObject ? 1 : 0);
undefined identifier `logEvent`
(struct) app.FORMATETC
FORMATETC
(local variable) _error_ fe
fe
= FORMATETC(CF_HDROP, null, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
const
(local variable) const(_error_) hrHdrop
hrHdrop
= obj.
(field) _error_ obj.vtbl
vtbl
.QueryGetData(dataObj, &fe);
fe.
fe.cfFormat
cfFormat
= CF_UNICODETEXT;
const
(local variable) const(_error_) hrText
hrText
= obj.
(field) _error_ obj.vtbl
vtbl
.QueryGetData(dataObj, &fe);
logEvent("dnd_querygetdata CF_HDROP=0x%08lx CF_UNICODETEXT=0x%08lx", hrHdrop, hrText);
undefined identifier `logEvent`
void*
(local variable) void* enumPtr
enumPtr
;
if (obj.
(field) _error_ obj.vtbl
vtbl
.EnumFormatEtc(dataObj, DATADIR_GET, &enumPtr) == S_OK &&
(local variable) void* enumPtr
enumPtr
)
undefined identifier `S_OK`
{ auto
(local variable) _error_ en
en
= cast(ComObj!
(struct) app.EnumFmtVtbl
EnumFmtVtbl
*) enumPtr;
template instance `app.ComObj!(EnumFmtVtbl)` error instantiating
char[256]
(local variable) char[256] line
line
= 0;
char[80]
(local variable) char[80] scratch
scratch
;
int
(local variable) int p
p
,
(local variable) int count
count
;
(struct) app.FORMATETC
FORMATETC
(local variable) _error_ got
got
;
ULONG
(local variable) _error_ fetched
fetched
;
undefined identifier `ULONG`
while (en.
(field) _error_ en.vtbl
vtbl
.Next(enumPtr, 1, &got, &fetched) == S_OK && fetched == 1)
undefined identifier `S_OK`
{ const
(local variable) const(_error_) nm
nm
= cfName(got.
got.cfFormat
cfFormat
, scratch.
scratch.ptr
ptr
, scratch.
scratch.length
length
);
(local variable) int p
p
+=
int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogc
snprintf
(
(local variable) char[256] line
line
.
(constant) char* char[256].ptr = &line
ptr
+
(local variable) int p
p
,
(local variable) char[256] line
line
.
(constant) ulong char[256].length = 256LU
length
-
(local variable) int p
p
, "%s%s(tymed=%lu)",
(local variable) int count
count
? ",".
(constant) immutable(char)* ",".ptr = ","
ptr
: "".
(constant) immutable(char)* "".ptr = ""
ptr
, nm, got.
(field) _error_ got.tymed
tymed
);
++
(local variable) int count
count
;
} en.
(field) _error_ en.vtbl
vtbl
.Release(enumPtr);
logEvent("dnd_enter formats=[%s] n=%d", line.
line.ptr
ptr
, count);
undefined identifier `logEvent`
} } extern (Windows) HRESULT
app.dtDragEnter
dtDragEnter
(void* self, void*
(parameter) void* dataObj
dataObj
, DWORD keys,
undefined identifier `HRESULT`
undefined identifier `DWORD`
(struct) app.POINTL
POINTL
(parameter) POINTL pt
pt
, DWORD*
(parameter) DWORD* effect
effect
) nothrow
undefined identifier `DWORD`
{ logEvent("dnd_enter keys=0x%lx pt=%ld,%ld effects_offered=0x%lx", keys, pt.
pt.x
x
, pt.
pt.y
y
, *effect);
logDataObjectFormats(dataObj); *effect = DROPEFFECT_COPY; // accept: we want a copy logEvent("dnd_enter_reply effect=DROPEFFECT_COPY"); return S_OK; } extern (Windows) HRESULT
app.dtDragOver
dtDragOver
(void* self, DWORD keys,
(struct) app.POINTL
POINTL
(parameter) POINTL pt
pt
, DWORD*
(parameter) DWORD* effect
effect
) nothrow
undefined identifier `HRESULT`
undefined identifier `DWORD`
undefined identifier `DWORD`
{ static __gshared int
int overCount
overCount
;
if (++overCount <= 3) logEvent("dnd_over n=%d pt=%ld,%ld effect_in=0x%lx", overCount, pt.
pt.x
x
, pt.
pt.y
y
, *effect);
*effect = DROPEFFECT_COPY; return S_OK; } extern (Windows) HRESULT
app.dtDragLeave
dtDragLeave
(void* self) nothrow
undefined identifier `HRESULT`
{ logEvent("dnd_leave"); return S_OK; } extern (Windows) HRESULT
app.dtDrop
dtDrop
(void* self, void*
(parameter) void* dataObj
dataObj
, DWORD keys,
undefined identifier `HRESULT`
undefined identifier `DWORD`
(struct) app.POINTL
POINTL
(parameter) POINTL pt
pt
, DWORD*
(parameter) DWORD* effect
effect
) nothrow
undefined identifier `DWORD`
{ logEvent("dnd_drop pt=%ld,%ld effects_in=0x%lx", pt.
pt.x
x
, pt.
pt.y
y
, *effect);
auto
_error_ obj
obj
= cast(ComObj!
(unresolved type) DataObjectVtbl
DataObjectVtbl
*) dataObj;
(unresolved type) FORMATETC
FORMATETC
_error_ fe
fe
= FORMATETC(CF_HDROP, null, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
(unresolved type) STGMEDIUM
STGMEDIUM
_error_ med
med
;
const
_error_ hr
hr
= obj.
obj.vtbl
vtbl
.
obj.vtbl.GetData
GetData
(dataObj, &fe, &med);
if (hr == S_OK && med.
med.tymed
tymed
== TYMED_HGLOBAL)
{ const
_error_ bytes
bytes
= GlobalSize(med.
med.hGlobal
hGlobal
);
auto
_error_ drop
drop
= cast(
(unresolved type) DROPFILES
DROPFILES
*) GlobalLock(med.
med.hGlobal
hGlobal
);
const
_error_ nFiles
nFiles
= DragQueryFileW(med.
med.hGlobal
hGlobal
, 0xFFFFFFFF, null, 0);
WCHAR[MAX_PATH]
_error_ path
path
;
const
_error_ len
len
= DragQueryFileW(med.
med.hGlobal
hGlobal
, 0, path.
path.ptr
ptr
, MAX_PATH);
char[MAX_PATH]
_error_ ascii
ascii
;
int
_error_ p
p
;
foreach (
(parameter) i
i
; 0 .. len)
ascii[p++] = path[i] < 0x80 ? cast(char) path[i] : '?'; ascii[p] = 0; logEvent("dnd_drop fmt=CF_HDROP bytes=%zu files=%u fwide=%d file0=%s", bytes, nFiles, drop.
drop.fWide
fWide
, ascii.
ascii.ptr
ptr
);
GlobalUnlock(med.
med.hGlobal
hGlobal
);
ReleaseStgMedium(&med); } else logEvent("dnd_drop getdata_hr=0x%08lx tymed=%lu", hr, med.
med.tymed
tymed
);
*effect = DROPEFFECT_COPY; return S_OK; } // --------------------------------------------------------------------------- // IDropSource — drives the in-process drag to a programmatic drop. extern (Windows) HRESULT
app.dsQueryInterface
dsQueryInterface
(void* self, const(GUID)* riid, void** ppv) nothrow
undefined identifier `HRESULT`
undefined identifier `GUID`
{ if (sameGuid(riid, &IID_IUnknown) || sameGuid(riid, &IID_IDropSource)) { *ppv = self; return S_OK; } *ppv = null; return E_NOINTERFACE; } extern (Windows) HRESULT
app.dsQueryContinueDrag
dsQueryContinueDrag
(void* self, BOOL esc, DWORD keys) nothrow
undefined identifier `HRESULT`
undefined identifier `BOOL`
undefined identifier `DWORD`
{ auto
_error_ src
src
= cast(ComObj!
(unresolved type) DropSourceVtbl
DropSourceVtbl
*) self;
const
_error_ n
n
= ++src.
src.cursor
cursor
;
if (n <= 3 || n == 6) logEvent("dnd_source query_continue n=%u esc=%d keys=0x%lx", n, esc, keys); if (esc) return DRAGDROP_S_CANCEL; // No real mouse button is involved: after a few iterations, drop. return n >= 6 ? DRAGDROP_S_DROP : S_OK; } extern (Windows) HRESULT
app.dsGiveFeedback
dsGiveFeedback
(void* self, DWORD
(parameter) DWORD effect
effect
) nothrow
undefined identifier `HRESULT`
undefined identifier `DWORD`
{ static __gshared int
int n
n
;
if (++n <= 2) logEvent("dnd_source give_feedback n=%d effect=0x%lx", n, effect); return DRAGDROP_S_USEDEFAULTCURSORS; } // --------------------------------------------------------------------------- // IDataObject — offers exactly one format: CF_HDROP as HGLOBAL. extern (Windows) HRESULT
app.doQueryInterface
doQueryInterface
(void* self, const(GUID)* riid, void** ppv) nothrow
undefined identifier `HRESULT`
undefined identifier `GUID`
{ if (sameGuid(riid, &IID_IUnknown) || sameGuid(riid, &IID_IDataObject)) { *ppv = self; return S_OK; } *ppv = null; return E_NOINTERFACE; } bool
app.isOurFormat
isOurFormat
(const(
(struct) app.FORMATETC
FORMATETC
)*
(parameter) const(FORMATETC)* fe
fe
) nothrow
{ return fe.
fe.cfFormat
cfFormat
== CF_HDROP && (fe.
fe.tymed
tymed
& TYMED_HGLOBAL) != 0
&& fe.
fe.dwAspect
dwAspect
== DVASPECT_CONTENT;
} extern (Windows) HRESULT
app.doGetData
doGetData
(void* self,
(struct) app.FORMATETC
FORMATETC
*
(parameter) FORMATETC* fe
fe
,
(struct) app.STGMEDIUM
STGMEDIUM
*
(parameter) STGMEDIUM* med
med
) nothrow
undefined identifier `HRESULT`
{ logEvent("dnd_request fmt=0x%04x tymed=%lu aspect=%lu", fe.
fe.cfFormat
cfFormat
, fe.
fe.tymed
tymed
, fe.
fe.dwAspect
dwAspect
);
if (!isOurFormat(fe)) return DV_E_FORMATETC; // Hand out a fresh copy; the callee owns and frees it (pUnkForRelease=null). const
_error_ size
size
= GlobalSize(g.
g.hDropFiles
hDropFiles
);
HANDLE
_error_ dup
dup
= GlobalAlloc(GMEM_MOVEABLE, size);
memcpy(GlobalLock(dup), GlobalLock(g.
g.hDropFiles
hDropFiles
), size);
GlobalUnlock(dup); GlobalUnlock(g.
g.hDropFiles
hDropFiles
);
med.
med.tymed
tymed
= TYMED_HGLOBAL;
med.
med.hGlobal
hGlobal
= dup;
med.
med.pUnkForRelease
pUnkForRelease
= null;
logEvent("dnd_send fmt=CF_HDROP bytes=%zu", size); return S_OK; } extern (Windows) HRESULT
app.doGetDataHere
doGetDataHere
(void* self,
(struct) app.FORMATETC
FORMATETC
*
(parameter) FORMATETC* fe
fe
,
(struct) app.STGMEDIUM
STGMEDIUM
*
(parameter) STGMEDIUM* med
med
) nothrow
undefined identifier `HRESULT`
{ return E_NOTIMPL; } extern (Windows) HRESULT
app.doQueryGetData
doQueryGetData
(void* self,
(struct) app.FORMATETC
FORMATETC
*
(parameter) FORMATETC* fe
fe
) nothrow
undefined identifier `HRESULT`
{ return isOurFormat(fe) ? S_OK : DV_E_FORMATETC; } extern (Windows) HRESULT
app.doGetCanonical
doGetCanonical
(void* self,
(struct) app.FORMATETC
FORMATETC
*
(parameter) FORMATETC* a
a
,
(struct) app.FORMATETC
FORMATETC
* b) nothrow
undefined identifier `HRESULT`
{ *b = *a; b.
b.ptd
ptd
= null;
return E_NOTIMPL; } extern (Windows) HRESULT
app.doSetData
doSetData
(void* self,
(struct) app.FORMATETC
FORMATETC
*
(parameter) FORMATETC* fe
fe
,
(struct) app.STGMEDIUM
STGMEDIUM
*
(parameter) STGMEDIUM* med
med
, BOOL rel) nothrow
undefined identifier `HRESULT`
undefined identifier `BOOL`
{ return E_NOTIMPL; } extern (Windows) HRESULT
app.doEnumFormatEtc
doEnumFormatEtc
(void* self, DWORD dir, void** ppEnum) nothrow
undefined identifier `HRESULT`
undefined identifier `DWORD`
{ if (dir != DATADIR_GET) { *ppEnum = null; return E_NOTIMPL; } __gshared ComObj!
(unresolved type) EnumFmtVtbl
EnumFmtVtbl
_error_ en
en
; // single static enumerator (demo-grade)
en.
en.vtbl
vtbl
= &gEnumVtbl;
en.
en.refs
refs
= 1;
en.
en.cursor
cursor
= 0;
*ppEnum = &en; return S_OK; } extern (Windows) HRESULT
app.doDAdvise
doDAdvise
(void* self,
(struct) app.FORMATETC
FORMATETC
*
(parameter) FORMATETC* fe
fe
, DWORD
(parameter) DWORD f
f
, void* sink, DWORD* conn) nothrow
undefined identifier `HRESULT`
undefined identifier `DWORD`
undefined identifier `DWORD`
{ return OLE_E_ADVISENOTSUPPORTED; } extern (Windows) HRESULT
app.doDUnadvise
doDUnadvise
(void* self, DWORD conn) nothrow
undefined identifier `HRESULT`
undefined identifier `DWORD`
{ return OLE_E_ADVISENOTSUPPORTED; } extern (Windows) HRESULT
app.doEnumDAdvise
doEnumDAdvise
(void* self, void** ppEnum) nothrow
undefined identifier `HRESULT`
{ *ppEnum = null; return OLE_E_ADVISENOTSUPPORTED; } // IEnumFORMATETC over the one-format list. extern (Windows) HRESULT
app.efQueryInterface
efQueryInterface
(void* self, const(GUID)* riid, void** ppv) nothrow
undefined identifier `HRESULT`
undefined identifier `GUID`
{ if (sameGuid(riid, &IID_IUnknown) || sameGuid(riid, &IID_IEnumFORMATETC)) { *ppv = self; return S_OK; } *ppv = null; return E_NOINTERFACE; } extern (Windows) HRESULT
app.efNext
efNext
(void* self, ULONG celt,
(struct) app.FORMATETC
FORMATETC
* rgelt, ULONG*
(parameter) ULONG* fetched
fetched
) nothrow
undefined identifier `HRESULT`
undefined identifier `ULONG`
undefined identifier `ULONG`
{ auto
_error_ en
en
= cast(ComObj!
(unresolved type) EnumFmtVtbl
EnumFmtVtbl
*) self;
ULONG
_error_ got
got
= 0;
if (celt >= 1 && en.
en.cursor
cursor
== 0)
{ rgelt[0] = FORMATETC(CF_HDROP, null, DVASPECT_CONTENT, -1, TYMED_HGLOBAL); en.
en.cursor
cursor
= 1;
got = 1; } if (fetched) *fetched = got; return got == celt ? S_OK : S_FALSE; } extern (Windows) HRESULT
app.efSkip
efSkip
(void* self, ULONG celt) nothrow
undefined identifier `HRESULT`
undefined identifier `ULONG`
{ auto
_error_ en
en
= cast(ComObj!
(unresolved type) EnumFmtVtbl
EnumFmtVtbl
*) self;
en.
en.cursor
cursor
+= celt;
return en.
en.cursor
cursor
<= 1 ? S_OK : S_FALSE;
} extern (Windows) HRESULT
app.efReset
efReset
(void* self) nothrow
undefined identifier `HRESULT`
{ (cast(ComObj!
(unresolved type) EnumFmtVtbl
EnumFmtVtbl
*) self).
(cast(ComObj!EnumFmtVtbl*)self).cursor
cursor
= 0;
return S_OK; } extern (Windows) HRESULT
app.efClone
efClone
(void* self, void** ppv) nothrow
undefined identifier `HRESULT`
{ *ppv = null; return E_NOTIMPL; } void
void app.initVtbls() nothrow
initVtbls
() nothrow
{
(__gshared global) app.DropTargetVtbl app.gTargetVtbl
gTargetVtbl
= DropTargetVtbl(&dtQueryInterface, &comAddRef, &comRelease,
&dtDragEnter, &dtDragOver, &dtDragLeave, &dtDrop);
(__gshared global) app.DropSourceVtbl app.gSourceVtbl
gSourceVtbl
= DropSourceVtbl(&dsQueryInterface, &comAddRef, &comRelease,
&dsQueryContinueDrag, &dsGiveFeedback);
(__gshared global) app.DataObjectVtbl app.gDataVtbl
gDataVtbl
= DataObjectVtbl(&doQueryInterface, &comAddRef, &comRelease,
&doGetData, &doGetDataHere, &doQueryGetData, &doGetCanonical, &doSetData, &doEnumFormatEtc, &doDAdvise, &doDUnadvise, &doEnumDAdvise);
(__gshared global) app.EnumFmtVtbl app.gEnumVtbl
gEnumVtbl
= EnumFmtVtbl(&efQueryInterface, &comAddRef, &comRelease,
&efNext, &efSkip, &efReset, &efClone); g.
(field) _error_ g.target
target
.
g.target.vtbl
vtbl
= &gTargetVtbl;
g.
(field) _error_ g.source
source
.
g.source.vtbl
vtbl
= &gSourceVtbl;
g.
(field) _error_ g.data
data
.
g.data.vtbl
vtbl
= &gDataVtbl;
} // --------------------------------------------------------------------------- // Phase 7: the in-process drag — DoDragDrop onto our own registered target. // DoDragDrop's modal loop is driven by input events; headless there are none, // so a helper thread jiggles the (wineserver-virtual) cursor to keep the loop // calling QueryContinueDrag. extern (Windows) uint
uint app.jiggleThread(void* arg) nothrow
jiggleThread
(void*
(parameter) void* arg
arg
) nothrow
{ POINT
(local variable) _error_ p
p
;
undefined identifier `POINT`, did you mean struct `POINTL`?
GetCursorPos(&p);
undefined identifier `GetCursorPos`
foreach (
(local variable) int i
i
; 0 .. 150)
{ SetCursorPos(p.
p.x
x
+ (i & 1), p.
p.y
y
);
undefined identifier `SetCursorPos`
Sleep(20);
undefined identifier `Sleep`
if (g.
(field) _error_ g.dndDone
dndDone
)
break; } return 0; } void
void app.buildDropFiles() nothrow
buildDropFiles
() nothrow
{ // A real witness file in the prefix's temp dir. WCHAR[MAX_PATH]
(local variable) _error_ tmp
tmp
;
undefined identifier `MAX_PATH`
GetTempPathW(MAX_PATH, tmp.
tmp.ptr
ptr
);
undefined identifier `GetTempPathW`
int
(local variable) int p
p
;
while (tmp[p]) ++
(local variable) int p
p
;
foreach (
(parameter) ulong i
i
,
(parameter) immutable(wchar) ch
ch
; "wsi-f16-drop.txt\0"w)
tmp[p + i] = ch; HANDLE
(local variable) _error_ f
f
= CreateFileW(tmp.
tmp.ptr
ptr
, GENERIC_WRITE, 0, null, CREATE_ALWAYS,
undefined identifier `HANDLE`
undefined identifier `CreateFileW`
FILE_ATTRIBUTE_NORMAL, null); DWORD
(local variable) _error_ wrote
wrote
;
undefined identifier `DWORD`
WriteFile(f, "wsi f16 drop payload\n".
"wsi f16 drop payload\n".ptr
ptr
, 21, &wrote, null);
undefined identifier `WriteFile`
CloseHandle(f);
undefined identifier `CloseHandle`
g.
(field) _error_ g.dropPath
dropPath
[] = tmp[];
int
(local variable) int pathLen
pathLen
;
while (tmp[pathLen]) ++
(local variable) int pathLen
pathLen
;
const
(local variable) const(_error_) bytes
bytes
= DROPFILES.sizeof + (
(local variable) int pathLen
pathLen
+ 2) * 2; // path + double NUL
g.
g.hDropFiles
hDropFiles
= GlobalAlloc(GMEM_MOVEABLE, bytes);
auto
(local variable) _error_ drop
drop
= cast(
(struct) app.DROPFILES
DROPFILES
*) GlobalLock(g.
g.hDropFiles
hDropFiles
);
*drop = DROPFILES.init; drop.
drop.pFiles
pFiles
= DROPFILES.sizeof;
drop.
drop.fWide
fWide
= TRUE;
auto
(local variable) _error_ dst
dst
= cast(wchar*)(cast(ubyte*) drop + DROPFILES.sizeof);
void* core.stdc.string.memcpy(return scope void* s1, scope const(void*) s2, ulong n) pure nothrow @nogc
memcpy
(dst, tmp.
(field) _error_ tmp.ptr
ptr
,
(local variable) int pathLen
pathLen
* 2);
dst[pathLen] = 0; dst[pathLen + 1] = 0; // the list terminator GlobalUnlock(g.
g.hDropFiles
hDropFiles
);
undefined identifier `GlobalUnlock`
logEvent("dnd_payload file_created bytes=%zu", bytes);
undefined identifier `logEvent`
} void
void app.phaseDnd() nothrow
phaseDnd
() nothrow
{ logEvent("phase name=dnd");
undefined identifier `logEvent`
void app.buildDropFiles() nothrow
buildDropFiles
();
// Park the virtual cursor over our client area so the OLE loop's // WindowFromPoint hit-test finds the registered IDropTarget. RECT
(local variable) _error_ rc
rc
;
undefined identifier `RECT`
GetWindowRect(g.
g.hwnd
hwnd
, &rc);
undefined identifier `GetWindowRect`
SetCursorPos((rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2);
undefined identifier `SetCursorPos`
logEvent("dnd_source cursor_parked=%ld,%ld", (rc.left + rc.right) / 2,
undefined identifier `logEvent`
(rc.top + rc.bottom) / 2); HANDLE
(local variable) _error_ jig
jig
= CreateThread(null, 0, &jiggleThread, null, 0, null);
undefined identifier `HANDLE`
undefined identifier `CreateThread`
DWORD
(local variable) _error_ effect
effect
= DROPEFFECT_NONE;
undefined identifier `DWORD`
logEvent("dnd_source DoDragDrop_begin ok_effects=COPY|MOVE|LINK");
undefined identifier `logEvent`
const
(local variable) const(_error_) hr
hr
= DoDragDrop(&g.
g.data
data
, &g.
g.source
source
, // hand-rolled COM objects
DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK, &effect); g.
g.dndDone
dndDone
= true;
const(char)*
(local variable) const(char)* hrName
hrName
= hr == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP"
: hr == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" : "other"; logEvent("dnd_source DoDragDrop_returned hr=0x%08lx (%s) effect=0x%lx query_continue_calls=%u",
undefined identifier `logEvent`
hr, hrName, effect, g.
g.source
source
.
g.source.cursor
cursor
);
WaitForSingleObject(jig, 4000);
undefined identifier `WaitForSingleObject`
CloseHandle(jig);
undefined identifier `CloseHandle`
} // --------------------------------------------------------------------------- // WndProc: the clipboard-protocol messages are the finding. extern (Windows) LRESULT
app.wndProc
wndProc
(HWND
(parameter) HWND hwnd
hwnd
, UINT
(parameter) UINT msg
msg
, WPARAM wp, LPARAM lp) nothrow
undefined identifier `LRESULT`
undefined identifier `HWND`
undefined identifier `UINT`
undefined identifier `WPARAM`
undefined identifier `LPARAM`
{ switch (msg) { case WM_RENDERFORMAT: // Demanded NOW: the clipboard is already open for us — just SetClipboardData. ++g.
g.renderDemands
renderDemands
;
logEvent("clip_request msg=WM_RENDERFORMAT fmt=0x%04llx demand_n=%d us_since_delayed_set=%lld", cast(ulong) wp, g.
g.renderDemands
renderDemands
, nowUs() - g.
g.delayedSetAt
delayedSetAt
);
if (wp == CF_UNICODETEXT) { SetClipboardData(CF_UNICODETEXT, makeTextHGlobal(PAYLOAD)); logEvent("clip_send fmt=CF_UNICODETEXT bytes=%zu delayed_render=1", PAYLOAD_BYTES); } return 0; case WM_RENDERALLFORMATS: // Owner is dying with promises outstanding: render everything, with // the open/owner-check ceremony the docs require. ++g.
g.renderAll
renderAll
;
logEvent("clip_request msg=WM_RENDERALLFORMATS"); if (OpenClipboard(hwnd)) { if (GetClipboardOwner() is hwnd) { SetClipboardData(CF_UNICODETEXT, makeTextHGlobal(PAYLOAD)); logEvent("clip_send fmt=CF_UNICODETEXT bytes=%zu render_all=1", PAYLOAD_BYTES); } CloseClipboard(); } return 0; case WM_DESTROYCLIPBOARD: logEvent("ownership_lost msg=WM_DESTROYCLIPBOARD seq=%lu", GetClipboardSequenceNumber()); return 0; case WM_CLIPBOARDUPDATE: logEvent("clip_update msg=WM_CLIPBOARDUPDATE seq=%lu owner=%p", GetClipboardSequenceNumber(), GetClipboardOwner()); return 0; case WM_TIMER: ++g.
g.ticks
ticks
;
if (!g.
g.autoExit
autoExit
)
return 0; switch (g.
g.ticks
ticks
)
{ case 3: g.
g.phase
phase
= 1;
phasePasteStartup(); break; case 12: g.
g.phase
phase
= 2;
phaseCopyImmediate(); break; case 25: g.
g.phase
phase
= 3;
spawnReader(1); break; case 60: g.
g.phase
phase
= 4;
phaseCopyDelayed("copy_delayed_thread_demand"); CloseHandle(CreateThread(null, 0, &demandThread, null, 0, null)); break; case 90: g.
g.phase
phase
= 5;
phaseCopyDelayed("copy_delayed_process_demand"); spawnReader(2); break; case 130: g.
g.phase
phase
= 6;
logEvent("phase name=grab_ownership"); CloseHandle(CreateThread(null, 0, &grabThread, null, 0, null)); break; case 150: g.
g.phase
phase
= 7;
phaseDnd(); break; case 190: g.
g.phase
phase
= 8;
phaseCopyDelayed("copy_delayed_then_destroy"); DestroyWindow(hwnd); // -> WM_RENDERALLFORMATS before WM_DESTROY break; default: break; } return 0; case WM_CHAR: // interactive mode: c=copy v=paste d=dnd if (wp == 'c') phaseCopyImmediate(); else if (wp == 'v') phasePasteStartup(); else if (wp == 'd') phaseDnd(); return 0; case WM_PAINT: PAINTSTRUCT
_error_ ps
ps
;
HDC
_error_ dc
dc
= BeginPaint(hwnd, &ps);
RECT
_error_ rc
rc
;
GetClientRect(hwnd, &rc); FillRect(dc, &rc, cast(HBRUSH)(COLOR_WINDOW + 1)); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: logEvent("msg name=WM_DESTROY render_demands=%d render_all=%d", g.
g.renderDemands
renderDemands
, g.
g.renderAll
renderAll
);
KillTimer(hwnd, 1); RevokeDragDrop(hwnd); PostQuitMessage(0); return 0; default: return DefWindowProcW(hwnd, msg, wp, lp); } } // --------------------------------------------------------------------------- // --reader role: a separate process in the same prefix reading the clipboard. int
int app.readerMain() nothrow
readerMain
() nothrow
{ instrumentInit("f16_win32_reader");
undefined identifier `instrumentInit`
logEvent("reader_start pid=%lu", GetCurrentProcessId());
undefined identifier `logEvent`
if (!openClipboardRetry(null)) { logEvent("error what=OpenClipboard code=%lu", GetLastError());
undefined identifier `logEvent`
return 0; }
void app.logOfferedFormats(const(char)* who) nothrow
logOfferedFormats
("reader");
if (IsClipboardFormatAvailable(CF_UNICODETEXT))
undefined identifier `IsClipboardFormatAvailable`
void app.readUnicodeText(const(char)* who) nothrow
readUnicodeText
("reader"); // a delayed format triggers WM_RENDERFORMAT in the owner
else logEvent("clip_read who=reader fmt=CF_UNICODETEXT result=absent");
undefined identifier `logEvent`
CloseClipboard();
undefined identifier `CloseClipboard`
logEvent("reader_exit code=0");
undefined identifier `logEvent`
return 0; } // --------------------------------------------------------------------------- bool
bool app.wantAutoExit() nothrow
wantAutoExit
() nothrow
{ WCHAR[8]
(local variable) _error_ buf
buf
;
undefined identifier `WCHAR`
const
(local variable) const(_error_) n
n
= GetEnvironmentVariableW("WSI_AUTO_EXIT"w.
"WSI_AUTO_EXIT"w.ptr
ptr
, buf.
buf.ptr
ptr
, buf.
buf.length
length
);
undefined identifier `GetEnvironmentVariableW`
return n >= 1 && n < buf.
(field) _error_ buf.length
length
&& buf[0] == '1';
} int
int D main(string[] args)
main
(
(alias) object.string = string
string
[]
(parameter) string[] args
args
)
{ foreach (
(parameter) string a
a
;
(parameter) string[] args
args
[1 .. $])
if (
(local variable) string a
a
== "--reader")
return
int app.readerMain() nothrow
readerMain
();
instrumentInit("f16_win32");
undefined identifier `instrumentInit`
logEvent("init_start");
undefined identifier `logEvent`
g.
g.autoExit
autoExit
= wantAutoExit();
g.
g.inst
inst
= GetModuleHandleW(null);
SetUnhandledExceptionFilter(cast(LPTOP_LEVEL_EXCEPTION_FILTER)&sehFilter);
undefined identifier `SetUnhandledExceptionFilter`
if (g.
(field) _error_ g.autoExit
autoExit
)
CloseHandle(CreateThread(null, 0, &watchdogProc,
undefined identifier `CloseHandle`
cast(void*) cast(
(unresolved type) size_t
size_t
) 30000, 0, null));
// OLE drag-and-drop REQUIRES OleInitialize (it layers the clipboard/DnD // machinery on top of CoInitialize's STA); a CoInitialize-only thread gets // E_OUTOFMEMORY-flavored failures from RegisterDragDrop. const
(local variable) const(_error_) hrOle
hrOle
= OleInitialize(null);
logEvent("step name=OleInitialize hr=0x%08lx", hrOle);
undefined identifier `logEvent`
void app.initVtbls() nothrow
initVtbls
();
WNDCLASSEXW
(local variable) _error_ wc
wc
;
undefined identifier `WNDCLASSEXW`
wc.cbSize = WNDCLASSEXW.sizeof; wc.lpfnWndProc = &wndProc; wc.hInstance = g.
g.inst
inst
;
wc.lpszClassName = "wsi-f16-class"w.
"wsi-f16-class"w.ptr
ptr
;
wc.hCursor = LoadCursorW(null, IDC_ARROW); wc.hbrBackground = cast(HBRUSH)(COLOR_WINDOW + 1); if (!RegisterClassExW(&wc))
undefined identifier `RegisterClassExW`
logEvent("error what=RegisterClassExW code=%lu", GetLastError());
undefined identifier `logEvent`
g.
g.hwnd
hwnd
= CreateWindowExW(0, "wsi-f16-class"w.
"wsi-f16-class"w.ptr
ptr
, "wsi-f16-clipboard-dnd"w.
"wsi-f16-clipboard-dnd"w.ptr
ptr
,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 480, 320, null, null, g.
g.inst
inst
, null);
if (g.
(field) _error_ g.hwnd
hwnd
is null)
{ logEvent("error what=CreateWindowExW code=%lu", GetLastError());
undefined identifier `logEvent`
return 0; } ShowWindow(g.
g.hwnd
hwnd
, SW_SHOW);
undefined identifier `ShowWindow`
UpdateWindow(g.
g.hwnd
hwnd
);
undefined identifier `UpdateWindow`
logEvent("window_created hwnd=%p", g.
g.hwnd
hwnd
);
undefined identifier `logEvent`
const
(local variable) const(_error_) lst
lst
= AddClipboardFormatListener(g.
g.hwnd
hwnd
);
logEvent("step name=AddClipboardFormatListener ret=%d err=%lu", lst, GetLastError());
undefined identifier `logEvent`
const
(local variable) const(_error_) hrReg
hrReg
= RegisterDragDrop(g.
g.hwnd
hwnd
, &g.
g.target
target
);
logEvent("step name=RegisterDragDrop hr=0x%08lx", hrReg);
undefined identifier `logEvent`
SetTimer(g.
g.hwnd
hwnd
, 1, 16, null);
undefined identifier `SetTimer`
MSG
(local variable) _error_ msg
msg
;
undefined identifier `MSG`
while (GetMessageW(&msg, null, 0, 0) > 0)
undefined identifier `GetMessageW`
{ TranslateMessage(&msg);
undefined identifier `TranslateMessage`
DispatchMessageW(&msg);
undefined identifier `DispatchMessageW`
}
void app.OleUninitialize() nothrow @nogc
OleUninitialize
();
logEvent("exit code=0 render_demands=%d render_all=%d", g.
g.renderDemands
renderDemands
, g.
g.renderAll
renderAll
);
undefined identifier `logEvent`
return 0; }