Add a proof of concept drag-and-drop snooper

This doesn't do anything useful yet, but it will print the names of
files that are being dragged and dropped on the Wine server (using the
OLE API) while the plugin is open.
This commit is contained in:
Robbert van der Helm
2021-07-09 20:49:54 +02:00
parent fc61658ada
commit f33c02f4f7
+107 -8
View File
@@ -16,17 +16,116 @@
#include "xdnd-proxy.h"
void CALLBACK dnd_winevent_callback(HWINEVENTHOOK hWinEventHook,
// FIXME: Remove
#include <iostream>
/**
* The window class name Wine uses for its `DoDragDrop()` tracker window.
*
* https://github.com/wine-mirror/wine/blob/d10887b8f56792ebcca717ccc28a289f7bcaf107/dlls/ole32/ole2.c#L101-L104
*/
static constexpr char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32";
/**
* Part of the struct Wine uses to keep track of the data during an OLE
* drag-and-drop operation. We only really care about the first field that
* contains the actual data.
*
* https://github.com/wine-mirror/wine/blob/d10887b8f56792ebcca717ccc28a289f7bcaf107/dlls/ole32/ole2.c#L54-L73
*/
struct TrackerWindowInfo {
IDataObject* dataObject;
IDropSource* dropSource;
// ... more fields that we don't need
};
void CALLBACK dnd_winevent_callback(HWINEVENTHOOK /*hWinEventHook*/,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD idEventThread,
DWORD dwmsEventTime) {
// TODO: `EVENT_OBJECT_DESTROY` doesn't seem to be implemented by Wine, so
// we can't rely on that.
if (event == EVENT_OBJECT_CREATE && idObject == OBJID_WINDOW) {
// TODO
LONG /*idChild*/,
DWORD /*idEventThread*/,
DWORD /*dwmsEventTime*/) {
// FIXME: Prevent this from being run by multiple processes at the same time
// XXX: `EVENT_OBJECT_DESTROY` doesn't seem to be implemented by Wine, so we
// can't rely on that.
if (!(event == EVENT_OBJECT_CREATE && idObject == OBJID_WINDOW)) {
return;
}
// Wine's drag-and-drop tracker windows always have the same window
// class name, so we can easily identify them
{
std::array<char, 64> window_class_name{0};
GetClassName(hwnd, window_class_name.data(), window_class_name.size());
if (strcmp(window_class_name.data(), OLEDD_DRAGTRACKERCLASS) != 0) {
return;
}
}
// They apaprently use 0 instead of `GWLP_USERDATA` to store the tracker
// data
auto tracker_info =
reinterpret_cast<TrackerWindowInfo*>(GetWindowLongPtr(hwnd, 0));
if (!tracker_info) {
return;
}
IEnumFORMATETC* enumerator = nullptr;
tracker_info->dataObject->EnumFormatEtc(DATADIR_GET, &enumerator);
if (!enumerator) {
return;
}
std::array<FORMATETC, 16> supported_formats;
unsigned int num_formats = 0;
enumerator->Next(supported_formats.size(), supported_formats.data(),
&num_formats);
enumerator->Release();
for (unsigned int format_idx = 0; format_idx < num_formats; format_idx++) {
STGMEDIUM storage{};
if (HRESULT result = tracker_info->dataObject->GetData(
&supported_formats[format_idx], &storage);
result == S_OK) {
switch (storage.tymed) {
case TYMED_HGLOBAL: {
auto drop = static_cast<HDROP>(GlobalLock(storage.hGlobal));
std::array<WCHAR, 1024> file_name{0};
const uint32_t num_files = DragQueryFileW(
drop, 0xFFFFFFFF, file_name.data(), file_name.size());
std::cerr << "Plugin wanted to drag-and-drop " << num_files
<< " files:" << std::endl;
for (uint32_t file_idx = 0; file_idx < num_files;
file_idx++) {
file_name[0] = 0;
DragQueryFileW(drop, file_idx, file_name.data(),
file_name.size());
std::cerr << "- "
<< wine_get_unix_file_name(file_name.data())
<< std::endl;
}
GlobalUnlock(GlobalLock(storage.hGlobal));
} break;
case TYMED_FILE: {
std::cerr << "Plugin wanted to drag-and-drop '"
<< wine_get_unix_file_name(storage.lpszFileName)
<< "'" << std::endl;
} break;
default: {
std::cerr << "Unknown format " << storage.tymed
<< std::endl;
} break;
}
// We won't release the storage, because we're not actually doing
// the Windows drag and drop. We're just snooping in on the data
// that's being dragged.
}
}
}