/*
 * Copyright 2004-2020 Sandboxie Holdings, LLC 
 *
 * This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

//---------------------------------------------------------------------------
// Ole32 -- Clipboard Management
//---------------------------------------------------------------------------


extern "C" {
#include "dll.h"
#include "gui_p.h"
#include "gdi.h"
#include "common/my_version.h"
}

#include <shellapi.h>
#include <shlobj.h>
#include <new>
#include <stdlib.h>


#undef   WITH_PRINT_FORMAT


//---------------------------------------------------------------------------
// Function Pointers
//---------------------------------------------------------------------------


typedef HRESULT (*P_OleSetClipboard)(IDataObject *pDataObject);

typedef HRESULT (*P_ReleaseStgMedium)(STGMEDIUM *pmedium);

typedef HRESULT (*P_RegisterDragDrop)(HWND hwnd, IDropTarget *pDropTarget);

typedef HRESULT (*P_RevokeDragDrop)(HWND hwnd);

typedef void (*P_DragFinish)(HDROP hDrop);

typedef UINT (*P_DragQueryFile)(
    HDROP hDrop, UINT iFile, WCHAR *lpszFile, UINT cch);

typedef LPITEMIDLIST (*P_ILCombine)(
    LPCITEMIDLIST pidl1, LPCITEMIDLIST pid2);

typedef UINT (*P_ILGetSize)(LPCITEMIDLIST pidl);

typedef BOOL (*P_SHGetPathFromIDList)(
    LPCITEMIDLIST pidl, void *pszPath);

typedef HRESULT (*P_SHILCreateFromPath)(
    void *pszPath, LPITEMIDLIST *ppidl, ULONG *rgflnOut);

typedef void (*P_SHFree)(void *pv);


//---------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------


static HRESULT Ole_OleSetClipboard(IDataObject *pDataObject);

static void Ole_ReleaseStgMedium(STGMEDIUM *pmedium);

static HDROP Ole_IsVirtualFile(IDataObject *pDataObject);

static BOOLEAN Ole_WriteStreamToFile(
    IStream *pStream, ULONG StreamSize, WCHAR *Path);

static HRESULT Ole_RegisterDragDrop(HWND hwnd, IDropTarget *pDropTarget);

static HRESULT Ole_RevokeDragDrop(HWND hwnd);

static ULONG Ole_DoDragDrop_2(void *ThreadParam);


//---------------------------------------------------------------------------
// Variables
//---------------------------------------------------------------------------


static ULONG Ole_CF_FileNameA = 0;
static ULONG Ole_CF_FileNameW = 0;
static ULONG Ole_CF_ShellIdList = 0;


//---------------------------------------------------------------------------


static P_OleSetClipboard        __sys_OleSetClipboard           = NULL;
static P_ReleaseStgMedium       __sys_ReleaseStgMedium          = NULL;
static P_RegisterDragDrop       __sys_RegisterDragDrop          = NULL;
static P_RevokeDragDrop         __sys_RevokeDragDrop            = NULL;
static P_DragFinish             __sys_DragFinish                = NULL;

//---------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------


extern "C" _FX BOOLEAN Ole_Init(HMODULE module)
{
    void *OleSetClipboard;
    void *ReleaseStgMedium;
    void *RegisterDragDrop;
    void *RevokeDragDrop;

    // DisableComProxy BEGIN
    if (!SbieApi_QueryConfBool(NULL, L"DisableComProxy", FALSE))
    // DisableComProxy END
    if (! SbieDll_IsOpenCOM()) {

        Com_Init_Ole32(module);

        RegisterDragDrop = GetProcAddress(module, "RegisterDragDrop");
        RevokeDragDrop   = GetProcAddress(module, "RevokeDragDrop");

        if (Gui_RenameClasses) {

            //
            // don't hook drag and drop if using OpenWinClass=*
            // because we have neither our Get/SetWindowLong hooks
            // which maintain Gui_DropTargetProp_Atom, nor the window
            // procedure hook to invoke Ole_DoDragDrop
            //

            SBIEDLL_HOOK(Ole_,RegisterDragDrop);
            SBIEDLL_HOOK(Ole_,RevokeDragDrop);
        }
    }

    ReleaseStgMedium = (P_ReleaseStgMedium)
        GetProcAddress(module, "ReleaseStgMedium");

    SBIEDLL_HOOK(Ole_, ReleaseStgMedium);

    if (Dll_ImageType == DLL_IMAGE_SHELL_EXPLORER) {

        OleSetClipboard = GetProcAddress(module, "OleSetClipboard");

        SBIEDLL_HOOK(Ole_,OleSetClipboard);
    }

    return TRUE;
}


//---------------------------------------------------------------------------
// XEnumFormatEtc Class
//---------------------------------------------------------------------------


class XEnumFormatEtc : public IEnumFORMATETC
{

public:

    STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

    STDMETHOD(Next)(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched);
    STDMETHOD(Skip)(ULONG celt);
    STDMETHOD(Reset)(void);
    STDMETHOD(Clone)(IEnumFORMATETC **ppenum);

    BOOL m_eof;

};


//---------------------------------------------------------------------------
// XEnumFormatEtc::IUnknown::QueryInterface
//---------------------------------------------------------------------------


_FX HRESULT XEnumFormatEtc::QueryInterface(REFIID iid, void **ppvObject)
{
    if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IEnumFORMATETC))
        *ppvObject = this;
    else
        return E_NOINTERFACE;
    ((IUnknown *)(*ppvObject))->AddRef();
    return S_OK;
}


//---------------------------------------------------------------------------
// XEnumFormatEtc::IUnknown::AddRef
//---------------------------------------------------------------------------


_FX ULONG XEnumFormatEtc::AddRef()
{
    return 1;
}


//---------------------------------------------------------------------------
// XEnumFormatEtc::IUnknown::Release
//---------------------------------------------------------------------------


_FX ULONG XEnumFormatEtc::Release()
{
    return 1;
}


//---------------------------------------------------------------------------
// XEnumFormatEtc::IUnknown::Next
//---------------------------------------------------------------------------


_FX HRESULT XEnumFormatEtc::Next(
    ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched)
{
    if (m_eof) {

        if (pceltFetched)
            *pceltFetched = 0;

    } else if (celt >= 1) {

        m_eof = TRUE;
        rgelt->cfFormat = CF_HDROP;
        rgelt->ptd = NULL;
        rgelt->dwAspect = DVASPECT_CONTENT;
        rgelt->lindex = -1;
        rgelt->tymed = TYMED_HGLOBAL;
        if (pceltFetched)
            *pceltFetched = 1;
        if (celt == 1)
            return S_OK;
    }

    return S_FALSE;
}


//---------------------------------------------------------------------------
// XEnumFormatEtc::IUnknown::Skip
//---------------------------------------------------------------------------


_FX HRESULT XEnumFormatEtc::Skip(ULONG celt)
{
    if (m_eof || celt != 1)
        return S_FALSE;
    return S_OK;
}


//---------------------------------------------------------------------------
// XEnumFormatEtc::IUnknown::Reset
//---------------------------------------------------------------------------


_FX HRESULT XEnumFormatEtc::Reset(void)
{
    m_eof = FALSE;
    return S_OK;
}


//---------------------------------------------------------------------------
// XEnumFormatEtc::IUnknown::Clone
//---------------------------------------------------------------------------


_FX HRESULT XEnumFormatEtc::Clone(IEnumFORMATETC **ppenum)
{
    *ppenum = this;
    return S_OK;
}


//---------------------------------------------------------------------------
// XDataObject Class
//---------------------------------------------------------------------------


class XDataObject : public IDataObject
{
public:

    XDataObject(IDataObject *pDataObject, HDROP hDrop);

protected:

    void PrintFormat(const WCHAR *FuncName, const FORMATETC *pFormatetc)
#ifndef WITH_PRINT_FORMAT
    {}
#endif
    ;

    BOOLEAN IsValidFormat(const FORMATETC *pFormatetc);

    HRESULT GetDataCommon(FORMATETC *format, STGMEDIUM *medium);

    HGLOBAL InitFormatHDrop(HGLOBAL hData);

    HANDLE OpenFileFromHDrop(HGLOBAL hData);

    HGLOBAL InitFormatFileNameA(HGLOBAL hData);

    HGLOBAL InitFormatFileNameW(HGLOBAL hData);

    HGLOBAL InitFormatIdList(HGLOBAL hData);


//---------------------------------------------------------------------------
// IUnknown Methods
//---------------------------------------------------------------------------


public:

    STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();


//---------------------------------------------------------------------------
// IDataObject Methods
//---------------------------------------------------------------------------


protected:

    STDMETHOD(DAdvise)(
        FORMATETC *pFormatetc, DWORD advf, IAdviseSink *pAdvSink,
        DWORD *pdwConnection);
    STDMETHOD(DUnadvise)(DWORD dwConnection);
    STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise);
    STDMETHOD(EnumFormatEtc)(
        DWORD dwDirection, IEnumFORMATETC **ppenumFormatetc);
    STDMETHOD(GetCanonicalFormatEtc)(
        FORMATETC *pFormatetcIn, FORMATETC *pFormatetcOut);
    STDMETHOD(GetData)(FORMATETC *pFormatetc, STGMEDIUM *pmedium);
    STDMETHOD(GetDataHere)(FORMATETC *pFormatetc, STGMEDIUM *pmedium);
    STDMETHOD(QueryGetData)(FORMATETC *pFormatetc);
    STDMETHOD(SetData)(
        FORMATETC *pFormatetc, STGMEDIUM *pmedium, BOOL fRelease);


//---------------------------------------------------------------------------
// Variables
//---------------------------------------------------------------------------


protected:

    IDataObject *m_pDataObject;
    HDROP m_hDrop;
    ULONG m_fileCount;
    ULONG m_refCount;

    XEnumFormatEtc m_EnumFormatEtc;

    HMODULE __shell32;
    P_DragQueryFile pDragQueryFileW;
    P_ILCombine pILCombine;
    P_ILGetSize pILGetSize;
    P_SHGetPathFromIDList pSHGetPathFromIDList;
    P_SHILCreateFromPath pSHILCreateFromPath;
    P_SHFree pSHFree;

};


//---------------------------------------------------------------------------
// Constructor
//---------------------------------------------------------------------------


_FX XDataObject::XDataObject(IDataObject *pDataObject, HDROP hDrop)
{
    //
    //
    //

    __shell32 = LoadLibrary(L"shell32.dll");
    if (__shell32) {

        pDragQueryFileW = (P_DragQueryFile)
            GetProcAddress(__shell32, "DragQueryFileW");

        pILCombine = (P_ILCombine)GetProcAddress(__shell32, "ILCombine");

        pILGetSize = (P_ILGetSize)GetProcAddress(__shell32, "ILGetSize");

        pSHGetPathFromIDList = (P_SHGetPathFromIDList)
            GetProcAddress(__shell32, "SHGetPathFromIDListW");

        pSHILCreateFromPath = (P_SHILCreateFromPath)
            GetProcAddress(__shell32, "SHILCreateFromPath");

        pSHFree = (P_SHFree)GetProcAddress(__shell32, "SHFree");

        if (! Ole_CF_FileNameA) {
            Ole_CF_FileNameA =
                __sys_RegisterClipboardFormatW(CFSTR_FILENAMEA);
        }
        if (! Ole_CF_FileNameW) {
            Ole_CF_FileNameW =
                __sys_RegisterClipboardFormatW(CFSTR_FILENAMEW);
        }

        if (! Ole_CF_ShellIdList) {
            Ole_CF_ShellIdList =
                __sys_RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
        }

    } else {

        pDragQueryFileW = NULL;
        pSHGetPathFromIDList = NULL;
        pSHILCreateFromPath = NULL;
    }

    //
    //
    //

    m_pDataObject = pDataObject;
    if (m_pDataObject)
        pDataObject->AddRef();

    m_hDrop = hDrop;
    if (m_hDrop && pDragQueryFileW) {
        m_fileCount = pDragQueryFileW(m_hDrop, 0xFFFFFFFF, NULL, 0);
        if (m_fileCount == -1)
            m_fileCount = 0;
    } else
        m_fileCount = 0;

    m_refCount = 1;
}


//---------------------------------------------------------------------------
// XDataObject::IUnknown::QueryInterface
//---------------------------------------------------------------------------


_FX HRESULT XDataObject::QueryInterface(REFIID iid, void **ppvObject)
{
    if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDataObject))
        *ppvObject = this;
    else
        return E_NOINTERFACE;
    ((IUnknown *)(*ppvObject))->AddRef();
    return S_OK;
}


//---------------------------------------------------------------------------
// XDataObject::IUnknown::AddRef
//---------------------------------------------------------------------------


_FX ULONG XDataObject::AddRef()
{
    return ++m_refCount;
}


//---------------------------------------------------------------------------
// XDataObject::IUnknown::Release
//---------------------------------------------------------------------------


_FX ULONG XDataObject::Release()
{
    --m_refCount;
    if (m_refCount != 0)
        return m_refCount;

    if (m_pDataObject)
        m_pDataObject->Release();

    if (__shell32)
        FreeLibrary(__shell32);

    Dll_Free(this);

    return 0;
}


//---------------------------------------------------------------------------
// IDataObject::DAdvise
//---------------------------------------------------------------------------


_FX HRESULT XDataObject::DAdvise(
    FORMATETC *pFormatetc, DWORD advf, IAdviseSink *pAdvSink,
    DWORD *pdwConnection)
{
    if (! m_pDataObject)
        return OLE_E_ADVISENOTSUPPORTED;
    return m_pDataObject->DAdvise(pFormatetc, advf, pAdvSink, pdwConnection);
}


//---------------------------------------------------------------------------
// IDataObject::DUnadvise
//---------------------------------------------------------------------------


_FX HRESULT XDataObject::DUnadvise(DWORD dwConnection)
{
    if (! m_pDataObject)
        return OLE_E_ADVISENOTSUPPORTED;
    return m_pDataObject->DUnadvise(dwConnection);
}


//---------------------------------------------------------------------------
// IDataObject::EnumDAdvise
//---------------------------------------------------------------------------


_FX HRESULT XDataObject::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)
{
    if (! m_pDataObject)
        return OLE_E_ADVISENOTSUPPORTED;
    return m_pDataObject->EnumDAdvise(ppenumAdvise);
}


//---------------------------------------------------------------------------
// IDataObject::EnumFormatEtc
//---------------------------------------------------------------------------


_FX HRESULT XDataObject::EnumFormatEtc(
    DWORD dwDirection, IEnumFORMATETC **ppenumFormatetc)
{
    if (! m_pDataObject) {
        m_EnumFormatEtc.Reset();
        *ppenumFormatetc = &m_EnumFormatEtc;
        return S_OK;
    }

    return m_pDataObject->EnumFormatEtc(dwDirection, ppenumFormatetc);
}


//---------------------------------------------------------------------------
// IDataObject::GetCanonicalFormatEtc
//---------------------------------------------------------------------------


_FX HRESULT XDataObject::GetCanonicalFormatEtc(
    FORMATETC *pFormatetcIn, FORMATETC *pFormatetcOut)
{
    if (! m_pDataObject) {
        memzero(pFormatetcOut, sizeof(FORMATETC));
        return DATA_S_SAMEFORMATETC;
    }
    return m_pDataObject->GetCanonicalFormatEtc(pFormatetcIn, pFormatetcOut);
}


//---------------------------------------------------------------------------
// IDataObject::GetData
//---------------------------------------------------------------------------


_FX HRESULT XDataObject::GetData(
    FORMATETC *pFormatetc, STGMEDIUM *pmedium)
{
    HRESULT hr;

    PrintFormat(L"GetData", pFormatetc);

    if (m_pDataObject)
        hr = m_pDataObject->GetData(pFormatetc, pmedium);

    else if (! IsValidFormat(pFormatetc))
        hr = DV_E_FORMATETC;
    else if ((pFormatetc->tymed & TYMED_HGLOBAL) == 0)
        hr = DV_E_TYMED;
    else if (pFormatetc->lindex != -1)
        hr = DV_E_LINDEX;
    else if (pFormatetc->dwAspect != DVASPECT_CONTENT)
        hr = DV_E_DVASPECT;
    else {

        SIZE_T hDropLen = GlobalSize(m_hDrop);
        HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, hDropLen);
        if (! hGlobal)
            hr = E_OUTOFMEMORY;
        else {

            void *ptrSrc = GlobalLock(m_hDrop);
            void *ptrDst = GlobalLock(hGlobal);
            memcpy(ptrDst, ptrSrc, hDropLen);
            GlobalUnlock(m_hDrop);
            GlobalUnlock(hGlobal);

            pmedium->tymed = TYMED_HGLOBAL;
            pmedium->hGlobal = hGlobal;
            pmedium->pUnkForRelease = NULL;
            hr = S_OK;
        }
    }

    if (SUCCEEDED(hr))
        hr = GetDataCommon(pFormatetc, pmedium);
    return hr;
}


//---------------------------------------------------------------------------
// IDataObject::GetDataHere
//---------------------------------------------------------------------------


_FX HRESULT XDataObject::GetDataHere(
    FORMATETC *pFormatetc, STGMEDIUM *pmedium)
{
    if (! m_pDataObject)
        return E_FAIL;
    HRESULT hr = m_pDataObject->GetDataHere(pFormatetc, pmedium);
    if (SUCCEEDED(hr))
        hr = GetDataCommon(pFormatetc, pmedium);
    return hr;
}


//---------------------------------------------------------------------------
// IDataObject::QueryGetData
//---------------------------------------------------------------------------


_FX HRESULT XDataObject::QueryGetData(FORMATETC *pFormatetc)
{
    HRESULT hr;

    PrintFormat(L"QueryGetData", pFormatetc);

    if (m_pDataObject)
        return m_pDataObject->QueryGetData(pFormatetc);

    else if (! IsValidFormat(pFormatetc))
        hr = DV_E_FORMATETC;
    else if ((pFormatetc->tymed & TYMED_HGLOBAL) == 0)
        hr = DV_E_TYMED;
    else if (pFormatetc->lindex != -1)
        hr = DV_E_LINDEX;
    else if (pFormatetc->dwAspect != DVASPECT_CONTENT)
        hr = DV_E_DVASPECT;
    else
        hr = S_OK;
    return hr;
}


//---------------------------------------------------------------------------
// IDataObject::SetData
//---------------------------------------------------------------------------


_FX HRESULT XDataObject::SetData(
    FORMATETC *pFormatetc, STGMEDIUM *pmedium, BOOL fRelease)
{
    if (! m_pDataObject)
        return E_NOTIMPL;
    return m_pDataObject->SetData(pFormatetc, pmedium, fRelease);
}


//---------------------------------------------------------------------------
// PrintFormat
//---------------------------------------------------------------------------


#ifdef WITH_PRINT_FORMAT

#include <stdio.h>

_FX void XDataObject::PrintFormat(
    const WCHAR *FuncName, const FORMATETC *pFormatetc)
{
    const ULONG fmt = (ULONG)pFormatetc->cfFormat;
    WCHAR text[128];
    Sbie_snwprintf(text, 128, L"%-32.32s - <", FuncName);
    if (fmt >= 0xC000 && fmt <= 0xFFFF)
        __sys_GetClipboardFormatNameW(fmt, text + 36, 60);
    else
        Sbie_snwprintf(text + 36, 128 - 36, L"%08X", fmt);
    wcscat(text, L">\n");
    OutputDebugString(text);
}


#endif WITH_PRINT_FORMAT


//---------------------------------------------------------------------------
// IsValidFormat
//---------------------------------------------------------------------------


_FX BOOLEAN XDataObject::IsValidFormat(const FORMATETC *pFormatetc)
{
    const UINT fmt = pFormatetc->cfFormat;
    if (m_fileCount) {

        BOOLEAN IsFileNameA = (Ole_CF_FileNameA && fmt == Ole_CF_FileNameA);
        BOOLEAN IsFileNameW = (Ole_CF_FileNameW && fmt == Ole_CF_FileNameW);

        if (IsFileNameA || IsFileNameW) {

            if (m_fileCount == 1)
                return TRUE;
            else
                return FALSE;
        }

        if (fmt == CF_HDROP)
            return TRUE;
    }

    return FALSE;
}


//---------------------------------------------------------------------------
// GetDataCommon
//---------------------------------------------------------------------------


_FX HRESULT XDataObject::GetDataCommon(FORMATETC *format, STGMEDIUM *medium)
{
    HRESULT hr = S_OK;

    if (medium->tymed != TYMED_HGLOBAL)
        return hr;

    HGLOBAL hGlobal = NULL;
    const UINT fmt = format->cfFormat;

    PrintFormat(L"GetDataCommon", format);

    if (fmt == CF_HDROP) {

        hGlobal = InitFormatHDrop(medium->hGlobal);

    } else {

        if (Ole_CF_ShellIdList && fmt == Ole_CF_ShellIdList) {
            hGlobal = InitFormatIdList(medium->hGlobal);
            if (hGlobal == (HGLOBAL)-1) {
                hGlobal = NULL;
                hr = DV_E_FORMATETC;
            }
        }

        else if (Ole_CF_FileNameA && fmt == Ole_CF_FileNameA)
            hGlobal = InitFormatFileNameA(medium->hGlobal);

        else if (Ole_CF_FileNameW && fmt == Ole_CF_FileNameW)
            hGlobal = InitFormatFileNameW(medium->hGlobal);
    }

    if (hGlobal || hr != S_OK) {

        if (medium->pUnkForRelease) {
            medium->pUnkForRelease->Release();
            medium->pUnkForRelease = NULL;
        } else
            GlobalFree(medium->hGlobal);
        medium->hGlobal = hGlobal;
    }

    return hr;
}


//---------------------------------------------------------------------------
// InitFormatHDrop
//---------------------------------------------------------------------------


_FX HGLOBAL XDataObject::InitFormatHDrop(HGLOBAL hData)
{
    if (! pDragQueryFileW)
        return NULL;

    //
    // translate each path in the DROPFILES structure to a full
    // path into the sandbox, if applicable.  this is in case the
    // paste operation is going to be done by a program running
    // outside the sandbox
    //

    HDROP hDrop = (HDROP)hData;
    bool sandboxed = false;

    UINT count = pDragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
    if (count == -1)
        count = 0;

    ULONG DropFiles_len = (count + 8) * MAX_PATH * 2 * sizeof(WCHAR);
    DROPFILES *DropFiles = (DROPFILES *)Dll_Alloc(DropFiles_len);
    memzero(DropFiles, DropFiles_len);
    WCHAR *DropName = (WCHAR *)(DropFiles + 1);
    DropFiles->pFiles = (DWORD)((UCHAR *)DropName - (UCHAR *)DropFiles);
    DropFiles->fWide = TRUE;

    for (UINT i = 0; i < count; ++i) {

        pDragQueryFileW(hDrop, i, DropName, 510);

        HANDLE hFile = CreateFileW(DropName,
            GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS, NULL);
        if (hFile != INVALID_HANDLE_VALUE) {

            BOOLEAN is_copy;
            LONG status = SbieDll_GetHandlePath(hFile, DropName, &is_copy);
            if (status == 0 && is_copy) {
                SbieDll_TranslateNtToDosPath(DropName);
                sandboxed = true;
            } else {
                wmemzero(DropName, 512);
                pDragQueryFileW(hDrop, i, DropName, 510);
            }

            CloseHandle(hFile);
        }

        DropName += wcslen(DropName) + 1;
    }

    *DropName = L'\0';
    ++DropName;

    //
    // if we found any sandboxed files to translate to full paths, then
    // create our own HGLOBAL data object
    //

    if (! sandboxed) {

        hData = NULL;

    } else {

        ULONG_PTR len = (UCHAR *)DropName - (UCHAR *)DropFiles;
        hData = GlobalAlloc(GMEM_MOVEABLE, len);
        if (hData) {
            void *ptr = GlobalLock(hData);
            memcpy(ptr, DropFiles, len);
            GlobalUnlock(hData);
        }
    }

    Dll_Free(DropFiles);
    return hData;
}


//---------------------------------------------------------------------------
// OpenFileFromHDrop
//---------------------------------------------------------------------------


_FX HANDLE XDataObject::OpenFileFromHDrop(HGLOBAL hData)
{
    HANDLE hFile = INVALID_HANDLE_VALUE;

    WCHAR *FileName = (WCHAR *)Dll_Alloc(1024 * sizeof(WCHAR));
    UINT count = pDragQueryFileW((HDROP)hData, 0, FileName, 1000);
    if (count > 0 && count < 1000) {

        FileName[count] = L'\0';
        hFile = CreateFileW(FileName,
            GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS, NULL);
    }

    Dll_Free(FileName);
    return hFile;
}


//---------------------------------------------------------------------------
// InitFormatFileNameA
//---------------------------------------------------------------------------


_FX HGLOBAL XDataObject::InitFormatFileNameA(HGLOBAL hData)
{
    HGLOBAL hDataRet = NULL;
    HANDLE hFile;

    if (m_pDataObject) {

        //
        // hData is CF_FILENAMEA
        //

        char *FileNameA = (char *)GlobalLock(hData);

        hFile = CreateFileA(FileNameA,
            GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS, NULL);

        GlobalUnlock(hData);

    } else {

        //
        // hData is CF_HDROP
        //

        hFile = OpenFileFromHDrop(hData);
    }

    if (hFile != INVALID_HANDLE_VALUE) {

        BOOLEAN is_copy;
        WCHAR *name = (WCHAR *)Dll_Alloc(8192);
        LONG status = SbieDll_GetHandlePath(hFile, name, &is_copy);

        if (! m_pDataObject) {
            // hData was CF_HDROP so we don't have a default CF_FILENAMEA
            // to return.  we need to always create it regardless of is_copy
            is_copy = TRUE;
        }

        if (status == 0 && is_copy) {

            SbieDll_TranslateNtToDosPath(name);

            UNICODE_STRING uni;
            ANSI_STRING ansi;

            RtlInitUnicodeString(&uni, name);
            ansi.Length = 0;
            ansi.MaximumLength = uni.Length / sizeof(WCHAR) + 1;
            hDataRet = GlobalAlloc(GMEM_MOVEABLE, ansi.MaximumLength);
            if (hDataRet) {

                ansi.Buffer = (UCHAR *)GlobalLock(hDataRet);
                RtlUnicodeStringToAnsiString(&ansi, &uni, FALSE);
                GlobalUnlock(hDataRet);
            }
        }

        Dll_Free(name);
        CloseHandle(hFile);
    }

    return hDataRet;
}


//---------------------------------------------------------------------------
// InitFormatFileNameW
//---------------------------------------------------------------------------


_FX HGLOBAL XDataObject::InitFormatFileNameW(HGLOBAL hData)
{
    HGLOBAL hDataRet = NULL;
    HANDLE hFile;

    if (m_pDataObject) {

        //
        // hData is CF_FILENAMEW
        //

        WCHAR *FileNameW = (WCHAR *)GlobalLock(hData);

        hFile = CreateFileW(FileNameW,
            GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS, NULL);

        GlobalUnlock(hData);

    } else {

        //
        // hData is CF_HDROP
        //

        hFile = OpenFileFromHDrop(hData);
    }

    if (hFile != INVALID_HANDLE_VALUE) {

        BOOLEAN is_copy;
        WCHAR *name = (WCHAR *)Dll_Alloc(8192);
        LONG status = SbieDll_GetHandlePath(hFile, name, &is_copy);

        if (! m_pDataObject) {
            // hData was CF_HDROP so we don't have a default CF_FILENAMEW
            // to return.  we need to always create it regardless of is_copy
            is_copy = TRUE;
        }

        if (status == 0 && is_copy) {

            SbieDll_TranslateNtToDosPath(name);

            ULONG len = (wcslen(name) + 1) * sizeof(WCHAR);
            hDataRet = GlobalAlloc(GMEM_MOVEABLE, len);
            if (hDataRet) {

                WCHAR *ptr = (WCHAR *)GlobalLock(hDataRet);
                memcpy(ptr, name, len);
                GlobalUnlock(hDataRet);
            }
        }

        Dll_Free(name);
        CloseHandle(hFile);
    }

    return hDataRet;
}


//---------------------------------------------------------------------------
// InitFormatIdList
//---------------------------------------------------------------------------


_FX HGLOBAL XDataObject::InitFormatIdList(HGLOBAL hData)
{
#define GetPidl(i) (LPCITEMIDLIST)(((LPBYTE)pIdList)+(pIdList)->aoffset[i])

    if (! m_pDataObject) {
        // if we don't have an underlying data object,
        // then we shouldn't return an ID List
        return (HGLOBAL)-1;
    }

    if (        (! pILCombine)  ||  (!  pILGetSize)  ||  (! pSHFree)
            ||  (! pSHILCreateFromPath)  ||  (! pSHGetPathFromIDList))
        return NULL;

    CIDA *pIdList = (CIDA *)GlobalLock(hData);
    if (! pIdList)
        return NULL;

    HRESULT hr;
    HANDLE hFile;
    WCHAR *path = (WCHAR *)Dll_AllocTemp(8192);

    //
    // check how many of the files in the Shell ID List Array
    // have copies in the sandbox.  we will prepare a new array
    // only if all files have a copy in the sandbox
    //

    UINT num_in_box = 0;
    UINT count;
    for (count = 1; count <= pIdList->cidl; ++count) {

        LPCITEMIDLIST pidl = pILCombine(GetPidl(0), GetPidl(count));
        if (pidl) {

            hr = pSHGetPathFromIDList(pidl, path);
            if (SUCCEEDED(hr)) {

                hFile = CreateFileW(path,
                    GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
                    FILE_FLAG_BACKUP_SEMANTICS, NULL);

                if (hFile != INVALID_HANDLE_VALUE) {

                    BOOLEAN is_copy;
                    LONG rc = SbieDll_GetHandlePath(hFile, path, &is_copy);
                    if (rc == 0 && is_copy)
                        ++num_in_box;

                    CloseHandle(hFile);
                }
            }

            pSHFree((void *)pidl);
        }
    }

    //
    // if none of the files are in the sandbox, if all of the files are
    // in the sandbox then we create a new Shell ID List Array
    //
    // if some files are in the sandbox and others outside the sandbox
    // then we can't return a Shell ID List Array and hopefully the
    // caller can fallback on HDROP
    //

    HGLOBAL hDataRet = NULL;

    if (num_in_box == 0)
        goto finish;

    if (num_in_box != pIdList->cidl) {
        hDataRet = (HGLOBAL)-1;
        goto finish;
    }

    //
    // get the pidl for the parent folder in the sandbox
    //

    hr = pSHGetPathFromIDList(GetPidl(0), path);
    if (! SUCCEEDED(hr))
        goto finish;

    hFile = CreateFileW(path,
        GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, NULL,
        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        goto finish;

    BOOLEAN is_copy;
    LONG rc = SbieDll_GetHandlePath(hFile, path, &is_copy);

    CloseHandle(hFile);

    if (rc == STATUS_BAD_INITIAL_PC) {

        //
        // if the parent folder is a root folder, we have to open
        // the file and get rid of the last path component
        //

        LPCITEMIDLIST pidl = pILCombine(GetPidl(0), GetPidl(1));
        if (pidl) {

            hr = pSHGetPathFromIDList(pidl, path);
            if (SUCCEEDED(hr)) {

                hFile = CreateFileW(path,
                    GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, NULL,
                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

                LONG rc2 = SbieDll_GetHandlePath(hFile, path, &is_copy);

                CloseHandle(hFile);

                if (rc2 == 0 && is_copy) {

                    WCHAR *ptr = wcsrchr(path, L'\\');
                    if (ptr) {

                        *ptr = L'\0';
                        rc = 0;
                    }
                }
            }

            pSHFree((void *)pidl);
        }
    }

    if (rc != 0 || (! is_copy))
        goto finish;

    //
    // create a new pidl for the parent folder path in the sandbox
    //

    SbieDll_TranslateNtToDosPath(path);

    LPITEMIDLIST pidl;
    ULONG flags = 0;
    hr = pSHILCreateFromPath(path, &pidl, &flags);
    if (FAILED(hr))
        goto finish;

    //
    // create a new array where the pidl for the parent folder
    // references the corresponding folder in the sandbox
    //

    ULONG new_idlist_len = (pIdList->cidl + 2) * sizeof(UINT)
                         + pILGetSize(pidl);
    for (count = 1; count <= pIdList->cidl; ++count)
        new_idlist_len += pILGetSize(GetPidl(count));

    hDataRet = GlobalAlloc(GMEM_MOVEABLE, new_idlist_len);
    if (hDataRet) {

        UCHAR *ptr0 = (UCHAR *)GlobalLock(hDataRet);
        ((CIDA *)ptr0)->cidl = pIdList->cidl;
        UINT *offsets = ((CIDA *)ptr0)->aoffset;

        UCHAR *ptr = (UCHAR *)&offsets[pIdList->cidl + 1];
        ULONG pidl_len = pILGetSize(pidl);
        memcpy(ptr, pidl, pidl_len);
        offsets[0] = (USHORT)(ULONG_PTR)(ptr - ptr0);
        ptr += pidl_len;

        for (count = 1; count <= pIdList->cidl; ++count) {
            pidl_len = pILGetSize(GetPidl(count));
            memcpy(ptr, pidl, pidl_len);
            offsets[count] = (USHORT)(ULONG_PTR)(ptr - ptr0);
            ptr += pidl_len;
        }

        GlobalUnlock(hDataRet);
    }

    pSHFree(pidl);

finish:

    Dll_Free(path);
    GlobalUnlock(hData);
    return hDataRet;

#undef GetPidl

}


//---------------------------------------------------------------------------
// Ole_ReleaseStgMedium
//---------------------------------------------------------------------------


_FX void Ole_ReleaseStgMedium(STGMEDIUM *pmedium)
{
    if (pmedium != NULL)
    {
        if (((pmedium->tymed == TYMED_ENHMF && pmedium->hEnhMetaFile != 0 && pmedium->pUnkForRelease == 0) ||
            (__sys_GetEnhMetaFileBits != 0 && 0 != __sys_GetEnhMetaFileBits(pmedium->hEnhMetaFile, 0, NULL))) &&
            __sys_DeleteEnhMetaFile != 0)
        {
            __sys_DeleteEnhMetaFile(pmedium->hEnhMetaFile);
            pmedium->tymed = TYMED_NULL;
            return;
        }
        if (((pmedium->tymed == TYMED_GDI && pmedium->hBitmap != 0 && pmedium->pUnkForRelease == 0) ||
            (__sys_GetBitmapBits != 0 && 0 != __sys_GetBitmapBits(pmedium->hBitmap, 0, NULL))) &&
            __sys_DeleteObject != 0)
        {
            __sys_DeleteObject(pmedium->hBitmap);
            pmedium->tymed = TYMED_NULL;
            return;
        }
    }
    ((P_ReleaseStgMedium)__sys_ReleaseStgMedium)(pmedium);
}


//---------------------------------------------------------------------------
// Ole_OleSetClipboard
//---------------------------------------------------------------------------


_FX HRESULT Ole_OleSetClipboard(IDataObject *pDataObject)
{
    if (pDataObject) {

        ULONG len = sizeof(XDataObject);
        void *buf = Dll_Alloc(len);

        XDataObject *xDataObject;

        HDROP hDrop = Ole_IsVirtualFile(pDataObject);
        if (hDrop)
            pDataObject = NULL;

        xDataObject = new (buf) XDataObject(pDataObject, hDrop);
        pDataObject = xDataObject;
    }

    return ((P_OleSetClipboard)__sys_OleSetClipboard)(pDataObject);
}


//---------------------------------------------------------------------------
// Ole_IsVirtualFile
//---------------------------------------------------------------------------

// this code doesn't seem to be necessary and causes tmp files to be created in the sandbox

_FX HDROP Ole_IsVirtualFile(IDataObject *pDataObject)
{
    return NULL;
}

    /*
    //
    // check for the clipboard formats that zip files publish:
    // FileGroupDescriptorW and FileContents
    //

    if (! __sys_ReleaseStgMedium)
        return NULL;

    IEnumFORMATETC *pEnum;
    HRESULT hr = pDataObject->EnumFormatEtc(DATADIR_GET, &pEnum);
    if (FAILED(hr))
        return NULL;

    CLIPFORMAT cfFileDescriptor = 0;
    CLIPFORMAT cfFileContents = 0;
    FORMATETC format;

    pEnum->Reset();
    while (1) {
        ULONG n;
        hr = pEnum->Next(1, &format, &n);
        if (FAILED(hr) || n == 0)
            break;

        WCHAR name[64];
        CLIPFORMAT fmt = format.cfFormat;
        name[0] = L'\0';
        if (fmt >= 0xC000 && fmt <= 0xFFFF) {

            __sys_GetClipboardFormatNameW(fmt, name, 60);

            if (_wcsicmp(name, CFSTR_FILEDESCRIPTORW) == 0)
                cfFileDescriptor = fmt;
            else if (_wcsicmp(name, CFSTR_FILECONTENTS) == 0)
                cfFileContents = fmt;
        }
    }

    pEnum->Release();
    if ((! cfFileDescriptor) || (! cfFileContents))
        return NULL;

    //
    // get temporary output folder:
    // C:\TEMP\SbieTemp\HHHHHHHHLLLLLLLL
    //

    WCHAR *TempPath = (WCHAR *)Dll_Alloc((MAX_PATH + 128) * sizeof(WCHAR));
    wmemzero(TempPath, MAX_PATH + 128);

    GetTempPath(MAX_PATH, TempPath);
    wcscat(TempPath, SBIE L"_Temp\\");
    CreateDirectory(TempPath, NULL);
    WCHAR *TempFile = TempPath + wcslen(TempPath);
    FILETIME ft;
    GetSystemTimeAsFileTime(&ft);
    _itow(ft.dwHighDateTime, TempFile, 16);
    TempFile += wcslen(TempFile);
    _itow(ft.dwLowDateTime, TempFile, 16);
    TempFile += wcslen(TempFile);
    CreateDirectory(TempPath, NULL);
    *TempFile = L'\\';
    ++TempFile;

    //
    // prepare the HDROP / DROPFILES output buffer
    //

    format.cfFormat = cfFileDescriptor;
    format.ptd = NULL;
    format.dwAspect = DVASPECT_CONTENT;
    format.lindex = -1;
    format.tymed = TYMED_HGLOBAL;

    STGMEDIUM mediumFileDescriptor;
    memzero(&mediumFileDescriptor, sizeof(mediumFileDescriptor));
    hr = pDataObject->GetData(&format, &mediumFileDescriptor);

    if (FAILED(hr) || mediumFileDescriptor.tymed != TYMED_HGLOBAL) {
        Dll_Free(TempPath);
        return NULL;
    }

    FILEGROUPDESCRIPTOR *Group =
        (FILEGROUPDESCRIPTOR *)GlobalLock(mediumFileDescriptor.hGlobal);

    ULONG DropFiles_len =
        ((ULONG)Group->cItems) * MAX_PATH * 2 * sizeof(WCHAR);
    DROPFILES *DropFiles = (DROPFILES *)Dll_Alloc(DropFiles_len);
    memzero(DropFiles, DropFiles_len);
    WCHAR *DropName = (WCHAR *)(DropFiles + 1);
    DropFiles->pFiles = (DWORD)((UCHAR *)DropName - (UCHAR *)DropFiles);
    DropFiles->fWide = TRUE;

    //
    // extract each file into the temporary folder
    //

    const WCHAR *ParentDirPtr = NULL;
    ULONG ParentDirLen = 0;

    BOOLEAN AtLeastOne = FALSE;

    for (UINT i = 0; i < Group->cItems; ++i) {

        const WCHAR *cFileName = Group->fgd[i].cFileName;

        //
        // if the item is a directory, we're going to add it as a directory
        // to DROPFILES, and make sure its children are extracted from
        // the virtual container, but are not added to the DROPFILES
        //

        if ((Group->fgd[i].dwFlags & FD_ATTRIBUTES) &&
             (Group->fgd[i].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {

            wcscpy(TempFile, cFileName);
            CreateDirectory(TempPath, NULL);

            //
            // don't add the file explicitly to DROPFILES if its
            // parent directory has already been added
            //

            BOOLEAN add = TRUE;

            if (ParentDirLen &&
                    cFileName[ParentDirLen] == L'\\' &&
                    wcsncmp(cFileName, ParentDirPtr, ParentDirLen) == 0)
                add = FALSE;

            if (add) {

                wcscpy(DropName, TempPath);
                DropName += wcslen(DropName) + 1;

                AtLeastOne = TRUE;

                ParentDirPtr = cFileName;
                ParentDirLen = wcslen(ParentDirPtr);
            }

            continue;
        }

        //
        // otherwise the item is a file, extract only if not too large
        //

        if ((Group->fgd[i].dwFlags & FD_FILESIZE) == 0)
            continue;
        if (Group->fgd[i].nFileSizeHigh)
            continue;
        ULONG FileSize = Group->fgd[i].nFileSizeLow;

        format.cfFormat = cfFileContents;
        format.ptd = NULL;
        format.dwAspect = DVASPECT_CONTENT;
        format.lindex = i;
        format.tymed = TYMED_ISTREAM;

        STGMEDIUM medium;
        memzero(&medium, sizeof(medium));

        hr = pDataObject->GetData(&format, &medium);
        if (FAILED(hr))
            continue;

        //
        //
        //

        BOOLEAN reset = TRUE;

        if (medium.tymed == TYMED_ISTREAM) {

            wcscpy(TempFile, cFileName);
            if (Ole_WriteStreamToFile(medium.pstm, FileSize, TempPath)) {

                //
                // don't add the file explicitly to DROPFILES if its
                // parent directory has already been added
                //

                if (ParentDirLen &&
                        cFileName[ParentDirLen] == L'\\' &&
                        wcsncmp(cFileName, ParentDirPtr, ParentDirLen) == 0) {

                    reset = FALSE;

                } else {

                    wcscpy(DropName, TempPath);
                    DropName += wcslen(DropName) + 1;

                    AtLeastOne = TRUE;
                }
            }
        }

        if (reset) {
            ParentDirPtr = NULL;
            ParentDirLen = 0;
        }

        __sys_ReleaseStgMedium(&medium);
    }

    //
    // create HGLOBAL / HDROP for the DROPFILES buffer
    //

    HGLOBAL hData = NULL;

    if (AtLeastOne) {

        *DropName = L'\0';
        ++DropName;

        ULONG_PTR len = (UCHAR *)DropName - (UCHAR *)DropFiles;
        hData = GlobalAlloc(GMEM_MOVEABLE, len);
        if (hData) {
            void *ptr = GlobalLock(hData);
            memcpy(ptr, DropFiles, len);
            GlobalUnlock(hData);
        }
    }

    GlobalUnlock(mediumFileDescriptor.hGlobal);
    __sys_ReleaseStgMedium(&mediumFileDescriptor);

    Dll_Free(DropFiles);
    Dll_Free(TempPath);
    return (HDROP)hData;
}


//---------------------------------------------------------------------------
// Ole_WriteStreamToFile
//---------------------------------------------------------------------------


_FX BOOLEAN Ole_WriteStreamToFile(
    IStream *pStream, ULONG StreamSize, WCHAR *Path)
{
    HANDLE hFile = CreateFileW(
        Path, GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, NULL,
        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if (hFile == INVALID_HANDLE_VALUE)
        return FALSE;

    BOOLEAN ok = TRUE;
    UCHAR *Buffer = (UCHAR *)Dll_Alloc(16384);
    while (1) {

        ULONG BytesRead = 0;
        pStream->Read(Buffer, 16384, &BytesRead);
        if (! BytesRead)
            break;

        ULONG BytesWritten = 0;
        WriteFile(hFile, Buffer, BytesRead, &BytesWritten, NULL);
        if (BytesWritten != BytesRead) {
            ok = FALSE;
            break;
        }
    }

    Dll_Free(Buffer);
    CloseHandle(hFile);
    return ok;
}

*/

