#include #include #include #include "vector.h" #include "_cgo_export.h" #define STDULONGMETHODIMP STDMETHODIMP_(ULONG) #define DRAGDROP_MAXFILEPATHSIZE 32767 typedef struct { IDropTargetVtbl *_vtbl; long _refCount; uint32_t _handle; HWND _windowHandle; } DragDropHandler; static void screenToClient(HWND windowHandle, POINTL *point) { ScreenToClient(windowHandle, (LPPOINT)point); } static STDMETHODIMP QueryInterface(IDropTarget *this, REFIID riid, LPVOID *ppvObj) { // Always set out parameter to NULL, validating it first. if (!ppvObj) return E_INVALIDARG; *ppvObj = NULL; if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDropTarget)) { // Increment the reference count and return the pointer. *ppvObj = (LPVOID)this; ((DragDropHandler *)this)->_vtbl->AddRef(this); return NOERROR; } return E_NOINTERFACE; } static STDULONGMETHODIMP AddRef(IDropTarget *this) { InterlockedIncrement(&((DragDropHandler *)this)->_refCount); return ((DragDropHandler *)this)->_refCount; } static STDULONGMETHODIMP Release(IDropTarget *this) { // Decrement the object's internal counter. ULONG ulRefCount = InterlockedDecrement(&((DragDropHandler *)this)->_refCount); if (0 == ((DragDropHandler *)this)->_refCount) { GlobalFree(this); } return ulRefCount; } static STDMETHODIMP DragEnter(IDropTarget *this, __RPC__in_opt IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect) { *pdwEffect = DROPEFFECT_COPY; wchar_t filePath[DRAGDROP_MAXFILEPATHSIZE]; FORMATETC format; format.cfFormat = CF_HDROP; format.ptd = NULL; format.dwAspect = DVASPECT_CONTENT; format.lindex = -1; format.tymed = TYMED_HGLOBAL; STGMEDIUM medium; HRESULT result = pDataObj->lpVtbl->GetData(pDataObj, &format, &medium); if (result != S_OK) return E_UNEXPECTED; HDROP drop = (HDROP)GlobalLock(medium.hGlobal); clearFiles(((DragDropHandler *)this)->_handle); UINT numberOfFiles = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0); for (UINT fileIndex = 0; fileIndex < numberOfFiles; fileIndex++) { UINT length = DragQueryFileW(drop, fileIndex, filePath, DRAGDROP_MAXFILEPATHSIZE); addFile(((DragDropHandler *)this)->_handle, &filePath[0], length); } GlobalUnlock(medium.hGlobal); if (medium.pUnkForRelease != NULL) medium.pUnkForRelease->lpVtbl->Release(medium.pUnkForRelease); screenToClient(((DragDropHandler *)this)->_windowHandle, &pt); onDragEnter(((DragDropHandler *)this)->_handle, pt.x, pt.y); return S_OK; } static STDMETHODIMP DragOver(IDropTarget *this, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect) { *pdwEffect = DROPEFFECT_COPY; screenToClient(((DragDropHandler *)this)->_windowHandle, &pt); onDragOver(((DragDropHandler *)this)->_handle, pt.x, pt.y); return S_OK; } static STDMETHODIMP DragLeave(IDropTarget *this) { onDragLeave(((DragDropHandler *)this)->_handle); return S_OK; } static STDMETHODIMP Drop(IDropTarget *this, __RPC__in_opt IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect) { *pdwEffect = DROPEFFECT_COPY; screenToClient(((DragDropHandler *)this)->_windowHandle, &pt); onDrop(((DragDropHandler *)this)->_handle, pt.x, pt.y); return S_OK; } static const IDropTargetVtbl DragDropHandlerVtbl = { QueryInterface, AddRef, Release, DragEnter, DragOver, DragLeave, Drop, }; static DragDropHandler *newDragDropHandler(HWND windowHandle, uint32_t handle) { DragDropHandler *handler = malloc(sizeof(DragDropHandler)); handler->_vtbl = (IDropTargetVtbl *)&DragDropHandlerVtbl; handler->_refCount = 0; handler->_windowHandle = windowHandle; handler->_handle = handle; return handler; } BOOL dragDropInitialized = FALSE; typedef vector(DragDropHandler *) DragDropHandlers; DragDropHandlers handlers; static void VerifyResult(CHAR *component, HRESULT result) { if (result == S_OK) return; printf("%s failed (error code: %d)\n", component, result); } static void initDragDrop(void) { if (dragDropInitialized == TRUE) return; HRESULT result = OleInitialize(NULL); VerifyResult("OleInitialize", result); vector_init(handlers); dragDropInitialized = TRUE; } uint32_t RegisterHandler(void *window) { initDragDrop(); size_t handle = handlers.count + 1; HWND windowHandle = (HWND)window; DragDropHandler *handler = newDragDropHandler(windowHandle, handle); vector_append(DragDropHandler *, handlers, handler); DragAcceptFiles(windowHandle, TRUE); HRESULT result = RegisterDragDrop(windowHandle, (IDropTarget *)handler); VerifyResult("RegisterDragDrop", result); return (uint32_t)handle; }