text: /*
   *	OLE2 library
   *
   * Copyright 1995 Martin von Loewis
   * Copyright 1999 Francis Beaudet
   * Copyright 1999 Noel Borthwick
   * Copyright 1999, 2000 Marcus Meissner
   * Copyright 2005 Juan Lang
   * Copyright 2011 Adam Martinson for CodeWeavers
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library 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
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
   */
  
  #include <assert.h>
  #include <stdlib.h>
  #include <stdarg.h>
  #include <stdio.h>
  #include <string.h>
  
  #define COBJMACROS
  #define NONAMELESSUNION
  
  #include "windef.h"
  #include "winbase.h"
  #include "winerror.h"
  #include "wingdi.h"
  #include "winuser.h"
  #include "winnls.h"
  #include "winreg.h"
  #include "ole2.h"
  #include "ole2ver.h"
  
  #include "compobj_private.h"
  #include "olestd.h"
  #include "wine/list.h"
  
  #include "wine/debug.h"
  
  WINE_DEFAULT_DEBUG_CHANNEL(ole);
  WINE_DECLARE_DEBUG_CHANNEL(accel);
  
  /******************************************************************************
   * These are static/global variables and internal data structures that the
   * OLE module uses to maintain its state.
   */
  typedef struct tagTrackerWindowInfo
  {
    IDataObject* dataObject;
    IDropSource* dropSource;
    DWORD        dwOKEffect;
    DWORD*       pdwEffect;
    BOOL       trackingDone;
    BOOL         inTrackCall;
    HRESULT      returnValue;
  
    BOOL       escPressed;
    HWND       curTargetHWND;	/* window the mouse is hovering over */
    IDropTarget* curDragTarget;
    POINTL     curMousePos;       /* current position of the mouse in screen coordinates */
    DWORD      dwKeyState;        /* current state of the shift and ctrl keys and the mouse buttons */
  } TrackerWindowInfo;
  
  typedef struct tagOleMenuDescriptor  /* OleMenuDescriptor */
  {
    HWND               hwndFrame;         /* The containers frame window */
    HWND               hwndActiveObject;  /* The active objects window */
    OLEMENUGROUPWIDTHS mgw;               /* OLE menu group widths for the shared menu */
    HMENU              hmenuCombined;     /* The combined menu */
    BOOL               bIsServerItem;     /* True if the currently open popup belongs to the server */
  } OleMenuDescriptor;
  
  typedef struct tagOleMenuHookItem   /* OleMenu hook item in per thread hook list */
  {
    DWORD tid;                /* Thread Id  */
    HANDLE hHeap;             /* Heap this is allocated from */
    HHOOK GetMsg_hHook;       /* message hook for WH_GETMESSAGE */
    HHOOK CallWndProc_hHook;  /* message hook for WH_CALLWNDPROC */
    struct tagOleMenuHookItem *next;
  } OleMenuHookItem;
  
  static OleMenuHookItem *hook_list;
  
  /*
   * This is the lock count on the OLE library. It is controlled by the
   * OLEInitialize/OLEUninitialize methods.
   */
  static LONG OLE_moduleLockCount = 0;
  
  /*
   * Name of our registered window class.
   */
  static const WCHAR OLEDD_DRAGTRACKERCLASS[] =
    {'W','i','n','e','D','r','a','g','D','r','o','p','T','r','a','c','k','e','r','3','2',0};
  
  /*
   * Name of menu descriptor property.
   */
  static const WCHAR prop_olemenuW[] =
    {'P','R','O','P','_','O','L','E','M','e','n','u','D','e','s','c','r','i','p','t','o','r',0};
  
  /* property to store IDropTarget pointer */
  static const WCHAR prop_oledroptarget[] =
    {'O','l','e','D','r','o','p','T','a','r','g','e','t','I','n','t','e','r','f','a','c','e',0};
  
  /* property to store Marshalled IDropTarget pointer */
  static const WCHAR prop_marshalleddroptarget[] =
    {'W','i','n','e','M','a','r','s','h','a','l','l','e','d','D','r','o','p','T','a','r','g','e','t',0};
  
  static const WCHAR emptyW[] = { 0 };
  
  /******************************************************************************
   * These are the prototypes of miscellaneous utility methods
   */
  static void OLEUTL_ReadRegistryDWORDValue(HKEY regKey, DWORD* pdwValue);
  
  /******************************************************************************
   * These are the prototypes of the utility methods used to manage a shared menu
   */
  static void OLEMenu_Initialize(void);
  static void OLEMenu_UnInitialize(void);
  static BOOL OLEMenu_InstallHooks( DWORD tid );
  static BOOL OLEMenu_UnInstallHooks( DWORD tid );
  static OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid );
  static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos );
  static BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor );
  static LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam);
  static LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam);
  
  /******************************************************************************
   * These are the prototypes of the OLE Clipboard initialization methods (in clipboard.c)
   */
  extern void OLEClipbrd_UnInitialize(void);
  extern void OLEClipbrd_Initialize(void);
  
  /******************************************************************************
   * These are the prototypes of the utility methods used for OLE Drag n Drop
   */
  static void OLEDD_Initialize(void);
  static LRESULT WINAPI  OLEDD_DragTrackerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo);
  static DWORD OLEDD_GetButtonState(void);
  
  /******************************************************************************
   *		OleBuildVersion [OLE32.@]
   */
  DWORD WINAPI OleBuildVersion(void)
  {
      TRACE("Returning version %d, build %d.\n", rmm, rup);
      return (rmm<<16)+rup;
  }
  
  /***********************************************************************
   *           OleInitialize       (OLE32.@)
   */
  HRESULT WINAPI DECLSPEC_HOTPATCH OleInitialize(LPVOID reserved)
  {
    HRESULT hr;
  
    TRACE("(%p)\n", reserved);
  
    /*
     * The first duty of the OleInitialize is to initialize the COM libraries.
     */
    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  
    /*
     * If the CoInitializeEx call failed, the OLE libraries can't be
     * initialized.
     */
    if (FAILED(hr))
      return hr;
  
    if (!COM_CurrentInfo()->ole_inits)
      hr = S_OK;
    else
      hr = S_FALSE;
  
    /*
     * Then, it has to initialize the OLE specific modules.
     * This includes:
     *     Clipboard
     *     Drag and Drop
     *     Object linking and Embedding
     *     In-place activation
     */
    if (!COM_CurrentInfo()->ole_inits++ &&
        InterlockedIncrement(&OLE_moduleLockCount) == 1)
    {
      /*
       * Initialize the libraries.
       */
      TRACE("() - Initializing the OLE libraries\n");
  
      /*
       * OLE Clipboard
       */
      OLEClipbrd_Initialize();
  
      /*
       * Drag and Drop
       */
      OLEDD_Initialize();
  
      /*
       * OLE shared menu
       */
      OLEMenu_Initialize();
    }
  
    return hr;
  }
  
  /******************************************************************************
   *		OleUninitialize	[OLE32.@]
   */
  void WINAPI DECLSPEC_HOTPATCH OleUninitialize(void)
  {
    TRACE("()\n");
  
    if (COM_CurrentInfo()->ole_inits == 0)
    {
      WARN("ole_inits is already 0\n");
      return ;
    }
    /*
     * If we hit the bottom of the lock stack, free the libraries.
     */
    if (!--COM_CurrentInfo()->ole_inits && !InterlockedDecrement(&OLE_moduleLockCount))
    {
      /*
       * Actually free the libraries.
       */
      TRACE("() - Freeing the last reference count\n");
  
      /*
       * OLE Clipboard
       */
      OLEClipbrd_UnInitialize();
  
      /*
       * OLE shared menu
       */
      OLEMenu_UnInitialize();
    }
  
    /*
     * Then, uninitialize the COM libraries.
     */
    CoUninitialize();
  }
  
  /******************************************************************************
   *		OleInitializeWOW	[OLE32.@]
   */
  HRESULT WINAPI OleInitializeWOW(DWORD x, DWORD y) {
          FIXME("(0x%08x, 0x%08x),stub!\n",x, y);
          return 0;
  }
  
  /*************************************************************
   *           get_droptarget_handle
   *
   * Retrieve a handle to the map containing the marshalled IDropTarget.
   * This handle belongs to the process that called RegisterDragDrop.
   * See get_droptarget_local_handle().
   */
  static inline HANDLE get_droptarget_handle(HWND hwnd)
  {
      return GetPropW(hwnd, prop_marshalleddroptarget);
  }
  
  /*************************************************************
   *           is_droptarget
   *
   * Is the window a droptarget.
   */
  static inline BOOL is_droptarget(HWND hwnd)
  {
      return get_droptarget_handle(hwnd) != 0;
  }
  
  /*************************************************************
   *           get_droptarget_local_handle
   *
   * Retrieve a handle to the map containing the marshalled IDropTarget.
   * The handle should be closed when finished with.
   */
  static HANDLE get_droptarget_local_handle(HWND hwnd)
  {
      HANDLE handle, local_handle = 0;
  
      handle = get_droptarget_handle(hwnd);
  
      if(handle)
      {
          DWORD pid;
          HANDLE process;
  
          GetWindowThreadProcessId(hwnd, &pid);
          process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
          if(process)
          {
              DuplicateHandle(process, handle, GetCurrentProcess(), &local_handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
              CloseHandle(process);
          }
      }
      return local_handle;
  }
  
  /***********************************************************************
   *     create_map_from_stream
   *
   * Helper for RegisterDragDrop.  Creates a file mapping object
   * with the contents of the provided stream.  The stream must
   * be a global memory backed stream.
   */
  static HRESULT create_map_from_stream(IStream *stream, HANDLE *map)
  {
      HGLOBAL hmem;
      DWORD size;
      HRESULT hr;
      void *data;
  
      hr = GetHGlobalFromStream(stream, &hmem);
      if(FAILED(hr)) return hr;
  
      size = GlobalSize(hmem);
      *map = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, NULL);
      if(!*map) return E_OUTOFMEMORY;
  
      data = MapViewOfFile(*map, FILE_MAP_WRITE, 0, 0, size);
      memcpy(data, GlobalLock(hmem), size);
      GlobalUnlock(hmem);
      UnmapViewOfFile(data);
      return S_OK;
  }
  
  /***********************************************************************
   *     create_stream_from_map
   *
   * Creates a stream from the provided map.
   */
  static HRESULT create_stream_from_map(HANDLE map, IStream **stream)
  {
      HRESULT hr = E_OUTOFMEMORY;
      HGLOBAL hmem;
      void *data;
      MEMORY_BASIC_INFORMATION info;
  
      data = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
      if(!data) return hr;
  
      VirtualQuery(data, &info, sizeof(info));
      TRACE("size %d\n", (int)info.RegionSize);
  
      hmem = GlobalAlloc(GMEM_MOVEABLE, info.RegionSize);
      if(hmem)
      {
          memcpy(GlobalLock(hmem), data, info.RegionSize);
          GlobalUnlock(hmem);
          hr = CreateStreamOnHGlobal(hmem, TRUE, stream);
      }
      UnmapViewOfFile(data);
      return hr;
  }
  
  /* This is to work around apps which break COM rules by not implementing
   * IDropTarget::QueryInterface().  Windows doesn't expose this because it
   * doesn't call CoMarshallInterface() in RegisterDragDrop().
   * The wrapper is only used internally, and only exists for the life of
   * the marshal.  We don't want to hold a ref on the app provided target
   * as some apps destroy this prior to CoUninitialize without calling
   * RevokeDragDrop.  The only (long-term) ref is held by the window prop. */
  typedef struct {
      IDropTarget IDropTarget_iface;
      HWND hwnd;
      LONG refs;
  } DropTargetWrapper;
  
  static inline DropTargetWrapper* impl_from_IDropTarget(IDropTarget* iface)
  {
      return CONTAINING_RECORD(iface, DropTargetWrapper, IDropTarget_iface);
  }
  
  static HRESULT WINAPI DropTargetWrapper_QueryInterface(IDropTarget* iface,
                                                         REFIID riid,
                                                         void** ppvObject)
  {
      DropTargetWrapper* This = impl_from_IDropTarget(iface);
      if (IsEqualIID(riid, &IID_IUnknown) ||
          IsEqualIID(riid, &IID_IDropTarget))
      {
          IDropTarget_AddRef(&This->IDropTarget_iface);
          *ppvObject = &This->IDropTarget_iface;
          return S_OK;
      }
      *ppvObject = NULL;
      return E_NOINTERFACE;
  }
  
  static ULONG WINAPI DropTargetWrapper_AddRef(IDropTarget* iface)
  {
      DropTargetWrapper* This = impl_from_IDropTarget(iface);
      return InterlockedIncrement(&This->refs);
  }
  
  static ULONG WINAPI DropTargetWrapper_Release(IDropTarget* iface)
  {
      DropTargetWrapper* This = impl_from_IDropTarget(iface);
      ULONG refs = InterlockedDecrement(&This->refs);
      if (!refs) HeapFree(GetProcessHeap(), 0, This);
      return refs;
  }
  
  static inline HRESULT get_target_from_wrapper( IDropTarget *wrapper, IDropTarget **target )
  {
      DropTargetWrapper* This = impl_from_IDropTarget( wrapper );
      *target = GetPropW( This->hwnd, prop_oledroptarget );
      if (!*target) return DRAGDROP_E_NOTREGISTERED;
      IDropTarget_AddRef( *target );
      return S_OK;
  }
  
  static HRESULT WINAPI DropTargetWrapper_DragEnter(IDropTarget* iface,
                                                    IDataObject* pDataObj,
                                                    DWORD grfKeyState,
                                                    POINTL pt,
                                                    DWORD* pdwEffect)
  {
      IDropTarget *target;
      HRESULT r = get_target_from_wrapper( iface, &target );
  
      if (SUCCEEDED( r ))
      {
          r = IDropTarget_DragEnter( target, pDataObj, grfKeyState, pt, pdwEffect );
          IDropTarget_Release( target );
      }
      return r;
  }
  
  static HRESULT WINAPI DropTargetWrapper_DragOver(IDropTarget* iface,
                                                   DWORD grfKeyState,
                                                   POINTL pt,
                                                   DWORD* pdwEffect)
  {
      IDropTarget *target;
      HRESULT r = get_target_from_wrapper( iface, &target );
  
      if (SUCCEEDED( r ))
      {
          r = IDropTarget_DragOver( target, grfKeyState, pt, pdwEffect );
          IDropTarget_Release( target );
      }
      return r;
  }
  
  static HRESULT WINAPI DropTargetWrapper_DragLeave(IDropTarget* iface)
  {
      IDropTarget *target;
      HRESULT r = get_target_from_wrapper( iface, &target );
  
      if (SUCCEEDED( r ))
      {
          r = IDropTarget_DragLeave( target );
          IDropTarget_Release( target );
      }
      return r;
  }
  
  static HRESULT WINAPI DropTargetWrapper_Drop(IDropTarget* iface,
                                               IDataObject* pDataObj,
                                               DWORD grfKeyState,
                                               POINTL pt,
                                               DWORD* pdwEffect)
  {
      IDropTarget *target;
      HRESULT r = get_target_from_wrapper( iface, &target );
  
      if (SUCCEEDED( r ))
      {
          r = IDropTarget_Drop( target, pDataObj, grfKeyState, pt, pdwEffect );
          IDropTarget_Release( target );
      }
      return r;
  }
  
  static const IDropTargetVtbl DropTargetWrapperVTbl =
  {
      DropTargetWrapper_QueryInterface,
      DropTargetWrapper_AddRef,
      DropTargetWrapper_Release,
      DropTargetWrapper_DragEnter,
      DropTargetWrapper_DragOver,
      DropTargetWrapper_DragLeave,
      DropTargetWrapper_Drop
  };
  
  static IDropTarget* WrapDropTarget( HWND hwnd )
  {
      DropTargetWrapper* This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
  
      if (This)
      {
          This->IDropTarget_iface.lpVtbl = &DropTargetWrapperVTbl;
          This->hwnd = hwnd;
          This->refs = 1;
      }
      return &This->IDropTarget_iface;
  }
  
  /***********************************************************************
   *     get_droptarget_pointer
   *
   * Retrieves the marshalled IDropTarget from the window.
   */
  static IDropTarget* get_droptarget_pointer(HWND hwnd)
  {
      IDropTarget *droptarget = NULL;
      HANDLE map;
      IStream *stream;
  
      map = get_droptarget_local_handle(hwnd);
      if(!map) return NULL;
  
      if(SUCCEEDED(create_stream_from_map(map, &stream)))
      {
          CoUnmarshalInterface(stream, &IID_IDropTarget, (void**)&droptarget);
          IStream_Release(stream);
      }
      CloseHandle(map);
      return droptarget;
  }
  
  /***********************************************************************
   *           RegisterDragDrop (OLE32.@)
   */
  HRESULT WINAPI RegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget)
  {
    DWORD pid = 0;
    HRESULT hr;
    IStream *stream;
    HANDLE map;
    IDropTarget *wrapper;
  
    TRACE("(%p,%p)\n", hwnd, pDropTarget);
  
    if (!COM_CurrentApt())
    {
      ERR("COM not initialized\n");
      return E_OUTOFMEMORY;
    }
  
    if (!pDropTarget)
      return E_INVALIDARG;
  
    if (!IsWindow(hwnd))
    {
      ERR("invalid hwnd %p\n", hwnd);
      return DRAGDROP_E_INVALIDHWND;
    }
  
    /* block register for other processes windows */
    GetWindowThreadProcessId(hwnd, &pid);
    if (pid != GetCurrentProcessId())
    {
      FIXME("register for another process windows is disabled\n");
      return DRAGDROP_E_INVALIDHWND;
    }
  
    /* check if the window is already registered */
    if (is_droptarget(hwnd))
      return DRAGDROP_E_ALREADYREGISTERED;
  
    /*
     * Marshal the drop target pointer into a shared memory map and
     * store the map's handle in a Wine specific window prop.  We also
     * store the drop target pointer itself in the
     * "OleDropTargetInterface" prop for compatibility with Windows.
     */
  
    hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
    if(FAILED(hr)) return hr;
  
    /* IDropTarget::QueryInterface() shouldn't be called, some (broken) apps depend on this. */
    wrapper = WrapDropTarget( hwnd );
    if(!wrapper)
    {
      IStream_Release(stream);
      return E_OUTOFMEMORY;
    }
    hr = CoMarshalInterface(stream, &IID_IDropTarget, (IUnknown*)wrapper, MSHCTX_LOCAL, NULL, MSHLFLAGS_TABLESTRONG);
    IDropTarget_Release(wrapper);
  
    if(SUCCEEDED(hr))
    {
      hr = create_map_from_stream(stream, &map);
      if(SUCCEEDED(hr))
      {
        IDropTarget_AddRef(pDropTarget);
        SetPropW(hwnd, prop_oledroptarget, pDropTarget);
        SetPropW(hwnd, prop_marshalleddroptarget, map);
      }
      else
      {
        LARGE_INTEGER zero;
        zero.QuadPart = 0;
        IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL);
        CoReleaseMarshalData(stream);
      }
    }
    IStream_Release(stream);
  
    return hr;
  }
  
  /***********************************************************************
   *           RevokeDragDrop (OLE32.@)
   */
  HRESULT WINAPI RevokeDragDrop(HWND hwnd)
  {
    HANDLE map;
    IStream *stream;
    IDropTarget *drop_target;
    HRESULT hr;
  
    TRACE("(%p)\n", hwnd);
  
    if (!IsWindow(hwnd))
    {
      ERR("invalid hwnd %p\n", hwnd);
      return DRAGDROP_E_INVALIDHWND;
    }
  
    /* no registration data */
    if (!(map = get_droptarget_handle(hwnd)))
      return DRAGDROP_E_NOTREGISTERED;
  
    drop_target = GetPropW(hwnd, prop_oledroptarget);
    if(drop_target) IDropTarget_Release(drop_target);
  
    RemovePropW(hwnd, prop_oledroptarget);
    RemovePropW(hwnd, prop_marshalleddroptarget);
  
    hr = create_stream_from_map(map, &stream);
    if(SUCCEEDED(hr))
    {
        CoReleaseMarshalData(stream);
        IStream_Release(stream);
    }
    CloseHandle(map);
  
    return hr;
  }
  
  /***********************************************************************
   *           OleRegGetUserType (OLE32.@)
   */
  HRESULT WINAPI OleRegGetUserType(REFCLSID clsid, DWORD form, LPOLESTR *usertype)
  {
    static const WCHAR auxusertypeW[] = {'A','u','x','U','s','e','r','T','y','p','e','\\','%','d',0};
    DWORD valuetype, valuelen;
    WCHAR auxkeynameW[16];
    HKEY    usertypekey;
    HRESULT hres;
    LONG    ret;
  
    TRACE("(%s, %u, %p)\n", debugstr_guid(clsid), form, usertype);
  
    if (!usertype)
      return E_INVALIDARG;
  
    *usertype = NULL;
  
    /* Return immediately if it's not registered. */
    hres = COM_OpenKeyForCLSID(clsid, NULL, KEY_READ, &usertypekey);
    if (FAILED(hres))
      return hres;
  
    valuelen = 0;
  
    /* Try additional types if requested. If they don't exist fall back to USERCLASSTYPE_FULL. */
    if (form != USERCLASSTYPE_FULL)
    {
      HKEY auxkey;
  
      swprintf(auxkeynameW, ARRAY_SIZE(auxkeynameW), auxusertypeW, form);
      if (COM_OpenKeyForCLSID(clsid, auxkeynameW, KEY_READ, &auxkey) == S_OK)
      {
        if (!RegQueryValueExW(auxkey, emptyW, NULL, &valuetype, NULL, &valuelen) && valuelen)
        {
          RegCloseKey(usertypekey);
          usertypekey = auxkey;
        }
        else
          RegCloseKey(auxkey);
      }
    }
  
    valuelen = 0;
    if (RegQueryValueExW(usertypekey, emptyW, NULL, &valuetype, NULL, &valuelen))
    {
      RegCloseKey(usertypekey);
      return REGDB_E_READREGDB;
    }
  
    *usertype = CoTaskMemAlloc(valuelen);
    if (!*usertype)
    {
      RegCloseKey(usertypekey);
      return E_OUTOFMEMORY;
    }
  
    ret = RegQueryValueExW(usertypekey,
  			  emptyW,
  			  NULL,
  			  &valuetype,
  			  (LPBYTE)*usertype,
  			  &valuelen);
    RegCloseKey(usertypekey);
    if (ret != ERROR_SUCCESS)
    {
      CoTaskMemFree(*usertype);
      *usertype = NULL;
      return REGDB_E_READREGDB;
    }
  
    return S_OK;
  }
  
  /***********************************************************************
   * DoDragDrop [OLE32.@]
   */
  HRESULT WINAPI DoDragDrop (
    IDataObject *pDataObject,  /* [in] ptr to the data obj           */
    IDropSource* pDropSource,  /* [in] ptr to the source obj         */
    DWORD       dwOKEffect,    /* [in] effects allowed by the source */
    DWORD       *pdwEffect)    /* [out] ptr to effects of the source */
  {
    static const WCHAR trackerW[] = {'T','r','a','c','k','e','r','W','i','n','d','o','w',0};
    TrackerWindowInfo trackerInfo;
    HWND            hwndTrackWindow;
    MSG             msg;
  
    TRACE("(%p, %p, %08x, %p)\n", pDataObject, pDropSource, dwOKEffect, pdwEffect);
  
    if (!pDataObject || !pDropSource || !pdwEffect)
        return E_INVALIDARG;
  
    /*
     * Setup the drag n drop tracking window.
     */
  
    trackerInfo.dataObject        = pDataObject;
    trackerInfo.dropSource        = pDropSource;
    trackerInfo.dwOKEffect        = dwOKEffect;
    trackerInfo.pdwEffect         = pdwEffect;
    trackerInfo.trackingDone      = FALSE;
    trackerInfo.inTrackCall       = FALSE;
    trackerInfo.escPressed        = FALSE;
    trackerInfo.curTargetHWND     = 0;
    trackerInfo.curDragTarget     = 0;
  
    hwndTrackWindow = CreateWindowW(OLEDD_DRAGTRACKERCLASS, trackerW,
                                    WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT,
                                    CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, 0,
                                    &trackerInfo);
  
    if (hwndTrackWindow)
    {
      /*
       * Capture the mouse input
       */
      SetCapture(hwndTrackWindow);
  
      msg.message = 0;
  
      /*
       * Pump messages. All mouse input should go to the capture window.
       */
      while (!trackerInfo.trackingDone && GetMessageW(&msg, 0, 0, 0) )
      {
        trackerInfo.curMousePos.x = msg.pt.x;
        trackerInfo.curMousePos.y = msg.pt.y;
        trackerInfo.dwKeyState = OLEDD_GetButtonState();
  	    
        if ( (msg.message >= WM_KEYFIRST) &&
  	   (msg.message <= WM_KEYLAST) )
        {
  	/*
  	 * When keyboard messages are sent to windows on this thread, we
  	 * want to ignore notify the drop source that the state changed.
  	 * in the case of the Escape key, we also notify the drop source
  	 * we give it a special meaning.
  	 */
  	if ( (msg.message==WM_KEYDOWN) &&
  	     (msg.wParam==VK_ESCAPE) )
  	{
  	  trackerInfo.escPressed = TRUE;
  	}
  
  	/*
  	 * Notify the drop source.
  	 */
          OLEDD_TrackStateChange(&trackerInfo);
        }
        else
        {
  	/*
  	 * Dispatch the messages only when it's not a keyboard message.
  	 */
  	DispatchMessageW(&msg);
        }
      }
  
      /* re-post the quit message to outer message loop */
      if (msg.message == WM_QUIT)
          PostQuitMessage(msg.wParam);
      /*
       * Destroy the temporary window.
       */
      DestroyWindow(hwndTrackWindow);
  
      return trackerInfo.returnValue;
    }
  
    return E_FAIL;
  }
  
  /***********************************************************************
   * OleQueryLinkFromData [OLE32.@]
   */
  HRESULT WINAPI OleQueryLinkFromData(
    IDataObject* pSrcDataObject)
  {
    FIXME("(%p),stub!\n", pSrcDataObject);
    return S_FALSE;
  }
  
  /***********************************************************************
   * OleRegGetMiscStatus [OLE32.@]
   */
  HRESULT WINAPI OleRegGetMiscStatus(
    REFCLSID clsid,
    DWORD    dwAspect,
    DWORD*   pdwStatus)
  {
    static const WCHAR miscstatusW[] = {'M','i','s','c','S','t','a','t','u','s',0};
    static const WCHAR dfmtW[] = {'%','d',0};
    WCHAR   keyName[16];
    HKEY    miscStatusKey;
    HKEY    aspectKey;
    LONG    result;
    HRESULT hr;
  
    TRACE("(%s, %d, %p)\n", debugstr_guid(clsid), dwAspect, pdwStatus);
  
    if (!pdwStatus) return E_INVALIDARG;
  
    *pdwStatus = 0;
  
    if (actctx_get_miscstatus(clsid, dwAspect, pdwStatus)) return S_OK;
  
    hr = COM_OpenKeyForCLSID(clsid, miscstatusW, KEY_READ, &miscStatusKey);
    if (FAILED(hr))
      /* missing key is not a failure */
      return hr == REGDB_E_KEYMISSING ? S_OK : hr;
  
    OLEUTL_ReadRegistryDWORDValue(miscStatusKey, pdwStatus);
  
    /*
     * Open the key specific to the requested aspect.
     */
    swprintf(keyName, ARRAY_SIZE(keyName), dfmtW, dwAspect);
  
    result = open_classes_key(miscStatusKey, keyName, KEY_READ, &aspectKey);
    if (result == ERROR_SUCCESS)
    {
      OLEUTL_ReadRegistryDWORDValue(aspectKey, pdwStatus);
      RegCloseKey(aspectKey);
    }
  
    RegCloseKey(miscStatusKey);
    return S_OK;
  }
  
  static HRESULT EnumOLEVERB_Construct(HKEY hkeyVerb, ULONG index, IEnumOLEVERB **ppenum);
  
  typedef struct
  {
      IEnumOLEVERB IEnumOLEVERB_iface;
      LONG ref;
  
      HKEY hkeyVerb;
      ULONG index;
  } EnumOLEVERB;
  
  static inline EnumOLEVERB *impl_from_IEnumOLEVERB(IEnumOLEVERB *iface)
  {
      return CONTAINING_RECORD(iface, EnumOLEVERB, IEnumOLEVERB_iface);
  }
  
  static HRESULT WINAPI EnumOLEVERB_QueryInterface(
      IEnumOLEVERB *iface, REFIID riid, void **ppv)
  {
      TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
      if (IsEqualIID(riid, &IID_IUnknown) ||
          IsEqualIID(riid, &IID_IEnumOLEVERB))
      {
          IEnumOLEVERB_AddRef(iface);
          *ppv = iface;
          return S_OK;
      }
      return E_NOINTERFACE;
  }
  
  static ULONG WINAPI EnumOLEVERB_AddRef(
      IEnumOLEVERB *iface)
  {
      EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
      TRACE("()\n");
      return InterlockedIncrement(&This->ref);
  }
  
  static ULONG WINAPI EnumOLEVERB_Release(
      IEnumOLEVERB *iface)
  {
      EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
      LONG refs = InterlockedDecrement(&This->ref);
      TRACE("()\n");
      if (!refs)
      {
          RegCloseKey(This->hkeyVerb);
          HeapFree(GetProcessHeap(), 0, This);
      }
      return refs;
  }
  
  static HRESULT WINAPI EnumOLEVERB_Next(
      IEnumOLEVERB *iface, ULONG celt, LPOLEVERB rgelt,
      ULONG *pceltFetched)
  {
      EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
      HRESULT hr = S_OK;
  
      TRACE("(%d, %p, %p)\n", celt, rgelt, pceltFetched);
  
      if (pceltFetched)
          *pceltFetched = 0;
  
      for (; celt; celt--, rgelt++)
      {
          WCHAR wszSubKey[20];
          LONG cbData;
          LPWSTR pwszOLEVERB;
          LPWSTR pwszMenuFlags;
          LPWSTR pwszAttribs;
          LONG res = RegEnumKeyW(This->hkeyVerb, This->index, wszSubKey, ARRAY_SIZE(wszSubKey));
          if (res == ERROR_NO_MORE_ITEMS)
          {
              hr = S_FALSE;
              break;
          }
          else if (res != ERROR_SUCCESS)
          {
              ERR("RegEnumKeyW failed with error %d\n", res);
              hr = REGDB_E_READREGDB;
              break;
          }
          res = RegQueryValueW(This->hkeyVerb, wszSubKey, NULL, &cbData);
          if (res != ERROR_SUCCESS)
          {
              ERR("RegQueryValueW failed with error %d\n", res);
              hr = REGDB_E_READREGDB;
              break;
          }
          pwszOLEVERB = CoTaskMemAlloc(cbData);
          if (!pwszOLEVERB)
          {
              hr = E_OUTOFMEMORY;
              break;
          }
          res = RegQueryValueW(This->hkeyVerb, wszSubKey, pwszOLEVERB, &cbData);
          if (res != ERROR_SUCCESS)
          {
              ERR("RegQueryValueW failed with error %d\n", res);
              hr = REGDB_E_READREGDB;
              CoTaskMemFree(pwszOLEVERB);
              break;
          }
  
          TRACE("verb string: %s\n", debugstr_w(pwszOLEVERB));
          pwszMenuFlags = wcschr(pwszOLEVERB, ',');
          if (!pwszMenuFlags)
          {
              hr = OLEOBJ_E_INVALIDVERB;
              CoTaskMemFree(pwszOLEVERB);
              break;
          }
          /* nul terminate the name string and advance to first character */
          *pwszMenuFlags = '\0';
          pwszMenuFlags++;
          pwszAttribs = wcschr(pwszMenuFlags, ',');
          if (!pwszAttribs)
          {
              hr = OLEOBJ_E_INVALIDVERB;
              CoTaskMemFree(pwszOLEVERB);
              break;
          }
          /* nul terminate the menu string and advance to first character */
          *pwszAttribs = '\0';
          pwszAttribs++;
  
          /* fill out structure for this verb */
          rgelt->lVerb = wcstol(wszSubKey, NULL, 10);
          rgelt->lpszVerbName = pwszOLEVERB; /* user should free */
          rgelt->fuFlags = wcstol(pwszMenuFlags, NULL, 10);
          rgelt->grfAttribs = wcstol(pwszAttribs, NULL, 10);
  
          if (pceltFetched)
              (*pceltFetched)++;
          This->index++;
      }
      return hr;
  }
  
  static HRESULT WINAPI EnumOLEVERB_Skip(
      IEnumOLEVERB *iface, ULONG celt)
  {
      EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
  
      TRACE("(%d)\n", celt);
  
      This->index += celt;
      return S_OK;
  }
  
  static HRESULT WINAPI EnumOLEVERB_Reset(
      IEnumOLEVERB *iface)
  {
      EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
  
      TRACE("()\n");
  
      This->index = 0;
      return S_OK;
  }
  
  static HRESULT WINAPI EnumOLEVERB_Clone(
      IEnumOLEVERB *iface,
      IEnumOLEVERB **ppenum)
  {
      EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
      HKEY hkeyVerb;
      TRACE("(%p)\n", ppenum);
      if (!DuplicateHandle(GetCurrentProcess(), This->hkeyVerb, GetCurrentProcess(), (HANDLE *)&hkeyVerb, 0, FALSE, DUPLICATE_SAME_ACCESS))
          return HRESULT_FROM_WIN32(GetLastError());
      return EnumOLEVERB_Construct(hkeyVerb, This->index, ppenum);
  }
  
  static const IEnumOLEVERBVtbl EnumOLEVERB_VTable =
  {
      EnumOLEVERB_QueryInterface,
      EnumOLEVERB_AddRef,
      EnumOLEVERB_Release,
      EnumOLEVERB_Next,
      EnumOLEVERB_Skip,
      EnumOLEVERB_Reset,
      EnumOLEVERB_Clone
  };
  
  static HRESULT EnumOLEVERB_Construct(HKEY hkeyVerb, ULONG index, IEnumOLEVERB **ppenum)
  {
      EnumOLEVERB *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
      if (!This)
      {
          RegCloseKey(hkeyVerb);
          return E_OUTOFMEMORY;
      }
      This->IEnumOLEVERB_iface.lpVtbl = &EnumOLEVERB_VTable;
      This->ref = 1;
      This->index = index;
      This->hkeyVerb = hkeyVerb;
      *ppenum = &This->IEnumOLEVERB_iface;
      return S_OK;
  }
  
  /***********************************************************************
   *           OleRegEnumVerbs    [OLE32.@]
   *
   * Enumerates verbs associated with a class stored in the registry.
   *
   * PARAMS
   *  clsid  [I] Class ID to enumerate the verbs for.
   *  ppenum [O] Enumerator.
   *
   * RETURNS
   *  S_OK: Success.
   *  REGDB_E_CLASSNOTREG: The specified class does not have a key in the registry.
   *  REGDB_E_READREGDB: The class key could not be opened for some other reason.
   *  OLE_E_REGDB_KEY: The Verb subkey for the class is not present.
   *  OLEOBJ_E_NOVERBS: The Verb subkey for the class is empty.
   */
  HRESULT WINAPI OleRegEnumVerbs (REFCLSID clsid, LPENUMOLEVERB* ppenum)
  {
      LONG res;
      HKEY hkeyVerb;
      DWORD dwSubKeys;
      static const WCHAR wszVerb[] = {'V','e','r','b',0};
  
      TRACE("(%s, %p)\n", debugstr_guid(clsid), ppenum);
  
      res = COM_OpenKeyForCLSID(clsid, wszVerb, KEY_READ, &hkeyVerb);
      if (FAILED(res))
      {
          if (res == REGDB_E_CLASSNOTREG)
              ERR("CLSID %s not registered\n", debugstr_guid(clsid));
          else if (res == REGDB_E_KEYMISSING)
              ERR("no Verbs key for class %s\n", debugstr_guid(clsid));
          else
              ERR("failed to open Verbs key for CLSID %s with error %d\n",
                  debugstr_guid(clsid), res);
          return res;
      }
  
      res = RegQueryInfoKeyW(hkeyVerb, NULL, NULL, NULL, &dwSubKeys, NULL,
                            NULL, NULL, NULL, NULL, NULL, NULL);
      if (res != ERROR_SUCCESS)
      {
          ERR("failed to get subkey count with error %d\n", GetLastError());
          return REGDB_E_READREGDB;
      }
  
      if (!dwSubKeys)
      {
          WARN("class %s has no verbs\n", debugstr_guid(clsid));
          RegCloseKey(hkeyVerb);
          return OLEOBJ_E_NOVERBS;
      }
  
      return EnumOLEVERB_Construct(hkeyVerb, 0, ppenum);
  }
  
  /******************************************************************************
   *              OleSetContainedObject        [OLE32.@]
   */
  HRESULT WINAPI OleSetContainedObject(
    LPUNKNOWN pUnknown,
    BOOL      fContained)
  {
    IRunnableObject* runnable = NULL;
    HRESULT          hres;
  
    TRACE("(%p,%x)\n", pUnknown, fContained);
  
    hres = IUnknown_QueryInterface(pUnknown,
  				 &IID_IRunnableObject,
  				 (void**)&runnable);
  
    if (SUCCEEDED(hres))
    {
      hres = IRunnableObject_SetContainedObject(runnable, fContained);
  
      IRunnableObject_Release(runnable);
  
      return hres;
    }
  
    return S_OK;
  }
  
  /******************************************************************************
   *              OleRun        [OLE32.@]
   *
   * Set the OLE object to the running state.
   *
   * PARAMS
   *  pUnknown [I] OLE object to run.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: Any HRESULT code.
   */
  HRESULT WINAPI DECLSPEC_HOTPATCH OleRun(LPUNKNOWN pUnknown)
  {
      IRunnableObject *runable;
      HRESULT hres;
  
      TRACE("(%p)\n", pUnknown);
  
      hres = IUnknown_QueryInterface(pUnknown, &IID_IRunnableObject, (void**)&runable);
      if (FAILED(hres))
          return S_OK; /* Appears to return no error. */
  
      hres = IRunnableObject_Run(runable, NULL);
      IRunnableObject_Release(runable);
      return hres;
  }
  
  /******************************************************************************
   *              OleLoad        [OLE32.@]
   */
  HRESULT WINAPI OleLoad(
    LPSTORAGE       pStg,
    REFIID          riid,
    LPOLECLIENTSITE pClientSite,
    LPVOID*         ppvObj)
  {
    IPersistStorage* persistStorage = NULL;
    IUnknown*        pUnk;
    IOleObject*      pOleObject      = NULL;
    STATSTG          storageInfo;
    HRESULT          hres;
  
    TRACE("(%p, %s, %p, %p)\n", pStg, debugstr_guid(riid), pClientSite, ppvObj);
  
    *ppvObj = NULL;
  
    /*
     * TODO, Conversion ... OleDoAutoConvert
     */
  
    /*
     * Get the class ID for the object.
     */
    hres = IStorage_Stat(pStg, &storageInfo, STATFLAG_NONAME);
    if (FAILED(hres))
      return hres;
  
    /*
     * Now, try and create the handler for the object
     */
    hres = CoCreateInstance(&storageInfo.clsid,
  			  NULL,
  			  CLSCTX_INPROC_HANDLER|CLSCTX_INPROC_SERVER,
  			  riid,
  			  (void**)&pUnk);
  
    /*
     * If that fails, as it will most times, load the default
     * OLE handler.
     */
    if (FAILED(hres))
    {
      hres = OleCreateDefaultHandler(&storageInfo.clsid,
  				   NULL,
  				   riid,
  				   (void**)&pUnk);
    }
  
    /*
     * If we couldn't find a handler... this is bad. Abort the whole thing.
     */
    if (FAILED(hres))
      return hres;
  
    if (pClientSite)
    {
      hres = IUnknown_QueryInterface(pUnk, &IID_IOleObject, (void **)&pOleObject);
      if (SUCCEEDED(hres))
      {
          DWORD dwStatus;
          hres = IOleObject_GetMiscStatus(pOleObject, DVASPECT_CONTENT, &dwStatus);
      }
    }
  
    /*
     * Initialize the object with its IPersistStorage interface.
     */
    hres = IUnknown_QueryInterface(pUnk, &IID_IPersistStorage, (void**)&persistStorage);
    if (SUCCEEDED(hres))
    {
      hres = IPersistStorage_Load(persistStorage, pStg);
  
      IPersistStorage_Release(persistStorage);
      persistStorage = NULL;
    }
  
    if (SUCCEEDED(hres) && pClientSite)
      /*
       * Inform the new object of its client site.
       */
      hres = IOleObject_SetClientSite(pOleObject, pClientSite);
  
    /*
     * Cleanup interfaces used internally
     */
    if (pOleObject)
      IOleObject_Release(pOleObject);
  
    if (SUCCEEDED(hres))
    {
      IOleLink *pOleLink;
      HRESULT hres1;
      hres1 = IUnknown_QueryInterface(pUnk, &IID_IOleLink, (void **)&pOleLink);
      if (SUCCEEDED(hres1))
      {
        FIXME("handle OLE link\n");
        IOleLink_Release(pOleLink);
      }
    }
  
    if (FAILED(hres))
    {
      IUnknown_Release(pUnk);
      pUnk = NULL;
    }
  
    *ppvObj = pUnk;
  
    return hres;
  }
  
  /***********************************************************************
   *           OleSave     [OLE32.@]
   */
  HRESULT WINAPI OleSave(
    LPPERSISTSTORAGE pPS,
    LPSTORAGE        pStg,
    BOOL             fSameAsLoad)
  {
    HRESULT hres;
    CLSID   objectClass;
  
    TRACE("(%p,%p,%x)\n", pPS, pStg, fSameAsLoad);
  
    /*
     * First, we transfer the class ID (if available)
     */
    hres = IPersistStorage_GetClassID(pPS, &objectClass);
  
    if (SUCCEEDED(hres))
    {
      WriteClassStg(pStg, &objectClass);
    }
  
    /*
     * Then, we ask the object to save itself to the
     * storage. If it is successful, we commit the storage.
     */
    hres = IPersistStorage_Save(pPS, pStg, fSameAsLoad);
  
    if (SUCCEEDED(hres))
    {
      IStorage_Commit(pStg,
  		    STGC_DEFAULT);
    }
  
    return hres;
  }
  
  
  /******************************************************************************
   *              OleLockRunning        [OLE32.@]
   */
  HRESULT WINAPI OleLockRunning(LPUNKNOWN pUnknown, BOOL fLock, BOOL fLastUnlockCloses)
  {
    IRunnableObject* runnable = NULL;
    HRESULT          hres;
  
    TRACE("(%p,%x,%x)\n", pUnknown, fLock, fLastUnlockCloses);
  
    hres = IUnknown_QueryInterface(pUnknown,
  				 &IID_IRunnableObject,
  				 (void**)&runnable);
  
    if (SUCCEEDED(hres))
    {
      hres = IRunnableObject_LockRunning(runnable, fLock, fLastUnlockCloses);
  
      IRunnableObject_Release(runnable);
  
      return hres;
    }
  
    return S_OK;
  }
  
  
  /**************************************************************************
   * Internal methods to manage the shared OLE menu in response to the
   * OLE***MenuDescriptor API
   */
  
  /***
   * OLEMenu_Initialize()
   *
   * Initializes the OLEMENU data structures.
   */
  static void OLEMenu_Initialize(void)
  {
  }
  
  /***
   * OLEMenu_UnInitialize()
   *
   * Releases the OLEMENU data structures.
   */
  static void OLEMenu_UnInitialize(void)
  {
  }
  
  /*************************************************************************
   * OLEMenu_InstallHooks
   * Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
   *
   * RETURNS: TRUE if message hooks were successfully installed
   *          FALSE on failure
   */
  static BOOL OLEMenu_InstallHooks( DWORD tid )
  {
    OleMenuHookItem *pHookItem;
  
    /* Create an entry for the hook table */
    if ( !(pHookItem = HeapAlloc(GetProcessHeap(), 0,
                                 sizeof(OleMenuHookItem)) ) )
      return FALSE;
  
    pHookItem->tid = tid;
    pHookItem->hHeap = GetProcessHeap();
    pHookItem->CallWndProc_hHook = NULL;
  
    /* Install a thread scope message hook for WH_GETMESSAGE */
    pHookItem->GetMsg_hHook = SetWindowsHookExW( WH_GETMESSAGE, OLEMenu_GetMsgProc,
                                                 0, GetCurrentThreadId() );
    if ( !pHookItem->GetMsg_hHook )
      goto CLEANUP;
  
    /* Install a thread scope message hook for WH_CALLWNDPROC */
    pHookItem->CallWndProc_hHook = SetWindowsHookExW( WH_CALLWNDPROC, OLEMenu_CallWndProc,
                                                      0, GetCurrentThreadId() );
    if ( !pHookItem->CallWndProc_hHook )
      goto CLEANUP;
  
    /* Insert the hook table entry */
    pHookItem->next = hook_list;
    hook_list = pHookItem;
  
    return TRUE;
  
  CLEANUP:
    /* Unhook any hooks */
    if ( pHookItem->GetMsg_hHook )
      UnhookWindowsHookEx( pHookItem->GetMsg_hHook );
    if ( pHookItem->CallWndProc_hHook )
      UnhookWindowsHookEx( pHookItem->CallWndProc_hHook );
    /* Release the hook table entry */
    HeapFree(pHookItem->hHeap, 0, pHookItem );
  
    return FALSE;
  }
  
  /*************************************************************************
   * OLEMenu_UnInstallHooks
   * UnInstall thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
   *
   * RETURNS: TRUE if message hooks were successfully installed
   *          FALSE on failure
   */
  static BOOL OLEMenu_UnInstallHooks( DWORD tid )
  {
    OleMenuHookItem *pHookItem = NULL;
    OleMenuHookItem **ppHook = &hook_list;
  
    while (*ppHook)
    {
        if ((*ppHook)->tid == tid)
        {
            pHookItem = *ppHook;
            *ppHook = pHookItem->next;
            break;
        }
        ppHook = &(*ppHook)->next;
    }
    if (!pHookItem) return FALSE;
  
    /* Uninstall the hooks installed for this thread */
    if ( !UnhookWindowsHookEx( pHookItem->GetMsg_hHook ) )
      goto CLEANUP;
    if ( !UnhookWindowsHookEx( pHookItem->CallWndProc_hHook ) )
      goto CLEANUP;
  
    /* Release the hook table entry */
    HeapFree(pHookItem->hHeap, 0, pHookItem );
  
    return TRUE;
  
  CLEANUP:
    /* Release the hook table entry */
    HeapFree(pHookItem->hHeap, 0, pHookItem );
  
    return FALSE;
  }
  
  /*************************************************************************
   * OLEMenu_IsHookInstalled
   * Tests if OLEMenu hooks have been installed for a thread
   *
   * RETURNS: The pointer and index of the hook table entry for the tid
   *          NULL and -1 for the index if no hooks were installed for this thread
   */
  static OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid )
  {
    OleMenuHookItem *pHookItem;
  
    /* Do a simple linear search for an entry whose tid matches ours.
     * We really need a map but efficiency is not a concern here. */
    for (pHookItem = hook_list; pHookItem; pHookItem = pHookItem->next)
    {
      if ( tid == pHookItem->tid )
        return pHookItem;
    }
  
    return NULL;
  }
  
  /***********************************************************************
   *           OLEMenu_FindMainMenuIndex
   *
   * Used by OLEMenu API to find the top level group a menu item belongs to.
   * On success pnPos contains the index of the item in the top level menu group
   *
   * RETURNS: TRUE if the ID was found, FALSE on failure
   */
  static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos )
  {
    INT i, nItems;
  
    nItems = GetMenuItemCount( hMainMenu );
  
    for (i = 0; i < nItems; i++)
    {
      HMENU hsubmenu;
  
      /*  Is the current item a submenu? */
      if ( (hsubmenu = GetSubMenu(hMainMenu, i)) )
      {
        /* If the handle is the same we're done */
        if ( hsubmenu == hPopupMenu )
        {
          if (pnPos)
            *pnPos = i;
          return TRUE;
        }
        /* Recursively search without updating pnPos */
        else if ( OLEMenu_FindMainMenuIndex( hsubmenu, hPopupMenu, NULL ) )
        {
          if (pnPos)
            *pnPos = i;
          return TRUE;
        }
      }
    }
  
    return FALSE;
  }
  
  /***********************************************************************
   *           OLEMenu_SetIsServerMenu
   *
   * Checks whether a popup menu belongs to a shared menu group which is
   * owned by the server, and sets the menu descriptor state accordingly.
   * All menu messages from these groups should be routed to the server.
   *
   * RETURNS: TRUE if the popup menu is part of a server owned group
   *          FALSE if the popup menu is part of a container owned group
   */
  static BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor )
  {
    UINT nPos = 0, nWidth, i;
  
    pOleMenuDescriptor->bIsServerItem = FALSE;
  
    /* Don't bother searching if the popup is the combined menu itself */
    if ( hmenu == pOleMenuDescriptor->hmenuCombined )
      return FALSE;
  
    /* Find the menu item index in the shared OLE menu that this item belongs to */
    if ( !OLEMenu_FindMainMenuIndex( pOleMenuDescriptor->hmenuCombined, hmenu,  &nPos ) )
      return FALSE;
  
    /* The group widths array has counts for the number of elements
     * in the groups File, Edit, Container, Object, Window, Help.
     * The Edit, Object & Help groups belong to the server object
     * and the other three belong to the container.
     * Loop through the group widths and locate the group we are a member of.
     */
    for ( i = 0, nWidth = 0; i < 6; i++ )
    {
      nWidth += pOleMenuDescriptor->mgw.width[i];
      if ( nPos < nWidth )
      {
        /* Odd elements are server menu widths */
        pOleMenuDescriptor->bIsServerItem = i%2;
        break;
      }
    }
  
    return pOleMenuDescriptor->bIsServerItem;
  }
  
  /*************************************************************************
   * OLEMenu_CallWndProc
   * Thread scope WH_CALLWNDPROC hook proc filter function (callback)
   * This is invoked from a message hook installed in OleSetMenuDescriptor.
   */
  static LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam)
  {
    LPCWPSTRUCT pMsg;
    HOLEMENU hOleMenu = 0;
    OleMenuDescriptor *pOleMenuDescriptor = NULL;
    OleMenuHookItem *pHookItem = NULL;
    WORD fuFlags;
  
    TRACE("%i, %04lx, %08lx\n", code, wParam, lParam );
  
    /* Check if we're being asked to process the message */
    if ( HC_ACTION != code )
      goto NEXTHOOK;
  
    /* Retrieve the current message being dispatched from lParam */
    pMsg = (LPCWPSTRUCT)lParam;
  
    /* Check if the message is destined for a window we are interested in:
     * If the window has an OLEMenu property we may need to dispatch
     * the menu message to its active objects window instead. */
  
    hOleMenu = GetPropW( pMsg->hwnd, prop_olemenuW );
    if ( !hOleMenu )
      goto NEXTHOOK;
  
    /* Get the menu descriptor */
    pOleMenuDescriptor = GlobalLock( hOleMenu );
    if ( !pOleMenuDescriptor ) /* Bad descriptor! */
      goto NEXTHOOK;
  
    /* Process menu messages */
    switch( pMsg->message )
    {
      case WM_INITMENU:
      {
        /* Reset the menu descriptor state */
        pOleMenuDescriptor->bIsServerItem = FALSE;
  
        /* Send this message to the server as well */
        SendMessageW( pOleMenuDescriptor->hwndActiveObject,
                    pMsg->message, pMsg->wParam, pMsg->lParam );
        goto NEXTHOOK;
      }
  
      case WM_INITMENUPOPUP:
      {
        /* Save the state for whether this is a server owned menu */
        OLEMenu_SetIsServerMenu( (HMENU)pMsg->wParam, pOleMenuDescriptor );
        break;
      }
  
      case WM_MENUSELECT:
      {
        fuFlags = HIWORD(pMsg->wParam);  /* Get flags */
        if ( fuFlags & MF_SYSMENU )
           goto NEXTHOOK;
  
        /* Save the state for whether this is a server owned popup menu */
        else if ( fuFlags & MF_POPUP )
          OLEMenu_SetIsServerMenu( (HMENU)pMsg->lParam, pOleMenuDescriptor );
  
        break;
      }
  
      case WM_DRAWITEM:
      {
        LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) pMsg->lParam;
        if ( pMsg->wParam != 0 || lpdis->CtlType != ODT_MENU )
          goto NEXTHOOK;  /* Not a menu message */
  
        break;
      }
  
      default:
        goto NEXTHOOK;
    }
  
    /* If the message was for the server dispatch it accordingly */
    if ( pOleMenuDescriptor->bIsServerItem )
    {
      SendMessageW( pOleMenuDescriptor->hwndActiveObject,
                    pMsg->message, pMsg->wParam, pMsg->lParam );
    }
  
  NEXTHOOK:
    if ( pOleMenuDescriptor )
      GlobalUnlock( hOleMenu );
  
    /* Lookup the hook item for the current thread */
    if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId() ) ) )
    {
      /* This should never fail!! */
      WARN("could not retrieve hHook for current thread!\n" );
      return 0;
    }
  
    /* Pass on the message to the next hooker */
    return CallNextHookEx( pHookItem->CallWndProc_hHook, code, wParam, lParam );
  }
  
  /*************************************************************************
   * OLEMenu_GetMsgProc
   * Thread scope WH_GETMESSAGE hook proc filter function (callback)
   * This is invoked from a message hook installed in OleSetMenuDescriptor.
   */
  static LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam)
  {
    LPMSG pMsg;
    HOLEMENU hOleMenu = 0;
    OleMenuDescriptor *pOleMenuDescriptor = NULL;
    OleMenuHookItem *pHookItem = NULL;
    WORD wCode;
  
    TRACE("%i, %04lx, %08lx\n", code, wParam, lParam );
  
    /* Check if we're being asked to process a  messages */
    if ( HC_ACTION != code )
      goto NEXTHOOK;
  
    /* Retrieve the current message being dispatched from lParam */
    pMsg = (LPMSG)lParam;
  
    /* Check if the message is destined for a window we are interested in:
     * If the window has an OLEMenu property we may need to dispatch
     * the menu message to its active objects window instead. */
  
    hOleMenu = GetPropW( pMsg->hwnd, prop_olemenuW );
    if ( !hOleMenu )
      goto NEXTHOOK;
  
    /* Process menu messages */
    switch( pMsg->message )
    {
      case WM_COMMAND:
      {
        wCode = HIWORD(pMsg->wParam);  /* Get notification code */
        if ( wCode )
          goto NEXTHOOK;  /* Not a menu message */
        break;
      }
      default:
        goto NEXTHOOK;
    }
  
    /* Get the menu descriptor */
    pOleMenuDescriptor = GlobalLock( hOleMenu );
    if ( !pOleMenuDescriptor ) /* Bad descriptor! */
      goto NEXTHOOK;
  
    /* If the message was for the server dispatch it accordingly */
    if ( pOleMenuDescriptor->bIsServerItem )
    {
      /* Change the hWnd in the message to the active objects hWnd.
       * The message loop which reads this message will automatically
       * dispatch it to the embedded objects window. */
      pMsg->hwnd = pOleMenuDescriptor->hwndActiveObject;
    }
  
  NEXTHOOK:
    if ( pOleMenuDescriptor )
      GlobalUnlock( hOleMenu );
  
    /* Lookup the hook item for the current thread */
    if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId() ) ) )
    {
      /* This should never fail!! */
      WARN("could not retrieve hHook for current thread!\n" );
      return FALSE;
    }
  
    /* Pass on the message to the next hooker */
    return CallNextHookEx( pHookItem->GetMsg_hHook, code, wParam, lParam );
  }
  
  /***********************************************************************
   * OleCreateMenuDescriptor [OLE32.@]
   * Creates an OLE menu descriptor for OLE to use when dispatching
   * menu messages and commands.
   *
   * PARAMS:
   *    hmenuCombined  -  Handle to the objects combined menu
   *    lpMenuWidths   -  Pointer to array of 6 LONG's indicating menus per group
   *
   */
  HOLEMENU WINAPI OleCreateMenuDescriptor(
    HMENU                hmenuCombined,
    LPOLEMENUGROUPWIDTHS lpMenuWidths)
  {
    HOLEMENU hOleMenu;
    OleMenuDescriptor *pOleMenuDescriptor;
    int i;
  
    if ( !hmenuCombined || !lpMenuWidths )
      return 0;
  
    /* Create an OLE menu descriptor */
    if ( !(hOleMenu = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
                                  sizeof(OleMenuDescriptor) ) ) )
    return 0;
  
    pOleMenuDescriptor = GlobalLock( hOleMenu );
    if ( !pOleMenuDescriptor )
      return 0;
  
    /* Initialize menu group widths and hmenu */
    for ( i = 0; i < 6; i++ )
      pOleMenuDescriptor->mgw.width[i] = lpMenuWidths->width[i];
  
    pOleMenuDescriptor->hmenuCombined = hmenuCombined;
    pOleMenuDescriptor->bIsServerItem = FALSE;
    GlobalUnlock( hOleMenu );
  
    return hOleMenu;
  }
  
  /***********************************************************************
   * OleDestroyMenuDescriptor [OLE32.@]
   * Destroy the shared menu descriptor
   */
  HRESULT WINAPI OleDestroyMenuDescriptor(
      HOLEMENU hmenuDescriptor)
  {
      if ( hmenuDescriptor )
          GlobalFree( hmenuDescriptor );
      return S_OK;
  }
  
  /***********************************************************************
   * OleSetMenuDescriptor [OLE32.@]
   * Installs or removes OLE dispatching code for the containers frame window.
   *
   * PARAMS
   *     hOleMenu         Handle to composite menu descriptor
   *     hwndFrame        Handle to containers frame window
   *     hwndActiveObject Handle to objects in-place activation window
   *     lpFrame          Pointer to IOleInPlaceFrame on containers window
   *     lpActiveObject   Pointer to IOleInPlaceActiveObject on active in-place object
   *
   * RETURNS
   *      S_OK                               - menu installed correctly
   *      E_FAIL, E_INVALIDARG, E_UNEXPECTED - failure
   *
   * FIXME
   *      The lpFrame and lpActiveObject parameters are currently ignored
   *      OLE should install context sensitive help F1 filtering for the app when
   *      these are non null.
   */
  HRESULT WINAPI OleSetMenuDescriptor(
      HOLEMENU               hOleMenu,
      HWND                   hwndFrame,
      HWND                   hwndActiveObject,
      LPOLEINPLACEFRAME        lpFrame,
      LPOLEINPLACEACTIVEOBJECT lpActiveObject)
  {
      OleMenuDescriptor *pOleMenuDescriptor = NULL;
  
      /* Check args */
      if ( !hwndFrame || (hOleMenu && !hwndActiveObject) )
          return E_INVALIDARG;
  
      if ( lpFrame || lpActiveObject )
      {
          FIXME("(%p, %p, %p, %p, %p), Context sensitive help filtering not implemented!\n",
          hOleMenu,
          hwndFrame,
          hwndActiveObject,
          lpFrame,
          lpActiveObject);
      }
  
      /* Set up a message hook to intercept the containers frame window messages.
       * The message filter is responsible for dispatching menu messages from the
       * shared menu which are intended for the object.
       */
  
      if ( hOleMenu )  /* Want to install dispatching code */
      {
          /* If OLEMenu hooks are already installed for this thread, fail
           * Note: This effectively means that OleSetMenuDescriptor cannot
           * be called twice in succession on the same frame window
           * without first calling it with a null hOleMenu to uninstall
           */
          if ( OLEMenu_IsHookInstalled( GetCurrentThreadId() ) )
              return E_FAIL;
  
          /* Get the menu descriptor */
          pOleMenuDescriptor = GlobalLock( hOleMenu );
          if ( !pOleMenuDescriptor )
              return E_UNEXPECTED;
  
          /* Update the menu descriptor */
          pOleMenuDescriptor->hwndFrame = hwndFrame;
          pOleMenuDescriptor->hwndActiveObject = hwndActiveObject;
  
          GlobalUnlock( hOleMenu );
          pOleMenuDescriptor = NULL;
  
          /* Add a menu descriptor windows property to the frame window */
          SetPropW( hwndFrame, prop_olemenuW, hOleMenu );
  
          /* Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC */
          if ( !OLEMenu_InstallHooks( GetCurrentThreadId() ) )
              return E_FAIL;
      }
      else  /* Want to uninstall dispatching code */
      {
          /* Uninstall the hooks */
          if ( !OLEMenu_UnInstallHooks( GetCurrentThreadId() ) )
              return E_FAIL;
  
          /* Remove the menu descriptor property from the frame window */
          RemovePropW( hwndFrame, prop_olemenuW );
      }
  
      return S_OK;
  }
  
  /******************************************************************************
   *              IsAccelerator        [OLE32.@]
   * Mostly copied from controls/menu.c TranslateAccelerator implementation
   */
  BOOL WINAPI IsAccelerator(HACCEL hAccel, int cAccelEntries, LPMSG lpMsg, WORD* lpwCmd)
  {
      LPACCEL lpAccelTbl;
      int i;
  
      if(!lpMsg) return FALSE;
      if (!hAccel)
      {
  	WARN_(accel)("NULL accel handle\n");
  	return FALSE;
      }
      if((lpMsg->message != WM_KEYDOWN &&
  	lpMsg->message != WM_SYSKEYDOWN &&
  	lpMsg->message != WM_SYSCHAR &&
  	lpMsg->message != WM_CHAR)) return FALSE;
      lpAccelTbl = HeapAlloc(GetProcessHeap(), 0, cAccelEntries * sizeof(ACCEL));
      if (NULL == lpAccelTbl)
      {
  	return FALSE;
      }
      if (CopyAcceleratorTableW(hAccel, lpAccelTbl, cAccelEntries) != cAccelEntries)
      {
  	WARN_(accel)("CopyAcceleratorTableW failed\n");
  	HeapFree(GetProcessHeap(), 0, lpAccelTbl);
  	return FALSE;
      }
  
      TRACE_(accel)("hAccel=%p, cAccelEntries=%d,"
  		"msg->hwnd=%p, msg->message=%04x, wParam=%08lx, lParam=%08lx\n",
  		hAccel, cAccelEntries,
  		lpMsg->hwnd, lpMsg->message, lpMsg->wParam, lpMsg->lParam);
      for(i = 0; i < cAccelEntries; i++)
      {
  	if(lpAccelTbl[i].key != lpMsg->wParam)
  	    continue;
  
  	if(lpMsg->message == WM_CHAR)
  	{
  	    if(!(lpAccelTbl[i].fVirt & FALT) && !(lpAccelTbl[i].fVirt & FVIRTKEY))
  	    {
  		TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(lpMsg->wParam) & 0xff);
  		goto found;
  	    }
  	}
  	else
  	{
  	    if(lpAccelTbl[i].fVirt & FVIRTKEY)
  	    {
  		INT mask = 0;
  		TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
  				lpMsg->wParam, HIWORD(lpMsg->lParam) & 0xff);
  		if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
  		if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
  		if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
  		if(mask == (lpAccelTbl[i].fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
  		TRACE_(accel)("incorrect SHIFT/CTRL/ALT-state\n");
  	    }
  	    else
  	    {
  		if(!(lpMsg->lParam & 0x01000000))  /* no special_key */
  		{
  		    if((lpAccelTbl[i].fVirt & FALT) && (lpMsg->lParam & 0x20000000))
  		    {						       /* ^^ ALT pressed */
  			TRACE_(accel)("found accel for Alt-%c\n", LOWORD(lpMsg->wParam) & 0xff);
  			goto found;
  		    }
  		}
  	    }
  	}
      }
  
      WARN_(accel)("couldn't translate accelerator key\n");
      HeapFree(GetProcessHeap(), 0, lpAccelTbl);
      return FALSE;
  
  found:
      if(lpwCmd) *lpwCmd = lpAccelTbl[i].cmd;
      HeapFree(GetProcessHeap(), 0, lpAccelTbl);
      return TRUE;
  }
  
  /***********************************************************************
   * ReleaseStgMedium [OLE32.@]
   */
  void WINAPI ReleaseStgMedium(
    STGMEDIUM* pmedium)
  {
    if (!pmedium) return;
  
    switch (pmedium->tymed)
    {
      case TYMED_HGLOBAL:
      {
        if ( (pmedium->pUnkForRelease==0) &&
  	   (pmedium->u.hGlobal!=0) )
  	GlobalFree(pmedium->u.hGlobal);
        break;
      }
      case TYMED_FILE:
      {
        if (pmedium->u.lpszFileName!=0)
        {
  	if (pmedium->pUnkForRelease==0)
  	{
  	  DeleteFileW(pmedium->u.lpszFileName);
  	}
  
  	CoTaskMemFree(pmedium->u.lpszFileName);
        }
        break;
      }
      case TYMED_ISTREAM:
      {
        if (pmedium->u.pstm!=0)
        {
  	IStream_Release(pmedium->u.pstm);
        }
        break;
      }
      case TYMED_ISTORAGE:
      {
        if (pmedium->u.pstg!=0)
        {
  	IStorage_Release(pmedium->u.pstg);
        }
        break;
      }
      case TYMED_GDI:
      {
        if ( (pmedium->pUnkForRelease==0) &&
  	   (pmedium->u.hBitmap!=0) )
  	DeleteObject(pmedium->u.hBitmap);
        break;
      }
      case TYMED_MFPICT:
      {
        if ( (pmedium->pUnkForRelease==0) &&
  	   (pmedium->u.hMetaFilePict!=0) )
        {
  	LPMETAFILEPICT pMP = GlobalLock(pmedium->u.hMetaFilePict);
  	DeleteMetaFile(pMP->hMF);
  	GlobalUnlock(pmedium->u.hMetaFilePict);
  	GlobalFree(pmedium->u.hMetaFilePict);
        }
        break;
      }
      case TYMED_ENHMF:
      {
        if ( (pmedium->pUnkForRelease==0) &&
  	   (pmedium->u.hEnhMetaFile!=0) )
        {
  	DeleteEnhMetaFile(pmedium->u.hEnhMetaFile);
        }
        break;
      }
      case TYMED_NULL:
      default:
        break;
    }
    pmedium->tymed=TYMED_NULL;
  
    /*
     * After cleaning up, the unknown is released
     */
    if (pmedium->pUnkForRelease!=0)
    {
      IUnknown_Release(pmedium->pUnkForRelease);
      pmedium->pUnkForRelease = 0;
    }
  }
  
  /***
   * OLEDD_Initialize()
   *
   * Initializes the OLE drag and drop data structures.
   */
  static void OLEDD_Initialize(void)
  {
      WNDCLASSW wndClass;
  
      ZeroMemory (&wndClass, sizeof(WNDCLASSW));
      wndClass.style         = CS_GLOBALCLASS;
      wndClass.lpfnWndProc   = OLEDD_DragTrackerWindowProc;
      wndClass.cbClsExtra    = 0;
      wndClass.cbWndExtra    = sizeof(TrackerWindowInfo*);
      wndClass.hCursor       = 0;
      wndClass.hbrBackground = 0;
      wndClass.lpszClassName = OLEDD_DRAGTRACKERCLASS;
  
      RegisterClassW (&wndClass);
  }
  
  /***
   * OLEDD_DragTrackerWindowProc()
   *
   * This method is the WindowProcedure of the drag n drop tracking
   * window. During a drag n Drop operation, an invisible window is created
   * to receive the user input and act upon it. This procedure is in charge
   * of this behavior.
   */
  
  #define DRAG_TIMER_ID 1
  
  static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
  			 HWND   hwnd,
  			 UINT   uMsg,
  			 WPARAM wParam,
  			 LPARAM   lParam)
  {
    switch (uMsg)
    {
      case WM_CREATE:
      {
        LPCREATESTRUCTA createStruct = (LPCREATESTRUCTA)lParam;
  
        SetWindowLongPtrW(hwnd, 0, (LONG_PTR)createStruct->lpCreateParams);
        SetTimer(hwnd, DRAG_TIMER_ID, 50, NULL);
  
        break;
      }
      case WM_TIMER:
      case WM_MOUSEMOVE:
      case WM_LBUTTONUP:
      case WM_MBUTTONUP:
      case WM_RBUTTONUP:
      case WM_LBUTTONDOWN:
      case WM_MBUTTONDOWN:
      case WM_RBUTTONDOWN:
      {
        TrackerWindowInfo *trackerInfo = (TrackerWindowInfo*)GetWindowLongPtrA(hwnd, 0);
        if (trackerInfo->trackingDone) break;
        OLEDD_TrackStateChange(trackerInfo);
        break;
      }
      case WM_DESTROY:
      {
        KillTimer(hwnd, DRAG_TIMER_ID);
        break;
      }
    }
  
    /*
     * This is a window proc after all. Let's call the default.
     */
    return DefWindowProcW (hwnd, uMsg, wParam, lParam);
  }
  
  static void drag_enter( TrackerWindowInfo *info, HWND new_target )
  {
      HRESULT hr;
  
      info->curTargetHWND = new_target;
  
      while (new_target && !is_droptarget( new_target ))
          new_target = GetParent( new_target );
  
      info->curDragTarget = get_droptarget_pointer( new_target );
  
      if (info->curDragTarget)
      {
          *info->pdwEffect = info->dwOKEffect;
          hr = IDropTarget_DragEnter( info->curDragTarget, info->dataObject,
                                      info->dwKeyState, info->curMousePos,
                                      info->pdwEffect );
          *info->pdwEffect &= info->dwOKEffect;
  
          /* failed DragEnter() means invalid target */
          if (hr != S_OK)
          {
              IDropTarget_Release( info->curDragTarget );
              info->curDragTarget = NULL;
              info->curTargetHWND = NULL;
          }
      }
  }
  
  static void drag_end( TrackerWindowInfo *info )
  {
      HRESULT hr;
  
      info->trackingDone = TRUE;
      ReleaseCapture();
  
      if (info->curDragTarget)
      {
          if (info->returnValue == DRAGDROP_S_DROP &&
              *info->pdwEffect != DROPEFFECT_NONE)
          {
              *info->pdwEffect = info->dwOKEffect;
              hr = IDropTarget_Drop( info->curDragTarget, info->dataObject, info->dwKeyState,
                                     info->curMousePos, info->pdwEffect );
              *info->pdwEffect &= info->dwOKEffect;
  
              if (FAILED( hr ))
                  info->returnValue = hr;
          }
          else
          {
              IDropTarget_DragLeave( info->curDragTarget );
              *info->pdwEffect = DROPEFFECT_NONE;
          }
          IDropTarget_Release( info->curDragTarget );
          info->curDragTarget = NULL;
      }
      else
          *info->pdwEffect = DROPEFFECT_NONE;
  }
  
  static HRESULT give_feedback( TrackerWindowInfo *info )
  {
      HRESULT hr;
      int res;
      HCURSOR cur;
  
      if (info->curDragTarget == NULL)
          *info->pdwEffect = DROPEFFECT_NONE;
  
      hr = IDropSource_GiveFeedback( info->dropSource, *info->pdwEffect );
  
      if (hr == DRAGDROP_S_USEDEFAULTCURSORS)
      {
          if (*info->pdwEffect & DROPEFFECT_MOVE)
              res = CURSOR_MOVE;
          else if (*info->pdwEffect & DROPEFFECT_COPY)
              res = CURSOR_COPY;
          else if (*info->pdwEffect & DROPEFFECT_LINK)
              res = CURSOR_LINK;
          else
              res = CURSOR_NODROP;
  
          cur = LoadCursorW( hProxyDll, MAKEINTRESOURCEW( res ) );
          SetCursor( cur );
      }
  
      return hr;
  }
  
  /***
   * OLEDD_TrackStateChange()
   *
   * This method is invoked while a drag and drop operation is in effect.
   *
   * params:
   *    trackerInfo - Pointer to the structure identifying the
   *                  drag & drop operation that is currently
   *                  active.
   */
  static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo)
  {
    HWND   hwndNewTarget = 0;
    POINT pt;
  
    /*
     * This method may be called from QueryContinueDrag again,
     * (i.e. by running message loop) so avoid recursive call chain.
     */
    if (trackerInfo->inTrackCall) return;
    trackerInfo->inTrackCall = TRUE;
  
    /*
     * Get the handle of the window under the mouse
     */
    pt.x = trackerInfo->curMousePos.x;
    pt.y = trackerInfo->curMousePos.y;
    hwndNewTarget = WindowFromPoint(pt);
  
    trackerInfo->returnValue = IDropSource_QueryContinueDrag(trackerInfo->dropSource,
                                                             trackerInfo->escPressed,
                                                             trackerInfo->dwKeyState);
  
    if (trackerInfo->curTargetHWND != hwndNewTarget &&
        (trackerInfo->returnValue == S_OK ||
         trackerInfo->returnValue == DRAGDROP_S_DROP))
    {
      if (trackerInfo->curDragTarget)
      {
        IDropTarget_DragLeave(trackerInfo->curDragTarget);
        IDropTarget_Release(trackerInfo->curDragTarget);
        trackerInfo->curDragTarget = NULL;
        trackerInfo->curTargetHWND = NULL;
      }
  
      if (hwndNewTarget)
        drag_enter( trackerInfo, hwndNewTarget );
  
      give_feedback( trackerInfo );
  
    }
  
    if (trackerInfo->returnValue == S_OK)
    {
      if (trackerInfo->curDragTarget)
      {
        *trackerInfo->pdwEffect = trackerInfo->dwOKEffect;
        IDropTarget_DragOver(trackerInfo->curDragTarget,
                             trackerInfo->dwKeyState,
                             trackerInfo->curMousePos,
                             trackerInfo->pdwEffect);
        *trackerInfo->pdwEffect &= trackerInfo->dwOKEffect;
      }
      give_feedback( trackerInfo );
    }
    else
      drag_end( trackerInfo );
  
    trackerInfo->inTrackCall = FALSE;
  }
  
  /***
   * OLEDD_GetButtonState()
   *
   * This method will use the current state of the keyboard to build
   * a button state mask equivalent to the one passed in the
   * WM_MOUSEMOVE wParam.
   */
  static DWORD OLEDD_GetButtonState(void)
  {
    BYTE  keyboardState[256];
    DWORD keyMask = 0;
  
    GetKeyboardState(keyboardState);
  
    if ( (keyboardState[VK_SHIFT] & 0x80) !=0)
      keyMask |= MK_SHIFT;
  
    if ( (keyboardState[VK_CONTROL] & 0x80) !=0)
      keyMask |= MK_CONTROL;
  
    if ( (keyboardState[VK_MENU] & 0x80) !=0)
      keyMask |= MK_ALT;
  
    if ( (keyboardState[VK_LBUTTON] & 0x80) !=0)
      keyMask |= MK_LBUTTON;
  
    if ( (keyboardState[VK_RBUTTON] & 0x80) !=0)
      keyMask |= MK_RBUTTON;
  
    if ( (keyboardState[VK_MBUTTON] & 0x80) !=0)
      keyMask |= MK_MBUTTON;
  
    return keyMask;
  }
  
  /***
   * OLEDD_GetButtonState()
   *
   * This method will read the default value of the registry key in
   * parameter and extract a DWORD value from it. The registry key value
   * can be in a string key or a DWORD key.
   *
   * params:
   *     regKey   - Key to read the default value from
   *     pdwValue - Pointer to the location where the DWORD
   *                value is returned. This value is not modified
   *                if the value is not found.
   */
  
  static void OLEUTL_ReadRegistryDWORDValue(
    HKEY   regKey,
    DWORD* pdwValue)
  {
    WCHAR buffer[20];
    DWORD cbData = sizeof(buffer);
    DWORD dwKeyType;
    LONG  lres;
  
    lres = RegQueryValueExW(regKey,
  			  emptyW,
  			  NULL,
  			  &dwKeyType,
  			  (LPBYTE)buffer,
  			  &cbData);
  
    if (lres==ERROR_SUCCESS)
    {
      switch (dwKeyType)
      {
        case REG_DWORD:
  	*pdwValue = *(DWORD*)buffer;
  	break;
        case REG_EXPAND_SZ:
        case REG_MULTI_SZ:
        case REG_SZ:
  	*pdwValue = wcstoul(buffer, NULL, 10);
  	break;
      }
    }
  }
  
  /******************************************************************************
   * OleDraw (OLE32.@)
   *
   * The operation of this function is documented literally in the WinAPI
   * documentation to involve a QueryInterface for the IViewObject interface,
   * followed by a call to IViewObject::Draw.
   */
  HRESULT WINAPI OleDraw(
  	IUnknown *pUnk,
  	DWORD dwAspect,
  	HDC hdcDraw,
  	LPCRECT rect)
  {
    HRESULT hres;
    IViewObject *viewobject;
  
    if (!pUnk) return E_INVALIDARG;
  
    hres = IUnknown_QueryInterface(pUnk,
  				 &IID_IViewObject,
  				 (void**)&viewobject);
    if (SUCCEEDED(hres))
    {
      hres = IViewObject_Draw(viewobject, dwAspect, -1, 0, 0, 0, hdcDraw, (RECTL*)rect, 0, 0, 0);
      IViewObject_Release(viewobject);
      return hres;
    }
    else
      return DV_E_NOIVIEWOBJECT;
  }
  
  /***********************************************************************
   *             OleTranslateAccelerator [OLE32.@]
   */
  HRESULT WINAPI OleTranslateAccelerator (LPOLEINPLACEFRAME lpFrame,
                     LPOLEINPLACEFRAMEINFO lpFrameInfo, LPMSG lpmsg)
  {
      WORD wID;
  
      TRACE("(%p,%p,%p)\n", lpFrame, lpFrameInfo, lpmsg);
  
      if (IsAccelerator(lpFrameInfo->haccel,lpFrameInfo->cAccelEntries,lpmsg,&wID))
          return IOleInPlaceFrame_TranslateAccelerator(lpFrame,lpmsg,wID);
  
      return S_FALSE;
  }
  
  /******************************************************************************
   *              OleCreate        [OLE32.@]
   *
   */
  HRESULT WINAPI OleCreate(
  	REFCLSID rclsid,
  	REFIID riid,
  	DWORD renderopt,
  	LPFORMATETC pFormatEtc,
  	LPOLECLIENTSITE pClientSite,
  	LPSTORAGE pStg,
  	LPVOID* ppvObj)
  {
      HRESULT hres;
      IUnknown * pUnk = NULL;
      IOleObject *pOleObject = NULL;
  
      TRACE("(%s, %s, %d, %p, %p, %p, %p)\n", debugstr_guid(rclsid),
          debugstr_guid(riid), renderopt, pFormatEtc, pClientSite, pStg, ppvObj);
  
      hres = CoCreateInstance(rclsid, 0, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER, riid, (LPVOID*)&pUnk);
  
      if (SUCCEEDED(hres))
          hres = IStorage_SetClass(pStg, rclsid);
  
      if (pClientSite && SUCCEEDED(hres))
      {
          hres = IUnknown_QueryInterface(pUnk, &IID_IOleObject, (LPVOID*)&pOleObject);
          if (SUCCEEDED(hres))
          {
              DWORD dwStatus;
              IOleObject_GetMiscStatus(pOleObject, DVASPECT_CONTENT, &dwStatus);
          }
      }
  
      if (SUCCEEDED(hres))
      {
          IPersistStorage * pPS;
          if (SUCCEEDED((hres = IUnknown_QueryInterface(pUnk, &IID_IPersistStorage, (LPVOID*)&pPS))))
          {
              TRACE("trying to set stg %p\n", pStg);
              hres = IPersistStorage_InitNew(pPS, pStg);
              TRACE("-- result 0x%08x\n", hres);
              IPersistStorage_Release(pPS);
          }
      }
  
      if (pClientSite && SUCCEEDED(hres))
      {
          TRACE("trying to set clientsite %p\n", pClientSite);
          hres = IOleObject_SetClientSite(pOleObject, pClientSite);
          TRACE("-- result 0x%08x\n", hres);
      }
  
      if (pOleObject)
          IOleObject_Release(pOleObject);
  
      if (((renderopt == OLERENDER_DRAW) || (renderopt == OLERENDER_FORMAT)) &&
          SUCCEEDED(hres))
      {
          hres = OleRun(pUnk);
          if (SUCCEEDED(hres))
          {
              IOleCache *pOleCache;
  
              if (SUCCEEDED(IUnknown_QueryInterface(pUnk, &IID_IOleCache, (void **)&pOleCache)))
              {
                  DWORD dwConnection;
                  if (renderopt == OLERENDER_DRAW && !pFormatEtc) {
                      FORMATETC pfe;
                      pfe.cfFormat = 0;
                      pfe.ptd = NULL;
                      pfe.dwAspect = DVASPECT_CONTENT;
                      pfe.lindex = -1;
                      pfe.tymed = TYMED_NULL;
                      hres = IOleCache_Cache(pOleCache, &pfe, ADVF_PRIMEFIRST, &dwConnection);
                  }
                  else
                      hres = IOleCache_Cache(pOleCache, pFormatEtc, ADVF_PRIMEFIRST, &dwConnection);
                  IOleCache_Release(pOleCache);
              }
          }
      }
  
      if (FAILED(hres) && pUnk)
      {
          IUnknown_Release(pUnk);
          pUnk = NULL;
      }
  
      *ppvObj = pUnk;
  
      TRACE("-- %p\n", pUnk);
      return hres;
  }
  
  /******************************************************************************
   *              OleGetAutoConvert        [OLE32.@]
   */
  HRESULT WINAPI OleGetAutoConvert(REFCLSID clsidOld, LPCLSID pClsidNew)
  {
      static const WCHAR wszAutoConvertTo[] = {'A','u','t','o','C','o','n','v','e','r','t','T','o',0};
      HKEY hkey = NULL;
      WCHAR buf[CHARS_IN_GUID];
      LONG len;
      HRESULT res = S_OK;
  
      res = COM_OpenKeyForCLSID(clsidOld, wszAutoConvertTo, KEY_READ, &hkey);
      if (FAILED(res))
          goto done;
  
      len = sizeof(buf);
      if (RegQueryValueW(hkey, NULL, buf, &len))
      {
          res = REGDB_E_KEYMISSING;
          goto done;
      }
      res = CLSIDFromString(buf, pClsidNew);
  done:
      if (hkey) RegCloseKey(hkey);
      return res;
  }
  
  /******************************************************************************
   *              OleSetAutoConvert        [OLE32.@]
   */
  HRESULT WINAPI OleSetAutoConvert(REFCLSID clsidOld, REFCLSID clsidNew)
  {
      static const WCHAR wszAutoConvertTo[] = {'A','u','t','o','C','o','n','v','e','r','t','T','o',0};
      HKEY hkey = NULL;
      WCHAR szClsidNew[CHARS_IN_GUID];
      HRESULT res = S_OK;
  
      TRACE("(%s,%s)\n", debugstr_guid(clsidOld), debugstr_guid(clsidNew));
      
      res = COM_OpenKeyForCLSID(clsidOld, NULL, KEY_READ | KEY_WRITE, &hkey);
      if (FAILED(res))
          goto done;
      StringFromGUID2(clsidNew, szClsidNew, CHARS_IN_GUID);
      if (RegSetValueW(hkey, wszAutoConvertTo, REG_SZ, szClsidNew, (lstrlenW(szClsidNew)+1) * sizeof(WCHAR)))
      {
          res = REGDB_E_WRITEREGDB;
  	goto done;
      }
  
  done:
      if (hkey) RegCloseKey(hkey);
      return res;
  }
  
  /******************************************************************************
   *              OleDoAutoConvert        [OLE32.@]
   */
  HRESULT WINAPI OleDoAutoConvert(LPSTORAGE pStg, LPCLSID pClsidNew)
  {
      WCHAR *user_type_old, *user_type_new;
      CLIPFORMAT cf;
      STATSTG stat;
      CLSID clsid;
      HRESULT hr;
  
      TRACE("(%p, %p)\n", pStg, pClsidNew);
  
      *pClsidNew = CLSID_NULL;
      if(!pStg)
          return E_INVALIDARG;
      hr = IStorage_Stat(pStg, &stat, STATFLAG_NONAME);
      if(FAILED(hr))
          return hr;
  
      *pClsidNew = stat.clsid;
      hr = OleGetAutoConvert(&stat.clsid, &clsid);
      if(FAILED(hr))
          return hr;
  
      hr = IStorage_SetClass(pStg, &clsid);
      if(FAILED(hr))
          return hr;
  
      hr = ReadFmtUserTypeStg(pStg, &cf, &user_type_old);
      if(FAILED(hr)) {
          cf = 0;
          user_type_new = NULL;
      }
  
      hr = OleRegGetUserType(&clsid, USERCLASSTYPE_FULL, &user_type_new);
      if(FAILED(hr))
          user_type_new = NULL;
  
      hr = WriteFmtUserTypeStg(pStg, cf, user_type_new);
      CoTaskMemFree(user_type_new);
      if(FAILED(hr))
      {
          CoTaskMemFree(user_type_old);
          IStorage_SetClass(pStg, &stat.clsid);
          return hr;
      }
  
      hr = SetConvertStg(pStg, TRUE);
      if(FAILED(hr))
      {
          WriteFmtUserTypeStg(pStg, cf, user_type_old);
          IStorage_SetClass(pStg, &stat.clsid);
      }
      else
          *pClsidNew = clsid;
      CoTaskMemFree(user_type_old);
      return hr;
  }
  
  /******************************************************************************
   *              OleIsRunning        [OLE32.@]
   */
  BOOL WINAPI OleIsRunning(LPOLEOBJECT object)
  {
      IRunnableObject *pRunnable;
      HRESULT hr;
      BOOL running;
  
      TRACE("(%p)\n", object);
  
      if (!object) return FALSE;
  
      hr = IOleObject_QueryInterface(object, &IID_IRunnableObject, (void **)&pRunnable);
      if (FAILED(hr))
          return TRUE;
      running = IRunnableObject_IsRunning(pRunnable);
      IRunnableObject_Release(pRunnable);
      return running;
  }
  
  /***********************************************************************
   *           OleNoteObjectVisible			    [OLE32.@]
   */
  HRESULT WINAPI OleNoteObjectVisible(LPUNKNOWN pUnknown, BOOL bVisible)
  {
      TRACE("(%p, %s)\n", pUnknown, bVisible ? "TRUE" : "FALSE");
      return CoLockObjectExternal(pUnknown, bVisible, TRUE);
  }
  
  /***********************************************************************
   *           PropSysAllocString			    [OLE32.@]
   * NOTES
   *  Forward to oleaut32.
   */
  BSTR WINAPI PropSysAllocString(LPCOLESTR str)
  {
      return SysAllocString(str);
  }
  
  /***********************************************************************
   *           PropSysFreeString			    [OLE32.@]
   * NOTES
   *  Forward to oleaut32.
   */
  void WINAPI PropSysFreeString(LPOLESTR str)
  {
      SysFreeString(str);
  }
  