//---------------------------------------------------------------------------
// Ole_RegisterDragDrop
//---------------------------------------------------------------------------


_FX HRESULT Ole_RegisterDragDrop(HWND hwnd, IDropTarget *pDropTarget)
{
    HRESULT hr = __sys_RegisterDragDrop(hwnd, pDropTarget);

    if (SUCCEEDED(hr) && Gui_DropTargetProp_Atom) {

        //
        // our hook on SetProp renamed the DropTarget window properties
        // set by the real RegisterDragDrop, and we enable the
        // WS_EX_ACCEPTFILES style flag so we get the WM_DROPFILES message
        // and turn it into an OLE drop operation in Ole_DoDragDrop
        //

        ULONG exstyle = __sys_GetWindowLongW(hwnd, GWL_EXSTYLE);
        if ((exstyle & WS_EX_ACCEPTFILES) == 0) {

            exstyle |= WS_EX_ACCEPTFILES;
            __sys_SetWindowLongW(hwnd, GWL_EXSTYLE, exstyle);

            __sys_SetPropW(hwnd, (LPCWSTR)Gui_DropTargetProp_Atom, pDropTarget);

            if (! __sys_DragFinish) {

                HMODULE shell32 = LoadLibrary(L"shell32.dll");
                __sys_DragFinish =
                    (P_DragFinish)GetProcAddress(shell32, "DragFinish");
            }

            Gui_SetWindowProc(hwnd, TRUE);
        }
    }

    return hr;
}


