| /* | 
 |  *	OLE2 library | 
 |  * | 
 |  *	Copyright 1995	Martin von Loewis | 
 |  *      Copyright 1999  Francis Beaudet | 
 |  *      Copyright 1999  Noel Borthwick  | 
 |  */ | 
 |  | 
 | #include <assert.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | #include "winuser.h" | 
 | #include "winerror.h" | 
 | #include "ole2.h" | 
 | #include "process.h" | 
 | #include "hook.h" | 
 | #include "commctrl.h" | 
 | #include "wine/obj_clientserver.h" | 
 | #include "wine/wingdi16.h" | 
 | #include "debugtools.h" | 
 | #include "ole2ver.h" | 
 | #include "winreg.h" | 
 |  | 
 | DEFAULT_DEBUG_CHANNEL(ole) | 
 |  | 
 | /****************************************************************************** | 
 |  * These are static/global variables and internal data structures that the  | 
 |  * OLE module uses to maintain it's state. | 
 |  */ | 
 | typedef struct tagDropTargetNode | 
 | { | 
 |   HWND          hwndTarget; | 
 |   IDropTarget*    dropTarget; | 
 |   struct tagDropTargetNode* prevDropTarget; | 
 |   struct tagDropTargetNode* nextDropTarget; | 
 | } DropTargetNode; | 
 |  | 
 | typedef struct tagTrackerWindowInfo | 
 | { | 
 |   IDataObject* dataObject; | 
 |   IDropSource* dropSource; | 
 |   DWORD        dwOKEffect; | 
 |   DWORD*       pdwEffect; | 
 |   BOOL       trackingDone; | 
 |   HRESULT      returnValue; | 
 |  | 
 |   BOOL       escPressed; | 
 |   HWND       curDragTargetHWND; | 
 |   IDropTarget* curDragTarget; | 
 | } 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 */ | 
 | } OleMenuHookItem; | 
 |  | 
 | /* | 
 |  * Dynamic pointer array of per thread message hooks (maintained by OleSetMenuDescriptor) | 
 |  */ | 
 | static HDPA OLEMenu_MsgHookDPA = NULL; | 
 |  | 
 | /* | 
 |  * This is the lock count on the OLE library. It is controlled by the | 
 |  * OLEInitialize/OLEUninitialize methods. | 
 |  */ | 
 | static ULONG OLE_moduleLockCount = 0; | 
 |  | 
 | /* | 
 |  * Name of our registered window class. | 
 |  */ | 
 | static const char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32"; | 
 |  | 
 | /* | 
 |  * This is the head of the Drop target container. | 
 |  */ | 
 | static DropTargetNode* targetListHead = NULL; | 
 |  | 
 | /****************************************************************************** | 
 |  * These are the prototypes of miscelaneous 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(); | 
 | static void OLEMenu_UnInitialize(); | 
 | BOOL OLEMenu_InstallHooks( DWORD tid ); | 
 | BOOL OLEMenu_UnInstallHooks( DWORD tid ); | 
 | OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook ); | 
 | static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos ); | 
 | BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor ); | 
 | LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam); | 
 | LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam); | 
 |  | 
 | /****************************************************************************** | 
 |  * These are the prototypes of the OLE Clipboard initialization methods (in clipboard.c) | 
 |  */ | 
 | void OLEClipbrd_UnInitialize(); | 
 | void OLEClipbrd_Initialize(); | 
 |  | 
 | /****************************************************************************** | 
 |  * These are the prototypes of the utility methods used for OLE Drag n Drop | 
 |  */ | 
 | static void            OLEDD_Initialize(); | 
 | static void            OLEDD_UnInitialize(); | 
 | static void            OLEDD_InsertDropTarget( | 
 | 			 DropTargetNode* nodeToAdd); | 
 | static DropTargetNode* OLEDD_ExtractDropTarget( | 
 |                          HWND hwndOfTarget); | 
 | static DropTargetNode* OLEDD_FindDropTarget( | 
 |                          HWND hwndOfTarget); | 
 | static LRESULT WINAPI  OLEDD_DragTrackerWindowProc( | 
 | 			 HWND   hwnd,  | 
 | 			 UINT   uMsg, | 
 | 			 WPARAM wParam,  | 
 | 			 LPARAM   lParam); | 
 | static void OLEDD_TrackMouseMove( | 
 |                          TrackerWindowInfo* trackerInfo, | 
 | 			 POINT            mousePos, | 
 | 			 DWORD              keyState); | 
 | static void OLEDD_TrackStateChange( | 
 |                          TrackerWindowInfo* trackerInfo, | 
 | 			 POINT            mousePos, | 
 | 			 DWORD              keyState); | 
 | static DWORD OLEDD_GetButtonState(); | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  *		OleBuildVersion	[OLE2.1] | 
 |  */ | 
 | DWORD WINAPI OleBuildVersion(void) | 
 | { | 
 |     TRACE("(void)\n"); | 
 |     return (rmm<<16)+rup; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           OleInitialize       (OLE2.2) (OLE32.108) | 
 |  */ | 
 | HRESULT WINAPI 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;     | 
 |  | 
 |   /* | 
 |    * Then, it has to initialize the OLE specific modules. | 
 |    * This includes: | 
 |    *     Clipboard | 
 |    *     Drag and Drop | 
 |    *     Object linking and Embedding | 
 |    *     In-place activation | 
 |    */ | 
 |   if (OLE_moduleLockCount==0) | 
 | { | 
 |     /*  | 
 |      * Initialize the libraries. | 
 |      */ | 
 |     TRACE("() - Initializing the OLE libraries\n"); | 
 |  | 
 |     /* | 
 |      * OLE Clipboard | 
 |      */ | 
 |     OLEClipbrd_Initialize(); | 
 |  | 
 |     /* | 
 |      * Drag and Drop | 
 |      */ | 
 |     OLEDD_Initialize(); | 
 |  | 
 |     /* | 
 |      * OLE shared menu | 
 |      */ | 
 |     OLEMenu_Initialize(); | 
 | } | 
 |  | 
 |   /* | 
 |    * Then, we increase the lock count on the OLE module. | 
 |    */ | 
 |   OLE_moduleLockCount++;   | 
 |  | 
 |   return hr; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *		CoGetCurrentProcess	[COMPOBJ.34] [OLE2.2][OLE32.108] | 
 |  * | 
 |  * NOTES | 
 |  *   Is DWORD really the correct return type for this function? | 
 |  */ | 
 | DWORD WINAPI CoGetCurrentProcess(void) { | 
 | 	return (DWORD)PROCESS_Current(); | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *		OleUninitialize	[OLE2.3] [OLE32.131] | 
 |  */ | 
 | void WINAPI OleUninitialize(void) | 
 | { | 
 |   TRACE("()\n"); | 
 |  | 
 |   /* | 
 |    * Decrease the lock count on the OLE module. | 
 |    */ | 
 |   OLE_moduleLockCount--; | 
 |  | 
 |   /* | 
 |    * If we hit the bottom of the lock stack, free the libraries. | 
 |    */ | 
 |   if (OLE_moduleLockCount==0) | 
 |   { | 
 |     /* | 
 |      * Actually free the libraries. | 
 |      */ | 
 |     TRACE("() - Freeing the last reference count\n"); | 
 |  | 
 |     /* | 
 |      * OLE Clipboard | 
 |      */ | 
 |     OLEClipbrd_UnInitialize(); | 
 |  | 
 |     /* | 
 |      * Drag and Drop | 
 |      */ | 
 |     OLEDD_UnInitialize(); | 
 |      | 
 |     /* | 
 |      * OLE shared menu | 
 |      */ | 
 |     OLEMenu_UnInitialize(); | 
 |   } | 
 |    | 
 |   /* | 
 |    * Then, uninitialize the COM libraries. | 
 |    */ | 
 |   CoUninitialize(); | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *		CoRegisterMessageFilter32	[OLE32.38] | 
 |  */ | 
 | HRESULT WINAPI CoRegisterMessageFilter( | 
 |     LPMESSAGEFILTER lpMessageFilter,	/* Pointer to interface */ | 
 |     LPMESSAGEFILTER *lplpMessageFilter	/* Indirect pointer to prior instance if non-NULL */ | 
 | ) { | 
 |     FIXME("stub\n"); | 
 |     if (lplpMessageFilter) { | 
 | 	*lplpMessageFilter = NULL; | 
 |     } | 
 |     return S_OK; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *		OleInitializeWOW	[OLE32.109] | 
 |  */ | 
 | HRESULT WINAPI OleInitializeWOW(DWORD x) { | 
 |         FIXME("(0x%08lx),stub!\n",x); | 
 |         return 0; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           RegisterDragDrop16 (OLE2.35) | 
 |  */ | 
 | HRESULT WINAPI RegisterDragDrop16( | 
 | 	HWND16 hwnd, | 
 | 	LPDROPTARGET pDropTarget | 
 | ) { | 
 | 	FIXME("(0x%04x,%p),stub!\n",hwnd,pDropTarget); | 
 | 	return S_OK; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           RegisterDragDrop32 (OLE32.139) | 
 |  */ | 
 | HRESULT WINAPI RegisterDragDrop( | 
 | 	HWND hwnd, | 
 | 	LPDROPTARGET pDropTarget)  | 
 | { | 
 |   DropTargetNode* dropTargetInfo; | 
 |  | 
 |   TRACE("(0x%x,%p)\n", hwnd, pDropTarget); | 
 |  | 
 |   /* | 
 |    * First, check if the window is already registered. | 
 |    */ | 
 |   dropTargetInfo = OLEDD_FindDropTarget(hwnd); | 
 |  | 
 |   if (dropTargetInfo!=NULL) | 
 |     return DRAGDROP_E_ALREADYREGISTERED; | 
 |  | 
 |   /* | 
 |    * If it's not there, we can add it. We first create a node for it. | 
 |    */ | 
 |   dropTargetInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(DropTargetNode)); | 
 |  | 
 |   if (dropTargetInfo==NULL) | 
 |     return E_OUTOFMEMORY; | 
 |  | 
 |   dropTargetInfo->hwndTarget     = hwnd; | 
 |   dropTargetInfo->prevDropTarget = NULL; | 
 |   dropTargetInfo->nextDropTarget = NULL; | 
 |  | 
 |   /* | 
 |    * Don't forget that this is an interface pointer, need to nail it down since | 
 |    * we keep a copy of it. | 
 |    */ | 
 |   dropTargetInfo->dropTarget  = pDropTarget; | 
 |   IDropTarget_AddRef(dropTargetInfo->dropTarget); | 
 |    | 
 |   OLEDD_InsertDropTarget(dropTargetInfo); | 
 |  | 
 | 	return S_OK; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           RevokeDragDrop16 (OLE2.36) | 
 |  */ | 
 | HRESULT WINAPI RevokeDragDrop16( | 
 | 	HWND16 hwnd | 
 | ) { | 
 | 	FIXME("(0x%04x),stub!\n",hwnd); | 
 | 	return S_OK; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           RevokeDragDrop32 (OLE32.141) | 
 |  */ | 
 | HRESULT WINAPI RevokeDragDrop( | 
 | 	HWND hwnd) | 
 | { | 
 |   DropTargetNode* dropTargetInfo; | 
 |  | 
 |   TRACE("(0x%x)\n", hwnd); | 
 |  | 
 |   /* | 
 |    * First, check if the window is already registered. | 
 |    */ | 
 |   dropTargetInfo = OLEDD_ExtractDropTarget(hwnd); | 
 |  | 
 |   /* | 
 |    * If it ain't in there, it's an error. | 
 |    */ | 
 |   if (dropTargetInfo==NULL) | 
 |     return DRAGDROP_E_NOTREGISTERED; | 
 |  | 
 |   /* | 
 |    * If it's in there, clean-up it's used memory and | 
 |    * references | 
 |    */ | 
 |   IDropTarget_Release(dropTargetInfo->dropTarget); | 
 |   HeapFree(GetProcessHeap(), 0, dropTargetInfo);   | 
 |  | 
 | 	return S_OK; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           OleRegGetUserType (OLE32.122) | 
 |  * | 
 |  * This implementation of OleRegGetUserType ignores the dwFormOfType | 
 |  * parameter and always returns the full name of the object. This is | 
 |  * not too bad since this is the case for many objects because of the | 
 |  * way they are registered. | 
 |  */ | 
 | HRESULT WINAPI OleRegGetUserType(  | 
 | 	REFCLSID clsid,  | 
 | 	DWORD dwFormOfType, | 
 | 	LPOLESTR* pszUserType) | 
 | { | 
 |   char  xclsid[50]; | 
 |   char  keyName[60]; | 
 |   DWORD dwKeyType; | 
 |   DWORD cbData; | 
 |   HKEY  clsidKey; | 
 |   LONG  hres; | 
 |  | 
 |   /* | 
 |    * Initialize the out parameter. | 
 |    */ | 
 |   *pszUserType = NULL; | 
 |  | 
 |   /* | 
 |    * Build the key name we're looking for | 
 |    */ | 
 |   WINE_StringFromCLSID((LPCLSID)clsid, xclsid); | 
 |  | 
 |   strcpy(keyName, "CLSID\\"); | 
 |   strcat(keyName, xclsid); | 
 |   strcat(keyName, "\\"); | 
 |  | 
 |   TRACE("(%s, %ld, %p)\n", keyName, dwFormOfType, pszUserType); | 
 |  | 
 |   /* | 
 |    * Open the class id Key | 
 |    */ | 
 |   hres = RegOpenKeyA(HKEY_CLASSES_ROOT, | 
 | 		     keyName, | 
 | 		     &clsidKey); | 
 |  | 
 |   if (hres != ERROR_SUCCESS) | 
 |     return REGDB_E_CLASSNOTREG; | 
 |  | 
 |   /* | 
 |    * Retrieve the size of the name string. | 
 |    */ | 
 |   cbData = 0; | 
 |  | 
 |   hres = RegQueryValueExA(clsidKey, | 
 | 			  "", | 
 | 			  NULL, | 
 | 			  &dwKeyType, | 
 | 			  NULL, | 
 | 			  &cbData); | 
 |  | 
 |   if (hres!=ERROR_SUCCESS) | 
 |   { | 
 |     RegCloseKey(clsidKey); | 
 |     return REGDB_E_READREGDB; | 
 |   } | 
 |  | 
 |   /* | 
 |    * Allocate a buffer for the registry value. | 
 |    */ | 
 |   *pszUserType = CoTaskMemAlloc(cbData); | 
 |  | 
 |   if (*pszUserType==NULL) | 
 |   { | 
 |     RegCloseKey(clsidKey); | 
 |     return E_OUTOFMEMORY; | 
 |   } | 
 |  | 
 |   hres = RegQueryValueExA(HKEY_CLASSES_ROOT, | 
 | 			  "", | 
 | 			  NULL, | 
 | 			  &dwKeyType, | 
 | 			  (LPBYTE)*pszUserType, | 
 | 			  &cbData); | 
 |  | 
 |   RegCloseKey(clsidKey); | 
 |    | 
 |   if (hres!=ERROR_SUCCESS) | 
 |   { | 
 |     CoTaskMemFree(*pszUserType); | 
 |     *pszUserType=NULL; | 
 |  | 
 |     return REGDB_E_READREGDB; | 
 |   } | 
 |  | 
 |   return S_OK; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  * DoDragDrop32 [OLE32.65] | 
 |  */ | 
 | HRESULT WINAPI DoDragDrop ( | 
 |   IDataObject *pDataObject,  /* ptr to the data obj           */ | 
 |   IDropSource* pDropSource,  /* ptr to the source obj         */ | 
 |   DWORD       dwOKEffect,    /* effects allowed by the source */ | 
 |   DWORD       *pdwEffect)    /* ptr to effects of the source  */ | 
 | { | 
 |   TrackerWindowInfo trackerInfo; | 
 |   HWND            hwndTrackWindow; | 
 |   MSG             msg; | 
 |  | 
 |   TRACE("(DataObject %p, DropSource %p)\n", pDataObject, pDropSource); | 
 |  | 
 |   /* | 
 |    * Setup the drag n drop tracking window. | 
 |    */ | 
 |   trackerInfo.dataObject        = pDataObject; | 
 |   trackerInfo.dropSource        = pDropSource; | 
 |   trackerInfo.dwOKEffect        = dwOKEffect; | 
 |   trackerInfo.pdwEffect         = pdwEffect; | 
 |   trackerInfo.trackingDone      = FALSE; | 
 |   trackerInfo.escPressed        = FALSE; | 
 |   trackerInfo.curDragTargetHWND = 0; | 
 |   trackerInfo.curDragTarget     = 0; | 
 |  | 
 |   hwndTrackWindow = CreateWindowA(OLEDD_DRAGTRACKERCLASS, | 
 | 				    "TrackerWindow", | 
 | 				    WS_POPUP, | 
 | 				    CW_USEDEFAULT, CW_USEDEFAULT, | 
 | 				    CW_USEDEFAULT, CW_USEDEFAULT, | 
 | 				    0, | 
 | 				    0, | 
 | 				    0, | 
 | 				    (LPVOID)&trackerInfo); | 
 |  | 
 |   if (hwndTrackWindow!=0) | 
 |   { | 
 |     /* | 
 |      * Capture the mouse input | 
 |      */ | 
 |     SetCapture(hwndTrackWindow); | 
 |  | 
 |     /* | 
 |      * Pump messages. All mouse input should go the the capture window. | 
 |      */ | 
 |     while (!trackerInfo.trackingDone && GetMessageA(&msg, 0, 0, 0) ) | 
 |     { | 
 |       if ( (msg.message >= WM_KEYFIRST) &&  | 
 | 	   (msg.message <= WM_KEYFIRST) ) | 
 |       { | 
 | 	/* | 
 | 	 * 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, | 
 | 			       msg.pt, | 
 | 			       OLEDD_GetButtonState()); | 
 |       } | 
 |       else | 
 |       { | 
 | 	/* | 
 | 	 * Dispatch the messages only when it's not a keyboard message. | 
 | 	 */ | 
 | 	DispatchMessageA(&msg); | 
 |       } | 
 |     } | 
 |  | 
 |     /* | 
 |      * Destroy the temporary window. | 
 |      */ | 
 |     DestroyWindow(hwndTrackWindow); | 
 |  | 
 |     return trackerInfo.returnValue; | 
 |   } | 
 |  | 
 |   return E_FAIL; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  * OleQueryLinkFromData32 [OLE32.118] | 
 |  */ | 
 | HRESULT WINAPI OleQueryLinkFromData( | 
 |   IDataObject* pSrcDataObject) | 
 | { | 
 |   FIXME("(%p),stub!\n", pSrcDataObject); | 
 |   return S_OK; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  * OleRegGetMiscStatus [OLE32.121] | 
 |  */ | 
 | HRESULT WINAPI OleRegGetMiscStatus( | 
 |   REFCLSID clsid, | 
 |   DWORD    dwAspect, | 
 |   DWORD*   pdwStatus) | 
 | { | 
 |   char    xclsid[50]; | 
 |   char    keyName[60]; | 
 |   HKEY    clsidKey; | 
 |   HKEY    miscStatusKey; | 
 |   HKEY    aspectKey; | 
 |   LONG    result; | 
 |  | 
 |   /* | 
 |    * Initialize the out parameter. | 
 |    */ | 
 |   *pdwStatus = 0; | 
 |  | 
 |   /* | 
 |    * Build the key name we're looking for | 
 |    */ | 
 |   WINE_StringFromCLSID((LPCLSID)clsid, xclsid); | 
 |  | 
 |   strcpy(keyName, "CLSID\\"); | 
 |   strcat(keyName, xclsid); | 
 |   strcat(keyName, "\\"); | 
 |  | 
 |   TRACE("(%s, %ld, %p)\n", keyName, dwAspect, pdwStatus); | 
 |  | 
 |   /* | 
 |    * Open the class id Key | 
 |    */ | 
 |   result = RegOpenKeyA(HKEY_CLASSES_ROOT, | 
 | 		       keyName, | 
 | 		       &clsidKey); | 
 |  | 
 |   if (result != ERROR_SUCCESS) | 
 |     return REGDB_E_CLASSNOTREG; | 
 |  | 
 |   /* | 
 |    * Get the MiscStatus | 
 |    */ | 
 |   result = RegOpenKeyA(clsidKey, | 
 | 		       "MiscStatus", | 
 | 		       &miscStatusKey); | 
 |  | 
 |    | 
 |   if (result != ERROR_SUCCESS) | 
 |   { | 
 |     RegCloseKey(clsidKey); | 
 |     return REGDB_E_READREGDB; | 
 |   } | 
 |  | 
 |   /* | 
 |    * Read the default value | 
 |    */ | 
 |   OLEUTL_ReadRegistryDWORDValue(miscStatusKey, pdwStatus); | 
 |  | 
 |   /* | 
 |    * Open the key specific to the requested aspect. | 
 |    */ | 
 |   sprintf(keyName, "%ld", dwAspect); | 
 |  | 
 |   result = RegOpenKeyA(miscStatusKey, | 
 | 		       keyName, | 
 | 		       &aspectKey); | 
 |    | 
 |   if (result == ERROR_SUCCESS) | 
 |   { | 
 |     OLEUTL_ReadRegistryDWORDValue(aspectKey, pdwStatus); | 
 |     RegCloseKey(aspectKey); | 
 |   } | 
 |  | 
 |   /* | 
 |    * Cleanup | 
 |    */ | 
 |   RegCloseKey(miscStatusKey); | 
 |   RegCloseKey(clsidKey); | 
 |  | 
 |   return S_OK; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *              OleSetContainedObject        [OLE32.128] | 
 |  */ | 
 | HRESULT WINAPI OleSetContainedObject( | 
 |   LPUNKNOWN pUnknown,  | 
 |   BOOL      fContained) | 
 | { | 
 |   IRunnableObject* runnable = NULL; | 
 |   HRESULT          hres; | 
 |  | 
 |   TRACE("(%p,%x), stub!\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; | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  *              OleLoad        [OLE32.112] | 
 |  */ | 
 | HRESULT WINAPI OleLoad( | 
 |   LPSTORAGE       pStg,  | 
 |   REFIID          riid,  | 
 |   LPOLECLIENTSITE pClientSite,  | 
 |   LPVOID*         ppvObj) | 
 | { | 
 |   IPersistStorage* persistStorage = NULL; | 
 |   IOleObject*      oleObject      = NULL; | 
 |   STATSTG          storageInfo; | 
 |   HRESULT          hres; | 
 |  | 
 |   TRACE("(%p,%p,%p,%p)\n", pStg, riid, pClientSite, ppvObj); | 
 |    | 
 |   /* | 
 |    * TODO, Conversion ... OleDoAutoConvert | 
 |    */ | 
 |  | 
 |   /* | 
 |    * Get the class ID for the object. | 
 |    */ | 
 |   hres = IStorage_Stat(pStg, &storageInfo, STATFLAG_NONAME); | 
 |  | 
 |   /* | 
 |    * Now, try and create the handler for the object | 
 |    */ | 
 |   hres = CoCreateInstance(&storageInfo.clsid, | 
 | 			  NULL, | 
 | 			  CLSCTX_INPROC_HANDLER, | 
 | 			  &IID_IOleObject, | 
 | 			  (void**)&oleObject); | 
 |  | 
 |   /* | 
 |    * If that fails, as it will most times, load the default | 
 |    * OLE handler. | 
 |    */ | 
 |   if (FAILED(hres)) | 
 |   { | 
 |     hres = OleCreateDefaultHandler(&storageInfo.clsid, | 
 | 				   NULL, | 
 | 				   &IID_IOleObject, | 
 | 				   (void**)&oleObject); | 
 |   } | 
 |  | 
 |   /* | 
 |    * If we couldn't find a handler... this is bad. Abort the whole thing. | 
 |    */ | 
 |   if (FAILED(hres)) | 
 |     return hres; | 
 |  | 
 |   /* | 
 |    * Inform the new object of it's client site. | 
 |    */ | 
 |   hres = IOleObject_SetClientSite(oleObject, pClientSite); | 
 |  | 
 |   /* | 
 |    * Initialize the object with it's IPersistStorage interface. | 
 |    */ | 
 |   hres = IOleObject_QueryInterface(oleObject, | 
 | 				   &IID_IPersistStorage, | 
 | 				   (void**)&persistStorage); | 
 |  | 
 |   if (SUCCEEDED(hres))  | 
 |   { | 
 |     IPersistStorage_Load(persistStorage, pStg); | 
 |  | 
 |     IPersistStorage_Release(persistStorage); | 
 |     persistStorage = NULL; | 
 |   } | 
 |  | 
 |   /* | 
 |    * Return the requested interface to the caller. | 
 |    */ | 
 |   hres = IOleObject_QueryInterface(oleObject, riid, ppvObj); | 
 |  | 
 |   /* | 
 |    * Cleanup interfaces used internally | 
 |    */ | 
 |   IOleObject_Release(oleObject); | 
 |  | 
 |   return hres; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           OleSave     [OLE32.124] | 
 |  */ | 
 | 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; | 
 | } | 
 |  | 
 |  | 
 | /************************************************************************** | 
 |  * 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() | 
 | { | 
 |   /* Create a dynamic pointer array to store the hook handles */ | 
 |   if ( !OLEMenu_MsgHookDPA ) | 
 |     OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() ); | 
 | } | 
 |  | 
 | /*** | 
 |  * OLEMenu_UnInitialize() | 
 |  * | 
 |  * Releases the OLEMENU data structures. | 
 |  */ | 
 | static void OLEMenu_UnInitialize() | 
 | { | 
 |   /* Release the hook table */ | 
 |   if ( OLEMenu_MsgHookDPA ) | 
 |     DPA_Destroy( OLEMenu_MsgHookDPA ); | 
 |  | 
 |   OLEMenu_MsgHookDPA = NULL; | 
 | } | 
 |  | 
 | /************************************************************************* | 
 |  * OLEMenu_InstallHooks | 
 |  * Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC | 
 |  * | 
 |  * RETURNS: TRUE if message hooks were succesfully installed | 
 |  *          FALSE on failure | 
 |  */ | 
 | BOOL OLEMenu_InstallHooks( DWORD tid ) | 
 | { | 
 |   OleMenuHookItem *pHookItem = NULL; | 
 |  | 
 |   if ( !OLEMenu_MsgHookDPA ) /* No hook table? Create one */ | 
 |   { | 
 |     /* Create a dynamic pointer array to store the hook handles */ | 
 |     if ( !(OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() )) )  | 
 |       return FALSE; | 
 |   } | 
 |  | 
 |   /* Create an entry for the hook table */ | 
 |   if ( !(pHookItem = HeapAlloc(GetProcessHeap(), 0, | 
 |                                sizeof(OleMenuHookItem)) ) ) | 
 |     return FALSE; | 
 |  | 
 |   pHookItem->tid = tid; | 
 |   pHookItem->hHeap = GetProcessHeap(); | 
 |    | 
 |   /* Install a thread scope message hook for WH_GETMESSAGE */ | 
 |   pHookItem->GetMsg_hHook = SetWindowsHookExA( WH_GETMESSAGE, OLEMenu_GetMsgProc, | 
 |                                                0, GetCurrentThreadId() ); | 
 |   if ( !pHookItem->GetMsg_hHook ) | 
 |     goto CLEANUP; | 
 |  | 
 |   /* Install a thread scope message hook for WH_CALLWNDPROC */ | 
 |   pHookItem->CallWndProc_hHook = SetWindowsHookExA( WH_CALLWNDPROC, OLEMenu_CallWndProc, | 
 |                                                     0, GetCurrentThreadId() ); | 
 |   if ( !pHookItem->CallWndProc_hHook ) | 
 |     goto CLEANUP; | 
 |  | 
 |   /* Insert the hook table entry */ | 
 |   if ( -1 == DPA_InsertPtr( OLEMenu_MsgHookDPA, 0, pHookItem ) ) | 
 |     goto CLEANUP; | 
 |    | 
 |   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 succesfully installed | 
 |  *          FALSE on failure | 
 |  */ | 
 | BOOL OLEMenu_UnInstallHooks( DWORD tid ) | 
 | { | 
 |   INT ixHook; | 
 |   OleMenuHookItem *pHookItem = NULL; | 
 |  | 
 |   if ( !OLEMenu_MsgHookDPA )  /* No hooks set */ | 
 |     return TRUE; | 
 |  | 
 |   /* Lookup the hHook index for this tid */ | 
 |   if ( !OLEMenu_IsHookInstalled( tid , &ixHook ) ) | 
 |     return TRUE; | 
 |  | 
 |   /* Remove the hook entry from the table(the pointer itself is not deleted) */ | 
 |   if ( !( pHookItem = DPA_DeletePtr(OLEMenu_MsgHookDPA, ixHook) ) ) | 
 |     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 */ | 
 |   if (pHookItem) | 
 |     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 | 
 |  */ | 
 | OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook ) | 
 | { | 
 |   INT ixHook; | 
 |   OleMenuHookItem *pHookItem = NULL; | 
 |  | 
 |   if ( pixHook ) | 
 |     *pixHook = -1; | 
 |    | 
 |   if ( !OLEMenu_MsgHookDPA )  /* No hooks set */ | 
 |     return NULL; | 
 |  | 
 |   /* 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( ixHook = 0; ; ixHook++ ) | 
 |   { | 
 |     /* Retrieve the hook entry */ | 
 |     if ( !( pHookItem = DPA_GetPtr(OLEMenu_MsgHookDPA, ixHook) ) ) | 
 |       return NULL; | 
 |  | 
 |     if ( tid == pHookItem->tid ) | 
 |     { | 
 |       if ( pixHook ) | 
 |         *pixHook = ixHook; | 
 |       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 ) | 
 | { | 
 |   UINT 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 | 
 |  *          FASE if the popup menu is part of a container owned group | 
 |  */ | 
 | 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 thru 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) ? TRUE : FALSE; | 
 |       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. | 
 |  */ | 
 | LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam) | 
 | { | 
 |   LPCWPSTRUCT pMsg = NULL; | 
 |   HOLEMENU hOleMenu = 0; | 
 |   OleMenuDescriptor *pOleMenuDescriptor = NULL; | 
 |   OleMenuHookItem *pHookItem = NULL; | 
 |   WORD fuFlags; | 
 |    | 
 |   TRACE("%i, %04x, %08x\n", code, wParam, (unsigned)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 = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" ); | 
 |   if ( !hOleMenu ) | 
 |     goto NEXTHOOK; | 
 |  | 
 |   /* Get the menu descriptor */ | 
 |   pOleMenuDescriptor = (OleMenuDescriptor *) 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 */ | 
 |       SendMessageA( 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 ) | 
 |   { | 
 |     SendMessageA( 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(), NULL ) ) ) | 
 |   { | 
 |     /* 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. | 
 |  */ | 
 | LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam) | 
 | { | 
 |   LPMSG pMsg = NULL; | 
 |   HOLEMENU hOleMenu = 0; | 
 |   OleMenuDescriptor *pOleMenuDescriptor = NULL; | 
 |   OleMenuHookItem *pHookItem = NULL; | 
 |   WORD wCode; | 
 |    | 
 |   TRACE("%i, %04x, %08x\n", code, wParam, (unsigned)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 = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" ); | 
 |   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 = (OleMenuDescriptor *) 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(), NULL ) ) ) | 
 |   { | 
 |     /* 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.97] | 
 |  * 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 = (OleMenuDescriptor *) 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.99] | 
 |  * Destroy the shared menu descriptor | 
 |  */ | 
 | HRESULT WINAPI OleDestroyMenuDescriptor( | 
 |   HOLEMENU hmenuDescriptor) | 
 | { | 
 |   if ( hmenuDescriptor ) | 
 |     GlobalFree( hmenuDescriptor ); | 
 | 	return S_OK; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  * OleSetMenuDescriptor [OLE32.129] | 
 |  * Installs or removes OLE dispatching code for the containers frame window | 
 |  * 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. | 
 |  *  | 
 |  * 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 | 
 |  */ | 
 | 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("(%x, %x, %x, %p, %p), Context sensitive help filtering not implemented!\n", | 
 | 	(unsigned int)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(), NULL ) ) | 
 |   return E_FAIL; | 
 |          | 
 |     /* Get the menu descriptor */ | 
 |     pOleMenuDescriptor = (OleMenuDescriptor *) 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 */ | 
 |     SetPropA( hwndFrame, "PROP_OLEMenuDescriptor", 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 */ | 
 |     RemovePropA( hwndFrame, "PROP_OLEMenuDescriptor" ); | 
 |   } | 
 |        | 
 |   return S_OK; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  * ReleaseStgMedium [OLE32.140] | 
 |  */ | 
 | void WINAPI ReleaseStgMedium( | 
 |   STGMEDIUM* pmedium) | 
 | { | 
 |   switch (pmedium->tymed) | 
 |   { | 
 |     case TYMED_HGLOBAL: | 
 |     { | 
 |       if ( (pmedium->pUnkForRelease==0) &&  | 
 | 	   (pmedium->u.hGlobal!=0) ) | 
 | 	GlobalFree(pmedium->u.hGlobal); | 
 |  | 
 |       pmedium->u.hGlobal = 0; | 
 |       break; | 
 |     } | 
 |     case TYMED_FILE: | 
 |     { | 
 |       if (pmedium->u.lpszFileName!=0) | 
 |       { | 
 | 	if (pmedium->pUnkForRelease==0) | 
 | 	{ | 
 | 	  DeleteFileW(pmedium->u.lpszFileName); | 
 | 	} | 
 | 	 | 
 | 	CoTaskMemFree(pmedium->u.lpszFileName); | 
 |       } | 
 |  | 
 |       pmedium->u.lpszFileName = 0; | 
 |       break; | 
 |     } | 
 |     case TYMED_ISTREAM: | 
 |     { | 
 |       if (pmedium->u.pstm!=0) | 
 |       { | 
 | 	IStream_Release(pmedium->u.pstm); | 
 |       } | 
 |  | 
 |       pmedium->u.pstm = 0; | 
 |       break; | 
 |     } | 
 |     case TYMED_ISTORAGE: | 
 |     { | 
 |       if (pmedium->u.pstg!=0) | 
 |       { | 
 | 	IStorage_Release(pmedium->u.pstg); | 
 |       } | 
 |  | 
 |       pmedium->u.pstg = 0; | 
 |       break; | 
 |     } | 
 |     case TYMED_GDI: | 
 |     { | 
 |       if ( (pmedium->pUnkForRelease==0) &&  | 
 | 	   (pmedium->u.hGlobal!=0) ) | 
 | 	DeleteObject(pmedium->u.hGlobal); | 
 |  | 
 |       pmedium->u.hGlobal = 0; | 
 |       break; | 
 |     } | 
 |     case TYMED_MFPICT: | 
 |     { | 
 |       if ( (pmedium->pUnkForRelease==0) &&  | 
 | 	   (pmedium->u.hMetaFilePict!=0) ) | 
 |       { | 
 | 	DeleteMetaFile(pmedium->u.hMetaFilePict); | 
 | 	GlobalFree(pmedium->u.hMetaFilePict); | 
 |       } | 
 |  | 
 |       pmedium->u.hMetaFilePict = 0; | 
 |       break; | 
 |     } | 
 |     case TYMED_ENHMF: | 
 |     { | 
 |       if ( (pmedium->pUnkForRelease==0) &&  | 
 | 	   (pmedium->u.hEnhMetaFile!=0) ) | 
 |       { | 
 | 	DeleteEnhMetaFile(pmedium->u.hEnhMetaFile); | 
 |       } | 
 |  | 
 |       pmedium->u.hEnhMetaFile = 0; | 
 |       break; | 
 |     } | 
 |     case TYMED_NULL: | 
 |     default: | 
 |       break; | 
 |   } | 
 |  | 
 |   /* | 
 |    * 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() | 
 | { | 
 |     WNDCLASSA wndClass; | 
 |  | 
 |     ZeroMemory (&wndClass, sizeof(WNDCLASSA)); | 
 |     wndClass.style         = CS_GLOBALCLASS; | 
 |     wndClass.lpfnWndProc   = (WNDPROC)OLEDD_DragTrackerWindowProc; | 
 |     wndClass.cbClsExtra    = 0; | 
 |     wndClass.cbWndExtra    = sizeof(TrackerWindowInfo*); | 
 |     wndClass.hCursor       = 0; | 
 |     wndClass.hbrBackground = 0; | 
 |     wndClass.lpszClassName = OLEDD_DRAGTRACKERCLASS; | 
 |   | 
 |     RegisterClassA (&wndClass); | 
 | } | 
 |  | 
 | /*** | 
 |  * OLEDD_UnInitialize() | 
 |  * | 
 |  * Releases the OLE drag and drop data structures. | 
 |  */ | 
 | static void OLEDD_UnInitialize() | 
 | { | 
 |   /* | 
 |    * Simply empty the list. | 
 |    */ | 
 |   while (targetListHead!=NULL) | 
 |   { | 
 |     RevokeDragDrop(targetListHead->hwndTarget); | 
 |   } | 
 | } | 
 |  | 
 | /*** | 
 |  * OLEDD_InsertDropTarget() | 
 |  * | 
 |  * Insert the target node in the tree. | 
 |  */ | 
 | static void OLEDD_InsertDropTarget(DropTargetNode* nodeToAdd) | 
 | { | 
 |   DropTargetNode*  curNode; | 
 |   DropTargetNode** parentNodeLink; | 
 |  | 
 |   /* | 
 |    * Iterate the tree to find the insertion point. | 
 |    */ | 
 |   curNode        = targetListHead; | 
 |   parentNodeLink = &targetListHead; | 
 |  | 
 |   while (curNode!=NULL) | 
 |   { | 
 |     if (nodeToAdd->hwndTarget<curNode->hwndTarget) | 
 |     { | 
 |       /* | 
 |        * If the node we want to add has a smaller HWND, go left | 
 |        */ | 
 |       parentNodeLink = &curNode->prevDropTarget; | 
 |       curNode        =  curNode->prevDropTarget; | 
 |     } | 
 |     else if (nodeToAdd->hwndTarget>curNode->hwndTarget) | 
 |     { | 
 |       /* | 
 |        * If the node we want to add has a larger HWND, go right | 
 |        */ | 
 |       parentNodeLink = &curNode->nextDropTarget; | 
 |       curNode        =  curNode->nextDropTarget; | 
 |     } | 
 |     else | 
 |     { | 
 |       /* | 
 |        * The item was found in the list. It shouldn't have been there | 
 |        */ | 
 |       assert(FALSE); | 
 |       return; | 
 |     } | 
 |   } | 
 |  | 
 |   /* | 
 |    * If we get here, we have found a spot for our item. The parentNodeLink | 
 |    * pointer points to the pointer that we have to modify.  | 
 |    * The curNode should be NULL. We just have to establish the link and Voila! | 
 |    */ | 
 |   assert(curNode==NULL); | 
 |   assert(parentNodeLink!=NULL); | 
 |   assert(*parentNodeLink==NULL); | 
 |  | 
 |   *parentNodeLink=nodeToAdd; | 
 | } | 
 |  | 
 | /*** | 
 |  * OLEDD_ExtractDropTarget() | 
 |  * | 
 |  * Removes the target node from the tree. | 
 |  */ | 
 | static DropTargetNode* OLEDD_ExtractDropTarget(HWND hwndOfTarget) | 
 | { | 
 |   DropTargetNode*  curNode; | 
 |   DropTargetNode** parentNodeLink; | 
 |  | 
 |   /* | 
 |    * Iterate the tree to find the insertion point. | 
 |    */ | 
 |   curNode        = targetListHead; | 
 |   parentNodeLink = &targetListHead; | 
 |  | 
 |   while (curNode!=NULL) | 
 |   { | 
 |     if (hwndOfTarget<curNode->hwndTarget) | 
 |     { | 
 |       /* | 
 |        * If the node we want to add has a smaller HWND, go left | 
 |        */ | 
 |       parentNodeLink = &curNode->prevDropTarget; | 
 |       curNode        =  curNode->prevDropTarget; | 
 |     } | 
 |     else if (hwndOfTarget>curNode->hwndTarget) | 
 |     { | 
 |       /* | 
 |        * If the node we want to add has a larger HWND, go right | 
 |        */ | 
 |       parentNodeLink = &curNode->nextDropTarget; | 
 |       curNode        =  curNode->nextDropTarget; | 
 |     } | 
 |     else | 
 |     { | 
 |       /* | 
 |        * The item was found in the list. Detach it from it's parent and  | 
 |        * re-insert it's kids in the tree. | 
 |        */ | 
 |       assert(parentNodeLink!=NULL); | 
 |       assert(*parentNodeLink==curNode); | 
 |  | 
 |       /* | 
 |        * We arbitrately re-attach the left sub-tree to the parent. | 
 |        */ | 
 |       *parentNodeLink = curNode->prevDropTarget; | 
 |  | 
 |       /* | 
 |        * And we re-insert the right subtree | 
 |        */ | 
 |       if (curNode->nextDropTarget!=NULL) | 
 |       { | 
 | 	OLEDD_InsertDropTarget(curNode->nextDropTarget); | 
 |       } | 
 |  | 
 |       /* | 
 |        * The node we found is still a valid node once we complete | 
 |        * the unlinking of the kids. | 
 |        */ | 
 |       curNode->nextDropTarget=NULL; | 
 |       curNode->prevDropTarget=NULL; | 
 |  | 
 |       return curNode; | 
 |     } | 
 |   } | 
 |  | 
 |   /* | 
 |    * If we get here, the node is not in the tree | 
 |    */ | 
 |   return NULL; | 
 | } | 
 |  | 
 | /*** | 
 |  * OLEDD_FindDropTarget() | 
 |  * | 
 |  * Finds information about the drop target. | 
 |  */ | 
 | static DropTargetNode* OLEDD_FindDropTarget(HWND hwndOfTarget) | 
 | { | 
 |   DropTargetNode*  curNode; | 
 |  | 
 |   /* | 
 |    * Iterate the tree to find the HWND value. | 
 |    */ | 
 |   curNode        = targetListHead; | 
 |  | 
 |   while (curNode!=NULL) | 
 |   { | 
 |     if (hwndOfTarget<curNode->hwndTarget) | 
 |     { | 
 |       /* | 
 |        * If the node we want to add has a smaller HWND, go left | 
 |        */ | 
 |       curNode =  curNode->prevDropTarget; | 
 |     } | 
 |     else if (hwndOfTarget>curNode->hwndTarget) | 
 |     { | 
 |       /* | 
 |        * If the node we want to add has a larger HWND, go right | 
 |        */ | 
 |       curNode =  curNode->nextDropTarget; | 
 |     } | 
 |     else | 
 |     { | 
 |       /* | 
 |        * The item was found in the list. | 
 |        */ | 
 |       return curNode; | 
 |     } | 
 |   } | 
 |  | 
 |   /* | 
 |    * If we get here, the item is not in the list | 
 |    */ | 
 |   return NULL; | 
 | } | 
 |  | 
 | /*** | 
 |  * 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. | 
 |  */ | 
 | static LRESULT WINAPI OLEDD_DragTrackerWindowProc( | 
 | 			 HWND   hwnd,  | 
 | 			 UINT   uMsg, | 
 | 			 WPARAM wParam,  | 
 | 			 LPARAM   lParam) | 
 | { | 
 |   switch (uMsg) | 
 |   { | 
 |     case WM_CREATE: | 
 |     { | 
 |       LPCREATESTRUCTA createStruct = (LPCREATESTRUCTA)lParam; | 
 |  | 
 |       SetWindowLongA(hwnd, 0, (LONG)createStruct->lpCreateParams);  | 
 |  | 
 |        | 
 |       break; | 
 |     } | 
 |     case WM_MOUSEMOVE: | 
 |     { | 
 |       TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0); | 
 |       POINT            mousePos; | 
 |  | 
 |       /* | 
 |        * Get the current mouse position in screen coordinates. | 
 |        */ | 
 |       mousePos.x = LOWORD(lParam); | 
 |       mousePos.y = HIWORD(lParam); | 
 |       ClientToScreen(hwnd, &mousePos); | 
 |  | 
 |       /* | 
 |        * Track the movement of the mouse. | 
 |        */ | 
 |       OLEDD_TrackMouseMove(trackerInfo, mousePos, wParam); | 
 |  | 
 |       break; | 
 |     } | 
 |     case WM_LBUTTONUP: | 
 |     case WM_MBUTTONUP: | 
 |     case WM_RBUTTONUP: | 
 |     case WM_LBUTTONDOWN: | 
 |     case WM_MBUTTONDOWN: | 
 |     case WM_RBUTTONDOWN: | 
 |     { | 
 |       TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0); | 
 |       POINT            mousePos; | 
 |  | 
 |       /* | 
 |        * Get the current mouse position in screen coordinates. | 
 |        */ | 
 |       mousePos.x = LOWORD(lParam); | 
 |       mousePos.y = HIWORD(lParam); | 
 |       ClientToScreen(hwnd, &mousePos); | 
 |  | 
 |       /* | 
 |        * Notify everyone that the button state changed | 
 |        * TODO: Check if the "escape" key was pressed. | 
 |        */ | 
 |       OLEDD_TrackStateChange(trackerInfo, mousePos, wParam); | 
 |  | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   /* | 
 |    * This is a window proc after all. Let's call the default. | 
 |    */ | 
 |   return DefWindowProcA (hwnd, uMsg, wParam, lParam); | 
 | } | 
 |  | 
 | /*** | 
 |  * OLEDD_TrackMouseMove() | 
 |  * | 
 |  * This method is invoked while a drag and drop operation is in effect. | 
 |  * it will generate the appropriate callbacks in the drop source | 
 |  * and drop target. It will also provide the expected feedback to | 
 |  * the user. | 
 |  * | 
 |  * params: | 
 |  *    trackerInfo - Pointer to the structure identifying the | 
 |  *                  drag & drop operation that is currently | 
 |  *                  active. | 
 |  *    mousePos    - Current position of the mouse in screen | 
 |  *                  coordinates. | 
 |  *    keyState    - Contains the state of the shift keys and the | 
 |  *                  mouse buttons (MK_LBUTTON and the like) | 
 |  */ | 
 | static void OLEDD_TrackMouseMove( | 
 |   TrackerWindowInfo* trackerInfo, | 
 |   POINT            mousePos, | 
 |   DWORD              keyState) | 
 | { | 
 |   HWND   hwndNewTarget = 0; | 
 |   HRESULT  hr = S_OK; | 
 |  | 
 |   /* | 
 |    * Get the handle of the window under the mouse | 
 |    */ | 
 |   hwndNewTarget = WindowFromPoint(mousePos); | 
 |  | 
 |   /* | 
 |    * Every time, we re-initialize the effects passed to the | 
 |    * IDropTarget to the effects allowed by the source. | 
 |    */ | 
 |   *trackerInfo->pdwEffect = trackerInfo->dwOKEffect; | 
 |  | 
 |   /* | 
 |    * If we are hovering over the same target as before, send the | 
 |    * DragOver notification | 
 |    */ | 
 |   if ( (trackerInfo->curDragTarget != 0) &&  | 
 |        (trackerInfo->curDragTargetHWND==hwndNewTarget) ) | 
 |   { | 
 |     POINTL  mousePosParam; | 
 |      | 
 |     /* | 
 |      * The documentation tells me that the coordinate should be in the target | 
 |      * window's coordinate space. However, the tests I made tell me the | 
 |      * coordinates should be in screen coordinates. | 
 |      */ | 
 |     mousePosParam.x = mousePos.x; | 
 |     mousePosParam.y = mousePos.y; | 
 |      | 
 |     IDropTarget_DragOver(trackerInfo->curDragTarget, | 
 | 			 keyState, | 
 | 			 mousePosParam, | 
 | 			 trackerInfo->pdwEffect); | 
 |   } | 
 |   else | 
 |   { | 
 |     DropTargetNode* newDropTargetNode = 0; | 
 |      | 
 |     /* | 
 |      * If we changed window, we have to notify our old target and check for | 
 |      * the new one. | 
 |      */ | 
 |     if (trackerInfo->curDragTarget!=0) | 
 |     { | 
 |       IDropTarget_DragLeave(trackerInfo->curDragTarget); | 
 |     } | 
 |      | 
 |     /* | 
 |      * Make sure we're hovering over a window. | 
 |      */ | 
 |     if (hwndNewTarget!=0) | 
 |     { | 
 |       /* | 
 |        * Find-out if there is a drag target under the mouse | 
 |        */ | 
 |       newDropTargetNode = OLEDD_FindDropTarget(hwndNewTarget); | 
 |        | 
 |       trackerInfo->curDragTargetHWND = hwndNewTarget; | 
 |       trackerInfo->curDragTarget     = newDropTargetNode ? newDropTargetNode->dropTarget : 0; | 
 |        | 
 |       /* | 
 |        * If there is, notify it that we just dragged-in | 
 |        */ | 
 |       if (trackerInfo->curDragTarget!=0) | 
 |       { | 
 | 	POINTL  mousePosParam; | 
 | 	 | 
 | 	/* | 
 | 	 * The documentation tells me that the coordinate should be in the target | 
 | 	 * window's coordinate space. However, the tests I made tell me the | 
 | 	 * coordinates should be in screen coordinates. | 
 | 	 */ | 
 | 	mousePosParam.x = mousePos.x; | 
 | 	mousePosParam.y = mousePos.y; | 
 | 	 | 
 | 	IDropTarget_DragEnter(trackerInfo->curDragTarget, | 
 | 			      trackerInfo->dataObject, | 
 | 			      keyState, | 
 | 			      mousePosParam, | 
 | 			      trackerInfo->pdwEffect); | 
 |       } | 
 |     } | 
 |     else | 
 |     { | 
 |       /* | 
 |        * The mouse is not over a window so we don't track anything. | 
 |        */ | 
 |       trackerInfo->curDragTargetHWND = 0; | 
 |       trackerInfo->curDragTarget     = 0; | 
 |     } | 
 |   } | 
 |  | 
 |   /* | 
 |    * Now that we have done that, we have to tell the source to give  | 
 |    * us feedback on the work being done by the target.  If we don't  | 
 |    * have a target, simulate no effect. | 
 |    */ | 
 |   if (trackerInfo->curDragTarget==0) | 
 |   { | 
 |     *trackerInfo->pdwEffect = DROPEFFECT_NONE; | 
 |   } | 
 |  | 
 |   hr = IDropSource_GiveFeedback(trackerInfo->dropSource, | 
 |   				*trackerInfo->pdwEffect); | 
 |  | 
 |   /* | 
 |    * When we ask for feedback from the drop source, sometimes it will | 
 |    * do all the necessary work and sometimes it will not handle it | 
 |    * when that's the case, we must display the standard drag and drop | 
 |    * cursors. | 
 |    */ | 
 |   if (hr==DRAGDROP_S_USEDEFAULTCURSORS) | 
 |   { | 
 |     if ( (*trackerInfo->pdwEffect & DROPEFFECT_MOVE) || | 
 | 	 (*trackerInfo->pdwEffect & DROPEFFECT_COPY) || | 
 | 	 (*trackerInfo->pdwEffect & DROPEFFECT_LINK) ) | 
 |     { | 
 |       SetCursor(LoadCursorA(0, IDC_SIZEALLA)); | 
 |     } | 
 |     else | 
 |     { | 
 |       SetCursor(LoadCursorA(0, IDC_NOA)); | 
 |     } | 
 |   }   | 
 | } | 
 |  | 
 | /*** | 
 |  * OLEDD_TrackStateChange() | 
 |  * | 
 |  * This method is invoked while a drag and drop operation is in effect. | 
 |  * It is used to notify the drop target/drop source callbacks when | 
 |  * the state of the keyboard or mouse button change. | 
 |  * | 
 |  * params: | 
 |  *    trackerInfo - Pointer to the structure identifying the | 
 |  *                  drag & drop operation that is currently | 
 |  *                  active. | 
 |  *    mousePos    - Current position of the mouse in screen | 
 |  *                  coordinates. | 
 |  *    keyState    - Contains the state of the shift keys and the | 
 |  *                  mouse buttons (MK_LBUTTON and the like) | 
 |  */ | 
 | static void OLEDD_TrackStateChange( | 
 |   TrackerWindowInfo* trackerInfo, | 
 |   POINT            mousePos, | 
 |   DWORD              keyState) | 
 | { | 
 |   /* | 
 |    * Ask the drop source what to do with the operation. | 
 |    */ | 
 |   trackerInfo->returnValue = IDropSource_QueryContinueDrag( | 
 | 			       trackerInfo->dropSource, | 
 | 			       trackerInfo->escPressed,  | 
 | 			       keyState); | 
 |    | 
 |   /* | 
 |    * All the return valued will stop the operation except the S_OK | 
 |    * return value. | 
 |    */ | 
 |   if (trackerInfo->returnValue!=S_OK) | 
 |   { | 
 |     /* | 
 |      * Make sure the message loop in DoDragDrop stops | 
 |      */ | 
 |     trackerInfo->trackingDone = TRUE; | 
 |  | 
 |     /* | 
 |      * Release the mouse in case the drop target decides to show a popup  | 
 |      * or a menu or something. | 
 |      */ | 
 |     ReleaseCapture(); | 
 |      | 
 |     /* | 
 |      * If we end-up over a target, drop the object in the target or  | 
 |      * inform the target that the operation was cancelled. | 
 |      */ | 
 |     if (trackerInfo->curDragTarget!=0) | 
 |     { | 
 |       switch (trackerInfo->returnValue) | 
 |       { | 
 | 	/* | 
 | 	 * If the source wants us to complete the operation, we tell  | 
 | 	 * the drop target that we just dropped the object in it. | 
 | 	 */ | 
 |         case DRAGDROP_S_DROP: | 
 | 	{ | 
 | 	  POINTL  mousePosParam; | 
 | 	 | 
 | 	  /* | 
 | 	   * The documentation tells me that the coordinate should be  | 
 | 	   * in the target window's coordinate space. However, the tests | 
 | 	   * I made tell me the coordinates should be in screen coordinates. | 
 | 	   */ | 
 | 	  mousePosParam.x = mousePos.x; | 
 | 	  mousePosParam.y = mousePos.y; | 
 | 	   | 
 | 	  IDropTarget_Drop(trackerInfo->curDragTarget, | 
 | 			   trackerInfo->dataObject, | 
 | 			   keyState, | 
 | 			   mousePosParam, | 
 | 			   trackerInfo->pdwEffect); | 
 | 	  break; | 
 | 	} | 
 | 	/* | 
 | 	 * If the source told us that we should cancel, fool the drop  | 
 | 	 * target by telling it that the mouse left it's window. | 
 | 	 */ | 
 |         case DRAGDROP_S_CANCEL: | 
 | 	  IDropTarget_DragLeave(trackerInfo->curDragTarget); | 
 | 	  break; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | /*** | 
 |  * 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() | 
 | { | 
 |   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_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) | 
 | { | 
 |   char  buffer[20]; | 
 |   DWORD dwKeyType; | 
 |   DWORD cbData = 20; | 
 |   LONG  lres; | 
 |  | 
 |   lres = RegQueryValueExA(regKey, | 
 | 			  "", | 
 | 			  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 = (DWORD)strtoul(buffer, NULL, 10); | 
 | 	break; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | /****************************************************************************** | 
 |  * OleMetaFilePictFromIconAndLabel | 
 |  * | 
 |  * Returns a global memory handle to a metafile which contains the icon and | 
 |  * label given. | 
 |  * I guess the result of that should look somehow like desktop icons. | 
 |  * If no hIcon is given, we load the icon via lpszSourceFile and iIconIndex. | 
 |  * This code might be wrong at some places. | 
 |  */ | 
 | HGLOBAL16 WINAPI OleMetaFilePictFromIconAndLabel16( | 
 | 	HICON16 hIcon, | 
 | 	LPCOLESTR16 lpszLabel, | 
 | 	LPCOLESTR16 lpszSourceFile, | 
 | 	UINT16 iIconIndex | 
 | ) { | 
 |     METAFILEPICT16 *mf; | 
 |     HGLOBAL16 hmf; | 
 |     HDC16 hdc; | 
 |  | 
 |     FIXME("(%04x, '%s', '%s', %d): incorrect metrics, please try to correct them !\n\n\n", hIcon, lpszLabel, lpszSourceFile, iIconIndex); | 
 |  | 
 |     if (!hIcon) { | 
 |         if (lpszSourceFile) { | 
 | 	    HINSTANCE16 hInstance = LoadLibrary16(lpszSourceFile); | 
 |  | 
 | 	    /* load the icon at index from lpszSourceFile */ | 
 | 	    hIcon = (HICON16)LoadIconA(hInstance, (LPCSTR)(DWORD)iIconIndex); | 
 | 	    FreeLibrary16(hInstance); | 
 | 	} else | 
 | 	    return (HGLOBAL)NULL; | 
 |     } | 
 |  | 
 |     hdc = CreateMetaFile16(NULL); | 
 |     DrawIcon(hdc, 0, 0, hIcon); /* FIXME */ | 
 |     TextOut16(hdc, 0, 0, lpszLabel, 1); /* FIXME */ | 
 |     hmf = GlobalAlloc16(0, sizeof(METAFILEPICT16)); | 
 |     mf = (METAFILEPICT16 *)GlobalLock16(hmf); | 
 |     mf->mm = MM_ANISOTROPIC; | 
 |     mf->xExt = 20; /* FIXME: bogus */ | 
 |     mf->yExt = 20; /* dito */ | 
 |     mf->hMF = CloseMetaFile16(hdc); | 
 |     return hmf; | 
 | } |