//---------------------------------------------------------------------------
// Ole_RevokeDragDrop
//---------------------------------------------------------------------------


_FX HRESULT Ole_RevokeDragDrop(HWND hwnd)
{
    HRESULT hr = __sys_RevokeDragDrop(hwnd);

    if (SUCCEEDED(hr) && Gui_DropTargetProp_Atom) {

        //
        // if we enabled the WS_EX_ACCEPTFILES style in Ole_RegisterDragDrop
        // then we should now clear that style bit
        //

        void *pDropTarget =
                __sys_RemovePropW(hwnd, (LPCWSTR)Gui_DropTargetProp_Atom);
        if (pDropTarget) {

            ULONG exstyle = __sys_GetWindowLongW(hwnd, GWL_EXSTYLE);
            if ((exstyle & WS_EX_ACCEPTFILES) == WS_EX_ACCEPTFILES) {

                exstyle &= ~WS_EX_ACCEPTFILES;
                __sys_SetWindowLongW(hwnd, GWL_EXSTYLE, exstyle);
            }
        }
    }

    return hr;
}


//---------------------------------------------------------------------------
// Ole_DoDragDrop
//---------------------------------------------------------------------------


extern "C" _FX BOOLEAN Ole_DoDragDrop(
    HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    if (! Gui_RenameClasses)
        return FALSE;

    if (wParam == tzuk) {

        Ole_DoDragDrop_2((void *)lParam);
        return TRUE;
    }

    //
    //
    //

    IDropTarget *pDropTarget = NULL;

    if (wParam) {

        pDropTarget = (IDropTarget *)__sys_GetPropW(
                            hWnd, (LPCWSTR)Gui_DropTargetProp_Atom);
    }

    if (! pDropTarget)
        return FALSE;

    ULONG len = sizeof(XDataObject);
    void *buf = Dll_Alloc(len);
    XDataObject *xDataObject =
                        new (buf) XDataObject(NULL, (HDROP)wParam);

    POINT ptx;
    __sys_GetCursorPos(&ptx);
    POINTL pt;
    pt.x = ptx.x;
    pt.y = ptx.y;

    ULONG effect = DROPEFFECT_COPY;
    BOOLEAN disregardEffect = FALSE;

    HRESULT hr = pDropTarget->DragEnter(xDataObject, 0, pt, &effect);
    if (SUCCEEDED(hr)) {

        if (Dll_ImageType == DLL_IMAGE_INTERNET_EXPLORER ||
            Dll_ImageType == DLL_IMAGE_MOZILLA_FIREFOX   ||
            Dll_ImageType == DLL_IMAGE_GOOGLE_CHROME) {

            ULONG_PTR *Args =
                (ULONG_PTR *)Dll_Alloc(sizeof(ULONG_PTR) * 8);
            Args[0] = (ULONG_PTR)Args;
            Args[1] = (ULONG_PTR)hWnd;
            Args[2] = (ULONG_PTR)wParam;
            Args[3] = (ULONG_PTR)pDropTarget;
            Args[4] = (ULONG_PTR)xDataObject;
            Args[5] = pt.x;
            Args[6] = pt.y;
            Args[7] = 1;

            if (QueueUserWorkItem(
                    Ole_DoDragDrop_2, Args, WT_EXECUTELONGFUNCTION))
                return TRUE;

            Dll_Free(Args);
        }

        hr = pDropTarget->DragOver(0, pt, &effect);
        if (effect == 0 && Dll_ImageType == DLL_IMAGE_WINDOWS_MEDIA_PLAYER)
            disregardEffect = TRUE;
    }

    if (SUCCEEDED(hr) && (effect || disregardEffect))
        pDropTarget->Drop(xDataObject, 0, pt, &effect);

    xDataObject->Release();

    __sys_DragFinish((HDROP)wParam);
    return TRUE;
}


//---------------------------------------------------------------------------
// Ole_DoDragDrop_2
//---------------------------------------------------------------------------


_FX ULONG Ole_DoDragDrop_2(void *ThreadArgs)
{
    ULONG_PTR *Args = (ULONG_PTR *)ThreadArgs;
    __try {
        if (*Args != (ULONG_PTR)Args)
            Args = 0;
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        Args = 0;
    }
    if (! Args)
        return 0;

    HWND hWnd = (HWND)Args[1];
    HDROP hDrop = (HDROP)Args[2];
    IDropTarget *pDropTarget = (IDropTarget *)Args[3];
    XDataObject *xDataObject = (XDataObject *)Args[4];
    POINTL pt;
    pt.x = (LONG)Args[5];
    pt.y = (LONG)Args[6];
    ULONG *step = (ULONG *)&Args[7];

    //
    // step 1:  invoked by Ole_DoDragDrop after getting the first
    // WM_DROPFILES message.  record ThreadArgs in a window property,
    // sleep for a short while, and post the second WM_DROPFILES
    //

    if (*step == 1) {

        Sleep(250);
        ++(*step);
        if (__sys_PostMessageW(hWnd, WM_DROPFILES, tzuk, (LPARAM)Args))
            return 0;
    }

    //
    // step 2:  invoked by the second WM_DROPFILES
    //

    if (*step < 10) {

        ULONG effect = DROPEFFECT_COPY;
        HRESULT hr = pDropTarget->DragOver(0, pt, &effect);
        if (SUCCEEDED(hr)) {
            Sleep(100);
            ++(*step);
            if ((*step) & 1) {
                ++Args[6];
                ++Args[7];
            } else {
                --Args[6];
                --Args[7];
            }
            if (__sys_PostMessageW(hWnd, WM_DROPFILES, tzuk, (LPARAM)Args))
                return 0;
        }
    }

    //
    // step 3:  invoked by the third WM_DROPFILES
    //

    if (*step >= 10) {

        ULONG effect = DROPEFFECT_COPY;
        pDropTarget->Drop(xDataObject, 0, pt, &effect);

        xDataObject->Release();
        __sys_DragFinish(hDrop);
    }

    //
    // clean up work data
    //

    Dll_Free(Args);
    return 0;
}


//---------------------------------------------------------------------------
// Ole_XDataObject_From_IDataObject
//---------------------------------------------------------------------------


extern "C" _FX IDataObject *Ole_XDataObject_From_IDataObject(
    IDataObject *pDataObject)
{
    ULONG len = sizeof(XDataObject);
    void *buf = Dll_Alloc(len);
    XDataObject *xDataObject = new (buf) XDataObject(pDataObject, NULL);
    return xDataObject;
}
