- implementation of RtlReg* (read access), RtlEvent*, RtlSemaphore*,
[wine] / dlls / ole32 / ole2.c
1 /*
2  *      OLE2 library
3  *
4  *      Copyright 1995  Martin von Loewis
5  *      Copyright 1999  Francis Beaudet
6  *      Copyright 1999  Noel Borthwick 
7  */
8
9 #include <assert.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #include "winuser.h"
14 #include "winerror.h"
15 #include "ole2.h"
16 #include "process.h"
17 #include "commctrl.h"
18 #include "wine/obj_clientserver.h"
19 #include "wine/wingdi16.h"
20 #include "debugtools.h"
21 #include "ole2ver.h"
22 #include "winreg.h"
23
24 DEFAULT_DEBUG_CHANNEL(ole)
25
26 /******************************************************************************
27  * These are static/global variables and internal data structures that the 
28  * OLE module uses to maintain it's state.
29  */
30 typedef struct tagDropTargetNode
31 {
32   HWND          hwndTarget;
33   IDropTarget*    dropTarget;
34   struct tagDropTargetNode* prevDropTarget;
35   struct tagDropTargetNode* nextDropTarget;
36 } DropTargetNode;
37
38 typedef struct tagTrackerWindowInfo
39 {
40   IDataObject* dataObject;
41   IDropSource* dropSource;
42   DWORD        dwOKEffect;
43   DWORD*       pdwEffect;
44   BOOL       trackingDone;
45   HRESULT      returnValue;
46
47   BOOL       escPressed;
48   HWND       curDragTargetHWND;
49   IDropTarget* curDragTarget;
50 } TrackerWindowInfo;
51
52 typedef struct tagOleMenuDescriptor  /* OleMenuDescriptor */
53 {
54   HWND               hwndFrame;         /* The containers frame window */
55   HWND               hwndActiveObject;  /* The active objects window */
56   OLEMENUGROUPWIDTHS mgw;               /* OLE menu group widths for the shared menu */
57   HMENU              hmenuCombined;     /* The combined menu */
58   BOOL               bIsServerItem;     /* True if the currently open popup belongs to the server */
59 } OleMenuDescriptor;
60
61 typedef struct tagOleMenuHookItem   /* OleMenu hook item in per thread hook list */
62 {
63   DWORD tid;                /* Thread Id  */
64   HANDLE hHeap;             /* Heap this is allocated from */
65   HHOOK GetMsg_hHook;       /* message hook for WH_GETMESSAGE */
66   HHOOK CallWndProc_hHook;  /* message hook for WH_CALLWNDPROC */
67 } OleMenuHookItem;
68
69 /*
70  * Dynamic pointer array of per thread message hooks (maintained by OleSetMenuDescriptor)
71  */
72 static HDPA OLEMenu_MsgHookDPA = NULL;
73
74 /*
75  * This is the lock count on the OLE library. It is controlled by the
76  * OLEInitialize/OLEUninitialize methods.
77  */
78 static ULONG OLE_moduleLockCount = 0;
79
80 /*
81  * Name of our registered window class.
82  */
83 static const char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32";
84
85 /*
86  * This is the head of the Drop target container.
87  */
88 static DropTargetNode* targetListHead = NULL;
89
90 /******************************************************************************
91  * These are the prototypes of miscelaneous utility methods 
92  */
93 static void OLEUTL_ReadRegistryDWORDValue(HKEY regKey, DWORD* pdwValue);
94
95 /******************************************************************************
96  * These are the prototypes of the utility methods used to manage a shared menu
97  */
98 static void OLEMenu_Initialize();
99 static void OLEMenu_UnInitialize();
100 BOOL OLEMenu_InstallHooks( DWORD tid );
101 BOOL OLEMenu_UnInstallHooks( DWORD tid );
102 OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook );
103 static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos );
104 BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor );
105 LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam);
106 LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam);
107
108 /******************************************************************************
109  * These are the prototypes of the OLE Clipboard initialization methods (in clipboard.c)
110  */
111 void OLEClipbrd_UnInitialize();
112 void OLEClipbrd_Initialize();
113
114 /******************************************************************************
115  * These are the prototypes of the utility methods used for OLE Drag n Drop
116  */
117 static void            OLEDD_Initialize();
118 static void            OLEDD_UnInitialize();
119 static void            OLEDD_InsertDropTarget(
120                          DropTargetNode* nodeToAdd);
121 static DropTargetNode* OLEDD_ExtractDropTarget(
122                          HWND hwndOfTarget);
123 static DropTargetNode* OLEDD_FindDropTarget(
124                          HWND hwndOfTarget);
125 static LRESULT WINAPI  OLEDD_DragTrackerWindowProc(
126                          HWND   hwnd, 
127                          UINT   uMsg,
128                          WPARAM wParam, 
129                          LPARAM   lParam);
130 static void OLEDD_TrackMouseMove(
131                          TrackerWindowInfo* trackerInfo,
132                          POINT            mousePos,
133                          DWORD              keyState);
134 static void OLEDD_TrackStateChange(
135                          TrackerWindowInfo* trackerInfo,
136                          POINT            mousePos,
137                          DWORD              keyState);
138 static DWORD OLEDD_GetButtonState();
139
140
141 /******************************************************************************
142  *              OleBuildVersion [OLE2.1]
143  */
144 DWORD WINAPI OleBuildVersion(void)
145 {
146     TRACE("Returning version %d, build %d.\n", rmm, rup);
147     return (rmm<<16)+rup;
148 }
149
150 /***********************************************************************
151  *           OleInitialize       (OLE2.2) (OLE32.108)
152  */
153 HRESULT WINAPI OleInitialize(LPVOID reserved)
154 {
155   HRESULT hr;
156
157   TRACE("(%p)\n", reserved);
158
159   /*
160    * The first duty of the OleInitialize is to initialize the COM libraries.
161    */
162   hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
163
164   /*
165    * If the CoInitializeEx call failed, the OLE libraries can't be 
166    * initialized.
167    */
168   if (FAILED(hr))
169     return hr;    
170
171   /*
172    * Then, it has to initialize the OLE specific modules.
173    * This includes:
174    *     Clipboard
175    *     Drag and Drop
176    *     Object linking and Embedding
177    *     In-place activation
178    */
179   if (OLE_moduleLockCount==0)
180 {
181     /* 
182      * Initialize the libraries.
183      */
184     TRACE("() - Initializing the OLE libraries\n");
185
186     /*
187      * OLE Clipboard
188      */
189     OLEClipbrd_Initialize();
190
191     /*
192      * Drag and Drop
193      */
194     OLEDD_Initialize();
195
196     /*
197      * OLE shared menu
198      */
199     OLEMenu_Initialize();
200 }
201
202   /*
203    * Then, we increase the lock count on the OLE module.
204    */
205   OLE_moduleLockCount++;  
206
207   return hr;
208 }
209
210 /******************************************************************************
211  *              CoGetCurrentProcess     [COMPOBJ.34] [OLE2.2][OLE32.108]
212  *
213  * NOTES
214  *   Is DWORD really the correct return type for this function?
215  */
216 DWORD WINAPI CoGetCurrentProcess(void) {
217         return (DWORD)PROCESS_Current();
218 }
219
220 /******************************************************************************
221  *              OleUninitialize [OLE2.3] [OLE32.131]
222  */
223 void WINAPI OleUninitialize(void)
224 {
225   TRACE("()\n");
226
227   /*
228    * Decrease the lock count on the OLE module.
229    */
230   OLE_moduleLockCount--;
231
232   /*
233    * If we hit the bottom of the lock stack, free the libraries.
234    */
235   if (OLE_moduleLockCount==0)
236   {
237     /*
238      * Actually free the libraries.
239      */
240     TRACE("() - Freeing the last reference count\n");
241
242     /*
243      * OLE Clipboard
244      */
245     OLEClipbrd_UnInitialize();
246
247     /*
248      * Drag and Drop
249      */
250     OLEDD_UnInitialize();
251     
252     /*
253      * OLE shared menu
254      */
255     OLEMenu_UnInitialize();
256   }
257   
258   /*
259    * Then, uninitialize the COM libraries.
260    */
261   CoUninitialize();
262 }
263
264 /******************************************************************************
265  *              CoRegisterMessageFilter32       [OLE32.38]
266  */
267 HRESULT WINAPI CoRegisterMessageFilter(
268     LPMESSAGEFILTER lpMessageFilter,    /* Pointer to interface */
269     LPMESSAGEFILTER *lplpMessageFilter  /* Indirect pointer to prior instance if non-NULL */
270 ) {
271     FIXME("stub\n");
272     if (lplpMessageFilter) {
273         *lplpMessageFilter = NULL;
274     }
275     return S_OK;
276 }
277
278 /******************************************************************************
279  *              OleInitializeWOW        [OLE32.109]
280  */
281 HRESULT WINAPI OleInitializeWOW(DWORD x) {
282         FIXME("(0x%08lx),stub!\n",x);
283         return 0;
284 }
285
286 /***********************************************************************
287  *           RegisterDragDrop16 (OLE2.35)
288  */
289 HRESULT WINAPI RegisterDragDrop16(
290         HWND16 hwnd,
291         LPDROPTARGET pDropTarget
292 ) {
293         FIXME("(0x%04x,%p),stub!\n",hwnd,pDropTarget);
294         return S_OK;
295 }
296
297 /***********************************************************************
298  *           RegisterDragDrop32 (OLE32.139)
299  */
300 HRESULT WINAPI RegisterDragDrop(
301         HWND hwnd,
302         LPDROPTARGET pDropTarget) 
303 {
304   DropTargetNode* dropTargetInfo;
305
306   TRACE("(0x%x,%p)\n", hwnd, pDropTarget);
307
308   /*
309    * First, check if the window is already registered.
310    */
311   dropTargetInfo = OLEDD_FindDropTarget(hwnd);
312
313   if (dropTargetInfo!=NULL)
314     return DRAGDROP_E_ALREADYREGISTERED;
315
316   /*
317    * If it's not there, we can add it. We first create a node for it.
318    */
319   dropTargetInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(DropTargetNode));
320
321   if (dropTargetInfo==NULL)
322     return E_OUTOFMEMORY;
323
324   dropTargetInfo->hwndTarget     = hwnd;
325   dropTargetInfo->prevDropTarget = NULL;
326   dropTargetInfo->nextDropTarget = NULL;
327
328   /*
329    * Don't forget that this is an interface pointer, need to nail it down since
330    * we keep a copy of it.
331    */
332   dropTargetInfo->dropTarget  = pDropTarget;
333   IDropTarget_AddRef(dropTargetInfo->dropTarget);
334   
335   OLEDD_InsertDropTarget(dropTargetInfo);
336
337         return S_OK;
338 }
339
340 /***********************************************************************
341  *           RevokeDragDrop16 (OLE2.36)
342  */
343 HRESULT WINAPI RevokeDragDrop16(
344         HWND16 hwnd
345 ) {
346         FIXME("(0x%04x),stub!\n",hwnd);
347         return S_OK;
348 }
349
350 /***********************************************************************
351  *           RevokeDragDrop32 (OLE32.141)
352  */
353 HRESULT WINAPI RevokeDragDrop(
354         HWND hwnd)
355 {
356   DropTargetNode* dropTargetInfo;
357
358   TRACE("(0x%x)\n", hwnd);
359
360   /*
361    * First, check if the window is already registered.
362    */
363   dropTargetInfo = OLEDD_ExtractDropTarget(hwnd);
364
365   /*
366    * If it ain't in there, it's an error.
367    */
368   if (dropTargetInfo==NULL)
369     return DRAGDROP_E_NOTREGISTERED;
370
371   /*
372    * If it's in there, clean-up it's used memory and
373    * references
374    */
375   IDropTarget_Release(dropTargetInfo->dropTarget);
376   HeapFree(GetProcessHeap(), 0, dropTargetInfo);  
377
378         return S_OK;
379 }
380
381 /***********************************************************************
382  *           OleRegGetUserType (OLE32.122)
383  *
384  * This implementation of OleRegGetUserType ignores the dwFormOfType
385  * parameter and always returns the full name of the object. This is
386  * not too bad since this is the case for many objects because of the
387  * way they are registered.
388  */
389 HRESULT WINAPI OleRegGetUserType( 
390         REFCLSID clsid, 
391         DWORD dwFormOfType,
392         LPOLESTR* pszUserType)
393 {
394   char  xclsid[50];
395   char  keyName[60];
396   DWORD dwKeyType;
397   DWORD cbData;
398   HKEY  clsidKey;
399   LONG  hres;
400
401   /*
402    * Initialize the out parameter.
403    */
404   *pszUserType = NULL;
405
406   /*
407    * Build the key name we're looking for
408    */
409   WINE_StringFromCLSID((LPCLSID)clsid, xclsid);
410
411   strcpy(keyName, "CLSID\\");
412   strcat(keyName, xclsid);
413   strcat(keyName, "\\");
414
415   TRACE("(%s, %ld, %p)\n", keyName, dwFormOfType, pszUserType);
416
417   /*
418    * Open the class id Key
419    */
420   hres = RegOpenKeyA(HKEY_CLASSES_ROOT,
421                      keyName,
422                      &clsidKey);
423
424   if (hres != ERROR_SUCCESS)
425     return REGDB_E_CLASSNOTREG;
426
427   /*
428    * Retrieve the size of the name string.
429    */
430   cbData = 0;
431
432   hres = RegQueryValueExA(clsidKey,
433                           "",
434                           NULL,
435                           &dwKeyType,
436                           NULL,
437                           &cbData);
438
439   if (hres!=ERROR_SUCCESS)
440   {
441     RegCloseKey(clsidKey);
442     return REGDB_E_READREGDB;
443   }
444
445   /*
446    * Allocate a buffer for the registry value.
447    */
448   *pszUserType = CoTaskMemAlloc(cbData);
449
450   if (*pszUserType==NULL)
451   {
452     RegCloseKey(clsidKey);
453     return E_OUTOFMEMORY;
454   }
455
456   hres = RegQueryValueExA(HKEY_CLASSES_ROOT,
457                           "",
458                           NULL,
459                           &dwKeyType,
460                           (LPBYTE)*pszUserType,
461                           &cbData);
462
463   RegCloseKey(clsidKey);
464   
465   if (hres!=ERROR_SUCCESS)
466   {
467     CoTaskMemFree(*pszUserType);
468     *pszUserType=NULL;
469
470     return REGDB_E_READREGDB;
471   }
472
473   return S_OK;
474 }
475
476 /***********************************************************************
477  * DoDragDrop32 [OLE32.65]
478  */
479 HRESULT WINAPI DoDragDrop (
480   IDataObject *pDataObject,  /* ptr to the data obj           */
481   IDropSource* pDropSource,  /* ptr to the source obj         */
482   DWORD       dwOKEffect,    /* effects allowed by the source */
483   DWORD       *pdwEffect)    /* ptr to effects of the source  */
484 {
485   TrackerWindowInfo trackerInfo;
486   HWND            hwndTrackWindow;
487   MSG             msg;
488
489   TRACE("(DataObject %p, DropSource %p)\n", pDataObject, pDropSource);
490
491   /*
492    * Setup the drag n drop tracking window.
493    */
494   trackerInfo.dataObject        = pDataObject;
495   trackerInfo.dropSource        = pDropSource;
496   trackerInfo.dwOKEffect        = dwOKEffect;
497   trackerInfo.pdwEffect         = pdwEffect;
498   trackerInfo.trackingDone      = FALSE;
499   trackerInfo.escPressed        = FALSE;
500   trackerInfo.curDragTargetHWND = 0;
501   trackerInfo.curDragTarget     = 0;
502
503   hwndTrackWindow = CreateWindowA(OLEDD_DRAGTRACKERCLASS,
504                                     "TrackerWindow",
505                                     WS_POPUP,
506                                     CW_USEDEFAULT, CW_USEDEFAULT,
507                                     CW_USEDEFAULT, CW_USEDEFAULT,
508                                     0,
509                                     0,
510                                     0,
511                                     (LPVOID)&trackerInfo);
512
513   if (hwndTrackWindow!=0)
514   {
515     /*
516      * Capture the mouse input
517      */
518     SetCapture(hwndTrackWindow);
519
520     /*
521      * Pump messages. All mouse input should go the the capture window.
522      */
523     while (!trackerInfo.trackingDone && GetMessageA(&msg, 0, 0, 0) )
524     {
525       if ( (msg.message >= WM_KEYFIRST) && 
526            (msg.message <= WM_KEYFIRST) )
527       {
528         /*
529          * When keyboard messages are sent to windows on this thread, we
530          * want to ignore notify the drop source that the state changed.
531          * in the case of the Escape key, we also notify the drop source
532          * we give it a special meaning.
533          */
534         if ( (msg.message==WM_KEYDOWN) &&
535              (msg.wParam==VK_ESCAPE) )
536         {
537           trackerInfo.escPressed = TRUE;
538         }
539
540         /*
541          * Notify the drop source.
542          */       
543         OLEDD_TrackStateChange(&trackerInfo,
544                                msg.pt,
545                                OLEDD_GetButtonState());
546       }
547       else
548       {
549         /*
550          * Dispatch the messages only when it's not a keyboard message.
551          */
552         DispatchMessageA(&msg);
553       }
554     }
555
556     /*
557      * Destroy the temporary window.
558      */
559     DestroyWindow(hwndTrackWindow);
560
561     return trackerInfo.returnValue;
562   }
563
564   return E_FAIL;
565 }
566
567 /***********************************************************************
568  * OleQueryLinkFromData32 [OLE32.118]
569  */
570 HRESULT WINAPI OleQueryLinkFromData(
571   IDataObject* pSrcDataObject)
572 {
573   FIXME("(%p),stub!\n", pSrcDataObject);
574   return S_OK;
575 }
576
577 /***********************************************************************
578  * OleRegGetMiscStatus [OLE32.121]
579  */
580 HRESULT WINAPI OleRegGetMiscStatus(
581   REFCLSID clsid,
582   DWORD    dwAspect,
583   DWORD*   pdwStatus)
584 {
585   char    xclsid[50];
586   char    keyName[60];
587   HKEY    clsidKey;
588   HKEY    miscStatusKey;
589   HKEY    aspectKey;
590   LONG    result;
591
592   /*
593    * Initialize the out parameter.
594    */
595   *pdwStatus = 0;
596
597   /*
598    * Build the key name we're looking for
599    */
600   WINE_StringFromCLSID((LPCLSID)clsid, xclsid);
601
602   strcpy(keyName, "CLSID\\");
603   strcat(keyName, xclsid);
604   strcat(keyName, "\\");
605
606   TRACE("(%s, %ld, %p)\n", keyName, dwAspect, pdwStatus);
607
608   /*
609    * Open the class id Key
610    */
611   result = RegOpenKeyA(HKEY_CLASSES_ROOT,
612                        keyName,
613                        &clsidKey);
614
615   if (result != ERROR_SUCCESS)
616     return REGDB_E_CLASSNOTREG;
617
618   /*
619    * Get the MiscStatus
620    */
621   result = RegOpenKeyA(clsidKey,
622                        "MiscStatus",
623                        &miscStatusKey);
624
625   
626   if (result != ERROR_SUCCESS)
627   {
628     RegCloseKey(clsidKey);
629     return REGDB_E_READREGDB;
630   }
631
632   /*
633    * Read the default value
634    */
635   OLEUTL_ReadRegistryDWORDValue(miscStatusKey, pdwStatus);
636
637   /*
638    * Open the key specific to the requested aspect.
639    */
640   sprintf(keyName, "%ld", dwAspect);
641
642   result = RegOpenKeyA(miscStatusKey,
643                        keyName,
644                        &aspectKey);
645   
646   if (result == ERROR_SUCCESS)
647   {
648     OLEUTL_ReadRegistryDWORDValue(aspectKey, pdwStatus);
649     RegCloseKey(aspectKey);
650   }
651
652   /*
653    * Cleanup
654    */
655   RegCloseKey(miscStatusKey);
656   RegCloseKey(clsidKey);
657
658   return S_OK;
659 }
660
661 /******************************************************************************
662  *              OleSetContainedObject        [OLE32.128]
663  */
664 HRESULT WINAPI OleSetContainedObject(
665   LPUNKNOWN pUnknown, 
666   BOOL      fContained)
667 {
668   IRunnableObject* runnable = NULL;
669   HRESULT          hres;
670
671   TRACE("(%p,%x), stub!\n", pUnknown, fContained);
672
673   hres = IUnknown_QueryInterface(pUnknown,
674                                  &IID_IRunnableObject,
675                                  (void**)&runnable);
676
677   if (SUCCEEDED(hres))
678   {
679     hres = IRunnableObject_SetContainedObject(runnable, fContained);
680
681     IRunnableObject_Release(runnable);
682
683     return hres;
684   }
685
686   return S_OK;
687 }
688
689 /******************************************************************************
690  *              OleLoad        [OLE32.112]
691  */
692 HRESULT WINAPI OleLoad(
693   LPSTORAGE       pStg, 
694   REFIID          riid, 
695   LPOLECLIENTSITE pClientSite, 
696   LPVOID*         ppvObj)
697 {
698   IPersistStorage* persistStorage = NULL;
699   IOleObject*      oleObject      = NULL;
700   STATSTG          storageInfo;
701   HRESULT          hres;
702
703   TRACE("(%p,%p,%p,%p)\n", pStg, riid, pClientSite, ppvObj);
704   
705   /*
706    * TODO, Conversion ... OleDoAutoConvert
707    */
708
709   /*
710    * Get the class ID for the object.
711    */
712   hres = IStorage_Stat(pStg, &storageInfo, STATFLAG_NONAME);
713
714   /*
715    * Now, try and create the handler for the object
716    */
717   hres = CoCreateInstance(&storageInfo.clsid,
718                           NULL,
719                           CLSCTX_INPROC_HANDLER,
720                           &IID_IOleObject,
721                           (void**)&oleObject);
722
723   /*
724    * If that fails, as it will most times, load the default
725    * OLE handler.
726    */
727   if (FAILED(hres))
728   {
729     hres = OleCreateDefaultHandler(&storageInfo.clsid,
730                                    NULL,
731                                    &IID_IOleObject,
732                                    (void**)&oleObject);
733   }
734
735   /*
736    * If we couldn't find a handler... this is bad. Abort the whole thing.
737    */
738   if (FAILED(hres))
739     return hres;
740
741   /*
742    * Inform the new object of it's client site.
743    */
744   hres = IOleObject_SetClientSite(oleObject, pClientSite);
745
746   /*
747    * Initialize the object with it's IPersistStorage interface.
748    */
749   hres = IOleObject_QueryInterface(oleObject,
750                                    &IID_IPersistStorage,
751                                    (void**)&persistStorage);
752
753   if (SUCCEEDED(hres)) 
754   {
755     IPersistStorage_Load(persistStorage, pStg);
756
757     IPersistStorage_Release(persistStorage);
758     persistStorage = NULL;
759   }
760
761   /*
762    * Return the requested interface to the caller.
763    */
764   hres = IOleObject_QueryInterface(oleObject, riid, ppvObj);
765
766   /*
767    * Cleanup interfaces used internally
768    */
769   IOleObject_Release(oleObject);
770
771   return hres;
772 }
773
774 /***********************************************************************
775  *           OleSave     [OLE32.124]
776  */
777 HRESULT WINAPI OleSave(
778   LPPERSISTSTORAGE pPS,
779   LPSTORAGE        pStg,
780   BOOL             fSameAsLoad)
781 {
782   HRESULT hres;
783   CLSID   objectClass;
784
785   TRACE("(%p,%p,%x)\n", pPS, pStg, fSameAsLoad);
786
787   /*
788    * First, we transfer the class ID (if available)
789    */
790   hres = IPersistStorage_GetClassID(pPS, &objectClass);
791
792   if (SUCCEEDED(hres))
793   {
794     WriteClassStg(pStg, &objectClass);
795   }
796
797   /*
798    * Then, we ask the object to save itself to the
799    * storage. If it is successful, we commit the storage.
800    */
801   hres = IPersistStorage_Save(pPS, pStg, fSameAsLoad);
802
803   if (SUCCEEDED(hres))
804   {
805     IStorage_Commit(pStg,
806                     STGC_DEFAULT);
807   }
808   
809   return hres;
810 }
811
812
813 /**************************************************************************
814  * Internal methods to manage the shared OLE menu in response to the
815  * OLE***MenuDescriptor API
816  */
817
818 /***
819  * OLEMenu_Initialize()
820  *
821  * Initializes the OLEMENU data structures.
822  */
823 static void OLEMenu_Initialize()
824 {
825   /* Create a dynamic pointer array to store the hook handles */
826   if ( !OLEMenu_MsgHookDPA )
827     OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() );
828 }
829
830 /***
831  * OLEMenu_UnInitialize()
832  *
833  * Releases the OLEMENU data structures.
834  */
835 static void OLEMenu_UnInitialize()
836 {
837   /* Release the hook table */
838   if ( OLEMenu_MsgHookDPA )
839     DPA_Destroy( OLEMenu_MsgHookDPA );
840
841   OLEMenu_MsgHookDPA = NULL;
842 }
843
844 /*************************************************************************
845  * OLEMenu_InstallHooks
846  * Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
847  *
848  * RETURNS: TRUE if message hooks were succesfully installed
849  *          FALSE on failure
850  */
851 BOOL OLEMenu_InstallHooks( DWORD tid )
852 {
853   OleMenuHookItem *pHookItem = NULL;
854
855   if ( !OLEMenu_MsgHookDPA ) /* No hook table? Create one */
856   {
857     /* Create a dynamic pointer array to store the hook handles */
858     if ( !(OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() )) ) 
859       return FALSE;
860   }
861
862   /* Create an entry for the hook table */
863   if ( !(pHookItem = HeapAlloc(GetProcessHeap(), 0,
864                                sizeof(OleMenuHookItem)) ) )
865     return FALSE;
866
867   pHookItem->tid = tid;
868   pHookItem->hHeap = GetProcessHeap();
869   
870   /* Install a thread scope message hook for WH_GETMESSAGE */
871   pHookItem->GetMsg_hHook = SetWindowsHookExA( WH_GETMESSAGE, OLEMenu_GetMsgProc,
872                                                0, GetCurrentThreadId() );
873   if ( !pHookItem->GetMsg_hHook )
874     goto CLEANUP;
875
876   /* Install a thread scope message hook for WH_CALLWNDPROC */
877   pHookItem->CallWndProc_hHook = SetWindowsHookExA( WH_CALLWNDPROC, OLEMenu_CallWndProc,
878                                                     0, GetCurrentThreadId() );
879   if ( !pHookItem->CallWndProc_hHook )
880     goto CLEANUP;
881
882   /* Insert the hook table entry */
883   if ( -1 == DPA_InsertPtr( OLEMenu_MsgHookDPA, 0, pHookItem ) )
884     goto CLEANUP;
885   
886   return TRUE;
887   
888 CLEANUP:
889   /* Unhook any hooks */
890   if ( pHookItem->GetMsg_hHook )
891     UnhookWindowsHookEx( pHookItem->GetMsg_hHook );
892   if ( pHookItem->CallWndProc_hHook )
893     UnhookWindowsHookEx( pHookItem->CallWndProc_hHook );
894   /* Release the hook table entry */
895   HeapFree(pHookItem->hHeap, 0, pHookItem );
896   
897   return FALSE;
898 }
899
900 /*************************************************************************
901  * OLEMenu_UnInstallHooks
902  * UnInstall thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
903  *
904  * RETURNS: TRUE if message hooks were succesfully installed
905  *          FALSE on failure
906  */
907 BOOL OLEMenu_UnInstallHooks( DWORD tid )
908 {
909   INT ixHook;
910   OleMenuHookItem *pHookItem = NULL;
911
912   if ( !OLEMenu_MsgHookDPA )  /* No hooks set */
913     return TRUE;
914
915   /* Lookup the hHook index for this tid */
916   if ( !OLEMenu_IsHookInstalled( tid , &ixHook ) )
917     return TRUE;
918
919   /* Remove the hook entry from the table(the pointer itself is not deleted) */
920   if ( !( pHookItem = DPA_DeletePtr(OLEMenu_MsgHookDPA, ixHook) ) )
921     return FALSE;
922
923   /* Uninstall the hooks installed for this thread */
924   if ( !UnhookWindowsHookEx( pHookItem->GetMsg_hHook ) )
925     goto CLEANUP;
926   if ( !UnhookWindowsHookEx( pHookItem->CallWndProc_hHook ) )
927     goto CLEANUP;
928
929   /* Release the hook table entry */
930   HeapFree(pHookItem->hHeap, 0, pHookItem );
931
932   return TRUE;
933
934 CLEANUP:
935   /* Release the hook table entry */
936   if (pHookItem)
937     HeapFree(pHookItem->hHeap, 0, pHookItem );
938
939   return FALSE;
940 }
941
942 /*************************************************************************
943  * OLEMenu_IsHookInstalled
944  * Tests if OLEMenu hooks have been installed for a thread
945  *
946  * RETURNS: The pointer and index of the hook table entry for the tid
947  *          NULL and -1 for the index if no hooks were installed for this thread
948  */
949 OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook )
950 {
951   INT ixHook;
952   OleMenuHookItem *pHookItem = NULL;
953
954   if ( pixHook )
955     *pixHook = -1;
956   
957   if ( !OLEMenu_MsgHookDPA )  /* No hooks set */
958     return NULL;
959
960   /* Do a simple linear search for an entry whose tid matches ours.
961    * We really need a map but efficiency is not a concern here. */
962   for( ixHook = 0; ; ixHook++ )
963   {
964     /* Retrieve the hook entry */
965     if ( !( pHookItem = DPA_GetPtr(OLEMenu_MsgHookDPA, ixHook) ) )
966       return NULL;
967
968     if ( tid == pHookItem->tid )
969     {
970       if ( pixHook )
971         *pixHook = ixHook;
972       return pHookItem;
973     }
974   }
975   
976   return NULL;
977 }
978
979 /***********************************************************************
980  *           OLEMenu_FindMainMenuIndex
981  *
982  * Used by OLEMenu API to find the top level group a menu item belongs to.
983  * On success pnPos contains the index of the item in the top level menu group
984  *
985  * RETURNS: TRUE if the ID was found, FALSE on failure
986  */
987 static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos )
988 {
989   UINT i, nItems;
990
991   nItems = GetMenuItemCount( hMainMenu );
992
993   for (i = 0; i < nItems; i++)
994   {
995     HMENU hsubmenu;
996       
997     /*  Is the current item a submenu? */
998     if ( (hsubmenu = GetSubMenu(hMainMenu, i)) )
999     {
1000       /* If the handle is the same we're done */
1001       if ( hsubmenu == hPopupMenu )
1002       {
1003         if (pnPos)
1004           *pnPos = i;
1005         return TRUE;
1006       }
1007       /* Recursively search without updating pnPos */
1008       else if ( OLEMenu_FindMainMenuIndex( hsubmenu, hPopupMenu, NULL ) )
1009       {
1010         if (pnPos)
1011           *pnPos = i;
1012         return TRUE;
1013       }
1014     }
1015   }
1016
1017   return FALSE;
1018 }
1019
1020 /***********************************************************************
1021  *           OLEMenu_SetIsServerMenu
1022  *
1023  * Checks whether a popup menu belongs to a shared menu group which is
1024  * owned by the server, and sets the menu descriptor state accordingly.
1025  * All menu messages from these groups should be routed to the server.
1026  *
1027  * RETURNS: TRUE if the popup menu is part of a server owned group
1028  *          FASE if the popup menu is part of a container owned group
1029  */
1030 BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor )
1031 {
1032   UINT nPos = 0, nWidth, i;
1033
1034   pOleMenuDescriptor->bIsServerItem = FALSE;
1035
1036   /* Don't bother searching if the popup is the combined menu itself */
1037   if ( hmenu == pOleMenuDescriptor->hmenuCombined )
1038     return FALSE;
1039
1040   /* Find the menu item index in the shared OLE menu that this item belongs to */
1041   if ( !OLEMenu_FindMainMenuIndex( pOleMenuDescriptor->hmenuCombined, hmenu,  &nPos ) )
1042     return FALSE;
1043   
1044   /* The group widths array has counts for the number of elements
1045    * in the groups File, Edit, Container, Object, Window, Help.
1046    * The Edit, Object & Help groups belong to the server object
1047    * and the other three belong to the container.
1048    * Loop thru the group widths and locate the group we are a member of.
1049    */
1050   for ( i = 0, nWidth = 0; i < 6; i++ )
1051   {
1052     nWidth += pOleMenuDescriptor->mgw.width[i];
1053     if ( nPos < nWidth )
1054     {
1055       /* Odd elements are server menu widths */
1056       pOleMenuDescriptor->bIsServerItem = (i%2) ? TRUE : FALSE;
1057       break;
1058     }
1059   }
1060
1061   return pOleMenuDescriptor->bIsServerItem;
1062 }
1063
1064 /*************************************************************************
1065  * OLEMenu_CallWndProc
1066  * Thread scope WH_CALLWNDPROC hook proc filter function (callback)
1067  * This is invoked from a message hook installed in OleSetMenuDescriptor.
1068  */
1069 LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam)
1070 {
1071   LPCWPSTRUCT pMsg = NULL;
1072   HOLEMENU hOleMenu = 0;
1073   OleMenuDescriptor *pOleMenuDescriptor = NULL;
1074   OleMenuHookItem *pHookItem = NULL;
1075   WORD fuFlags;
1076   
1077   TRACE("%i, %04x, %08x\n", code, wParam, (unsigned)lParam );
1078
1079   /* Check if we're being asked to process the message */
1080   if ( HC_ACTION != code )
1081     goto NEXTHOOK;
1082       
1083   /* Retrieve the current message being dispatched from lParam */
1084   pMsg = (LPCWPSTRUCT)lParam;
1085
1086   /* Check if the message is destined for a window we are interested in:
1087    * If the window has an OLEMenu property we may need to dispatch
1088    * the menu message to its active objects window instead. */
1089
1090   hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
1091   if ( !hOleMenu )
1092     goto NEXTHOOK;
1093
1094   /* Get the menu descriptor */
1095   pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
1096   if ( !pOleMenuDescriptor ) /* Bad descriptor! */
1097     goto NEXTHOOK;
1098
1099   /* Process menu messages */
1100   switch( pMsg->message )
1101   {
1102     case WM_INITMENU:
1103     {
1104       /* Reset the menu descriptor state */
1105       pOleMenuDescriptor->bIsServerItem = FALSE;
1106
1107       /* Send this message to the server as well */
1108       SendMessageA( pOleMenuDescriptor->hwndActiveObject,
1109                   pMsg->message, pMsg->wParam, pMsg->lParam );
1110       goto NEXTHOOK;
1111     }
1112     
1113     case WM_INITMENUPOPUP:
1114     {
1115       /* Save the state for whether this is a server owned menu */
1116       OLEMenu_SetIsServerMenu( (HMENU)pMsg->wParam, pOleMenuDescriptor );
1117       break;
1118     }
1119     
1120     case WM_MENUSELECT:
1121     {
1122       fuFlags = HIWORD(pMsg->wParam);  /* Get flags */
1123       if ( fuFlags & MF_SYSMENU )
1124          goto NEXTHOOK;
1125
1126       /* Save the state for whether this is a server owned popup menu */
1127       else if ( fuFlags & MF_POPUP )
1128         OLEMenu_SetIsServerMenu( (HMENU)pMsg->lParam, pOleMenuDescriptor );
1129
1130       break;
1131     }
1132     
1133     case WM_DRAWITEM:
1134     {
1135       LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) pMsg->lParam;
1136       if ( pMsg->wParam != 0 || lpdis->CtlType != ODT_MENU )
1137         goto NEXTHOOK;  /* Not a menu message */
1138
1139       break;
1140     }
1141
1142     default:
1143       goto NEXTHOOK;
1144   }
1145
1146   /* If the message was for the server dispatch it accordingly */
1147   if ( pOleMenuDescriptor->bIsServerItem )
1148   {
1149     SendMessageA( pOleMenuDescriptor->hwndActiveObject,
1150                   pMsg->message, pMsg->wParam, pMsg->lParam );
1151   }
1152     
1153 NEXTHOOK:
1154   if ( pOleMenuDescriptor )
1155     GlobalUnlock( hOleMenu );
1156   
1157   /* Lookup the hook item for the current thread */
1158   if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) ) )
1159   {
1160     /* This should never fail!! */
1161     WARN("could not retrieve hHook for current thread!\n" );
1162     return 0;
1163   }
1164   
1165   /* Pass on the message to the next hooker */
1166   return CallNextHookEx( pHookItem->CallWndProc_hHook, code, wParam, lParam );
1167 }
1168
1169 /*************************************************************************
1170  * OLEMenu_GetMsgProc
1171  * Thread scope WH_GETMESSAGE hook proc filter function (callback)
1172  * This is invoked from a message hook installed in OleSetMenuDescriptor.
1173  */
1174 LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam)
1175 {
1176   LPMSG pMsg = NULL;
1177   HOLEMENU hOleMenu = 0;
1178   OleMenuDescriptor *pOleMenuDescriptor = NULL;
1179   OleMenuHookItem *pHookItem = NULL;
1180   WORD wCode;
1181   
1182   TRACE("%i, %04x, %08x\n", code, wParam, (unsigned)lParam );
1183
1184   /* Check if we're being asked to process a  messages */
1185   if ( HC_ACTION != code )
1186     goto NEXTHOOK;
1187       
1188   /* Retrieve the current message being dispatched from lParam */
1189   pMsg = (LPMSG)lParam;
1190
1191   /* Check if the message is destined for a window we are interested in:
1192    * If the window has an OLEMenu property we may need to dispatch
1193    * the menu message to its active objects window instead. */
1194
1195   hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
1196   if ( !hOleMenu )
1197     goto NEXTHOOK;
1198
1199   /* Process menu messages */
1200   switch( pMsg->message )
1201   {
1202     case WM_COMMAND:
1203     {
1204       wCode = HIWORD(pMsg->wParam);  /* Get notification code */
1205       if ( wCode )
1206         goto NEXTHOOK;  /* Not a menu message */
1207       break;
1208     }
1209     default:
1210       goto NEXTHOOK;
1211   }
1212
1213   /* Get the menu descriptor */
1214   pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
1215   if ( !pOleMenuDescriptor ) /* Bad descriptor! */
1216     goto NEXTHOOK;
1217
1218   /* If the message was for the server dispatch it accordingly */
1219   if ( pOleMenuDescriptor->bIsServerItem )
1220   {
1221     /* Change the hWnd in the message to the active objects hWnd.
1222      * The message loop which reads this message will automatically
1223      * dispatch it to the embedded objects window. */
1224     pMsg->hwnd = pOleMenuDescriptor->hwndActiveObject;
1225   }
1226     
1227 NEXTHOOK:
1228   if ( pOleMenuDescriptor )
1229     GlobalUnlock( hOleMenu );
1230   
1231   /* Lookup the hook item for the current thread */
1232   if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) ) )
1233   {
1234     /* This should never fail!! */
1235     WARN("could not retrieve hHook for current thread!\n" );
1236     return FALSE;
1237   }
1238   
1239   /* Pass on the message to the next hooker */
1240   return CallNextHookEx( pHookItem->GetMsg_hHook, code, wParam, lParam );
1241 }
1242
1243 /***********************************************************************
1244  * OleCreateMenuDescriptor [OLE32.97]
1245  * Creates an OLE menu descriptor for OLE to use when dispatching
1246  * menu messages and commands.
1247  *
1248  * PARAMS:
1249  *    hmenuCombined  -  Handle to the objects combined menu
1250  *    lpMenuWidths   -  Pointer to array of 6 LONG's indicating menus per group
1251  *
1252  */
1253 HOLEMENU WINAPI OleCreateMenuDescriptor(
1254   HMENU                hmenuCombined,
1255   LPOLEMENUGROUPWIDTHS lpMenuWidths)
1256 {
1257   HOLEMENU hOleMenu;
1258   OleMenuDescriptor *pOleMenuDescriptor;
1259   int i;
1260
1261   if ( !hmenuCombined || !lpMenuWidths )
1262     return 0;
1263
1264   /* Create an OLE menu descriptor */
1265   if ( !(hOleMenu = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
1266                                 sizeof(OleMenuDescriptor) ) ) )
1267   return 0;
1268
1269   pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
1270   if ( !pOleMenuDescriptor )
1271     return 0;
1272
1273   /* Initialize menu group widths and hmenu */
1274   for ( i = 0; i < 6; i++ )
1275     pOleMenuDescriptor->mgw.width[i] = lpMenuWidths->width[i];
1276   
1277   pOleMenuDescriptor->hmenuCombined = hmenuCombined;
1278   pOleMenuDescriptor->bIsServerItem = FALSE;
1279   GlobalUnlock( hOleMenu );
1280       
1281   return hOleMenu;
1282 }
1283
1284 /***********************************************************************
1285  * OleDestroyMenuDescriptor [OLE32.99]
1286  * Destroy the shared menu descriptor
1287  */
1288 HRESULT WINAPI OleDestroyMenuDescriptor(
1289   HOLEMENU hmenuDescriptor)
1290 {
1291   if ( hmenuDescriptor )
1292     GlobalFree( hmenuDescriptor );
1293         return S_OK;
1294 }
1295
1296 /***********************************************************************
1297  * OleSetMenuDescriptor [OLE32.129]
1298  * Installs or removes OLE dispatching code for the containers frame window
1299  * FIXME: The lpFrame and lpActiveObject parameters are currently ignored
1300  * OLE should install context sensitive help F1 filtering for the app when
1301  * these are non null.
1302  * 
1303  * PARAMS:
1304  *     hOleMenu         Handle to composite menu descriptor
1305  *     hwndFrame        Handle to containers frame window
1306  *     hwndActiveObject Handle to objects in-place activation window
1307  *     lpFrame          Pointer to IOleInPlaceFrame on containers window
1308  *     lpActiveObject   Pointer to IOleInPlaceActiveObject on active in-place object
1309  *
1310  * RETURNS:
1311  *      S_OK                               - menu installed correctly
1312  *      E_FAIL, E_INVALIDARG, E_UNEXPECTED - failure
1313  */
1314 HRESULT WINAPI OleSetMenuDescriptor(
1315   HOLEMENU               hOleMenu,
1316   HWND                   hwndFrame,
1317   HWND                   hwndActiveObject,
1318   LPOLEINPLACEFRAME        lpFrame,
1319   LPOLEINPLACEACTIVEOBJECT lpActiveObject)
1320 {
1321   OleMenuDescriptor *pOleMenuDescriptor = NULL;
1322
1323   /* Check args */
1324   if ( !hwndFrame || (hOleMenu && !hwndActiveObject) )
1325     return E_INVALIDARG;
1326
1327   if ( lpFrame || lpActiveObject )
1328   {
1329      FIXME("(%x, %x, %x, %p, %p), Context sensitive help filtering not implemented!\n",
1330         (unsigned int)hOleMenu,
1331         hwndFrame,
1332         hwndActiveObject,
1333         lpFrame,
1334         lpActiveObject);
1335   }
1336
1337   /* Set up a message hook to intercept the containers frame window messages.
1338    * The message filter is responsible for dispatching menu messages from the
1339    * shared menu which are intended for the object.
1340    */
1341
1342   if ( hOleMenu )  /* Want to install dispatching code */
1343   {
1344     /* If OLEMenu hooks are already installed for this thread, fail
1345      * Note: This effectively means that OleSetMenuDescriptor cannot
1346      * be called twice in succession on the same frame window
1347      * without first calling it with a null hOleMenu to uninstall */
1348     if ( OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) )
1349   return E_FAIL;
1350         
1351     /* Get the menu descriptor */
1352     pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
1353     if ( !pOleMenuDescriptor )
1354       return E_UNEXPECTED;
1355
1356     /* Update the menu descriptor */
1357     pOleMenuDescriptor->hwndFrame = hwndFrame;
1358     pOleMenuDescriptor->hwndActiveObject = hwndActiveObject;
1359
1360     GlobalUnlock( hOleMenu );
1361     pOleMenuDescriptor = NULL;
1362     
1363     /* Add a menu descriptor windows property to the frame window */
1364     SetPropA( hwndFrame, "PROP_OLEMenuDescriptor", hOleMenu );
1365
1366     /* Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC */
1367     if ( !OLEMenu_InstallHooks( GetCurrentThreadId() ) )
1368       return E_FAIL;
1369   }
1370   else  /* Want to uninstall dispatching code */
1371   {
1372     /* Uninstall the hooks */
1373     if ( !OLEMenu_UnInstallHooks( GetCurrentThreadId() ) )
1374       return E_FAIL;
1375     
1376     /* Remove the menu descriptor property from the frame window */
1377     RemovePropA( hwndFrame, "PROP_OLEMenuDescriptor" );
1378   }
1379       
1380   return S_OK;
1381 }
1382
1383 /***********************************************************************
1384  * ReleaseStgMedium [OLE32.140]
1385  */
1386 void WINAPI ReleaseStgMedium(
1387   STGMEDIUM* pmedium)
1388 {
1389   switch (pmedium->tymed)
1390   {
1391     case TYMED_HGLOBAL:
1392     {
1393       if ( (pmedium->pUnkForRelease==0) && 
1394            (pmedium->u.hGlobal!=0) )
1395         GlobalFree(pmedium->u.hGlobal);
1396
1397       pmedium->u.hGlobal = 0;
1398       break;
1399     }
1400     case TYMED_FILE:
1401     {
1402       if (pmedium->u.lpszFileName!=0)
1403       {
1404         if (pmedium->pUnkForRelease==0)
1405         {
1406           DeleteFileW(pmedium->u.lpszFileName);
1407         }
1408         
1409         CoTaskMemFree(pmedium->u.lpszFileName);
1410       }
1411
1412       pmedium->u.lpszFileName = 0;
1413       break;
1414     }
1415     case TYMED_ISTREAM:
1416     {
1417       if (pmedium->u.pstm!=0)
1418       {
1419         IStream_Release(pmedium->u.pstm);
1420       }
1421
1422       pmedium->u.pstm = 0;
1423       break;
1424     }
1425     case TYMED_ISTORAGE:
1426     {
1427       if (pmedium->u.pstg!=0)
1428       {
1429         IStorage_Release(pmedium->u.pstg);
1430       }
1431
1432       pmedium->u.pstg = 0;
1433       break;
1434     }
1435     case TYMED_GDI:
1436     {
1437       if ( (pmedium->pUnkForRelease==0) && 
1438            (pmedium->u.hGlobal!=0) )
1439         DeleteObject(pmedium->u.hGlobal);
1440
1441       pmedium->u.hGlobal = 0;
1442       break;
1443     }
1444     case TYMED_MFPICT:
1445     {
1446       if ( (pmedium->pUnkForRelease==0) && 
1447            (pmedium->u.hMetaFilePict!=0) )
1448       {
1449         DeleteMetaFile(pmedium->u.hMetaFilePict);
1450         GlobalFree(pmedium->u.hMetaFilePict);
1451       }
1452
1453       pmedium->u.hMetaFilePict = 0;
1454       break;
1455     }
1456     case TYMED_ENHMF:
1457     {
1458       if ( (pmedium->pUnkForRelease==0) && 
1459            (pmedium->u.hEnhMetaFile!=0) )
1460       {
1461         DeleteEnhMetaFile(pmedium->u.hEnhMetaFile);
1462       }
1463
1464       pmedium->u.hEnhMetaFile = 0;
1465       break;
1466     }
1467     case TYMED_NULL:
1468     default:
1469       break;
1470   }
1471
1472   /*
1473    * After cleaning up, the unknown is released
1474    */
1475   if (pmedium->pUnkForRelease!=0)
1476   {
1477     IUnknown_Release(pmedium->pUnkForRelease);
1478     pmedium->pUnkForRelease = 0;
1479   }
1480 }
1481
1482 /***
1483  * OLEDD_Initialize()
1484  *
1485  * Initializes the OLE drag and drop data structures.
1486  */
1487 static void OLEDD_Initialize()
1488 {
1489     WNDCLASSA wndClass;
1490
1491     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1492     wndClass.style         = CS_GLOBALCLASS;
1493     wndClass.lpfnWndProc   = (WNDPROC)OLEDD_DragTrackerWindowProc;
1494     wndClass.cbClsExtra    = 0;
1495     wndClass.cbWndExtra    = sizeof(TrackerWindowInfo*);
1496     wndClass.hCursor       = 0;
1497     wndClass.hbrBackground = 0;
1498     wndClass.lpszClassName = OLEDD_DRAGTRACKERCLASS;
1499  
1500     RegisterClassA (&wndClass);
1501 }
1502
1503 /***
1504  * OLEDD_UnInitialize()
1505  *
1506  * Releases the OLE drag and drop data structures.
1507  */
1508 static void OLEDD_UnInitialize()
1509 {
1510   /*
1511    * Simply empty the list.
1512    */
1513   while (targetListHead!=NULL)
1514   {
1515     RevokeDragDrop(targetListHead->hwndTarget);
1516   }
1517 }
1518
1519 /***
1520  * OLEDD_InsertDropTarget()
1521  *
1522  * Insert the target node in the tree.
1523  */
1524 static void OLEDD_InsertDropTarget(DropTargetNode* nodeToAdd)
1525 {
1526   DropTargetNode*  curNode;
1527   DropTargetNode** parentNodeLink;
1528
1529   /*
1530    * Iterate the tree to find the insertion point.
1531    */
1532   curNode        = targetListHead;
1533   parentNodeLink = &targetListHead;
1534
1535   while (curNode!=NULL)
1536   {
1537     if (nodeToAdd->hwndTarget<curNode->hwndTarget)
1538     {
1539       /*
1540        * If the node we want to add has a smaller HWND, go left
1541        */
1542       parentNodeLink = &curNode->prevDropTarget;
1543       curNode        =  curNode->prevDropTarget;
1544     }
1545     else if (nodeToAdd->hwndTarget>curNode->hwndTarget)
1546     {
1547       /*
1548        * If the node we want to add has a larger HWND, go right
1549        */
1550       parentNodeLink = &curNode->nextDropTarget;
1551       curNode        =  curNode->nextDropTarget;
1552     }
1553     else
1554     {
1555       /*
1556        * The item was found in the list. It shouldn't have been there
1557        */
1558       assert(FALSE);
1559       return;
1560     }
1561   }
1562
1563   /*
1564    * If we get here, we have found a spot for our item. The parentNodeLink
1565    * pointer points to the pointer that we have to modify. 
1566    * The curNode should be NULL. We just have to establish the link and Voila!
1567    */
1568   assert(curNode==NULL);
1569   assert(parentNodeLink!=NULL);
1570   assert(*parentNodeLink==NULL);
1571
1572   *parentNodeLink=nodeToAdd;
1573 }
1574
1575 /***
1576  * OLEDD_ExtractDropTarget()
1577  *
1578  * Removes the target node from the tree.
1579  */
1580 static DropTargetNode* OLEDD_ExtractDropTarget(HWND hwndOfTarget)
1581 {
1582   DropTargetNode*  curNode;
1583   DropTargetNode** parentNodeLink;
1584
1585   /*
1586    * Iterate the tree to find the insertion point.
1587    */
1588   curNode        = targetListHead;
1589   parentNodeLink = &targetListHead;
1590
1591   while (curNode!=NULL)
1592   {
1593     if (hwndOfTarget<curNode->hwndTarget)
1594     {
1595       /*
1596        * If the node we want to add has a smaller HWND, go left
1597        */
1598       parentNodeLink = &curNode->prevDropTarget;
1599       curNode        =  curNode->prevDropTarget;
1600     }
1601     else if (hwndOfTarget>curNode->hwndTarget)
1602     {
1603       /*
1604        * If the node we want to add has a larger HWND, go right
1605        */
1606       parentNodeLink = &curNode->nextDropTarget;
1607       curNode        =  curNode->nextDropTarget;
1608     }
1609     else
1610     {
1611       /*
1612        * The item was found in the list. Detach it from it's parent and 
1613        * re-insert it's kids in the tree.
1614        */
1615       assert(parentNodeLink!=NULL);
1616       assert(*parentNodeLink==curNode);
1617
1618       /*
1619        * We arbitrately re-attach the left sub-tree to the parent.
1620        */
1621       *parentNodeLink = curNode->prevDropTarget;
1622
1623       /*
1624        * And we re-insert the right subtree
1625        */
1626       if (curNode->nextDropTarget!=NULL)
1627       {
1628         OLEDD_InsertDropTarget(curNode->nextDropTarget);
1629       }
1630
1631       /*
1632        * The node we found is still a valid node once we complete
1633        * the unlinking of the kids.
1634        */
1635       curNode->nextDropTarget=NULL;
1636       curNode->prevDropTarget=NULL;
1637
1638       return curNode;
1639     }
1640   }
1641
1642   /*
1643    * If we get here, the node is not in the tree
1644    */
1645   return NULL;
1646 }
1647
1648 /***
1649  * OLEDD_FindDropTarget()
1650  *
1651  * Finds information about the drop target.
1652  */
1653 static DropTargetNode* OLEDD_FindDropTarget(HWND hwndOfTarget)
1654 {
1655   DropTargetNode*  curNode;
1656
1657   /*
1658    * Iterate the tree to find the HWND value.
1659    */
1660   curNode        = targetListHead;
1661
1662   while (curNode!=NULL)
1663   {
1664     if (hwndOfTarget<curNode->hwndTarget)
1665     {
1666       /*
1667        * If the node we want to add has a smaller HWND, go left
1668        */
1669       curNode =  curNode->prevDropTarget;
1670     }
1671     else if (hwndOfTarget>curNode->hwndTarget)
1672     {
1673       /*
1674        * If the node we want to add has a larger HWND, go right
1675        */
1676       curNode =  curNode->nextDropTarget;
1677     }
1678     else
1679     {
1680       /*
1681        * The item was found in the list.
1682        */
1683       return curNode;
1684     }
1685   }
1686
1687   /*
1688    * If we get here, the item is not in the list
1689    */
1690   return NULL;
1691 }
1692
1693 /***
1694  * OLEDD_DragTrackerWindowProc()
1695  *
1696  * This method is the WindowProcedure of the drag n drop tracking
1697  * window. During a drag n Drop operation, an invisible window is created
1698  * to receive the user input and act upon it. This procedure is in charge
1699  * of this behavior.
1700  */
1701 static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
1702                          HWND   hwnd, 
1703                          UINT   uMsg,
1704                          WPARAM wParam, 
1705                          LPARAM   lParam)
1706 {
1707   switch (uMsg)
1708   {
1709     case WM_CREATE:
1710     {
1711       LPCREATESTRUCTA createStruct = (LPCREATESTRUCTA)lParam;
1712
1713       SetWindowLongA(hwnd, 0, (LONG)createStruct->lpCreateParams); 
1714
1715       
1716       break;
1717     }
1718     case WM_MOUSEMOVE:
1719     {
1720       TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
1721       POINT            mousePos;
1722
1723       /*
1724        * Get the current mouse position in screen coordinates.
1725        */
1726       mousePos.x = LOWORD(lParam);
1727       mousePos.y = HIWORD(lParam);
1728       ClientToScreen(hwnd, &mousePos);
1729
1730       /*
1731        * Track the movement of the mouse.
1732        */
1733       OLEDD_TrackMouseMove(trackerInfo, mousePos, wParam);
1734
1735       break;
1736     }
1737     case WM_LBUTTONUP:
1738     case WM_MBUTTONUP:
1739     case WM_RBUTTONUP:
1740     case WM_LBUTTONDOWN:
1741     case WM_MBUTTONDOWN:
1742     case WM_RBUTTONDOWN:
1743     {
1744       TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
1745       POINT            mousePos;
1746
1747       /*
1748        * Get the current mouse position in screen coordinates.
1749        */
1750       mousePos.x = LOWORD(lParam);
1751       mousePos.y = HIWORD(lParam);
1752       ClientToScreen(hwnd, &mousePos);
1753
1754       /*
1755        * Notify everyone that the button state changed
1756        * TODO: Check if the "escape" key was pressed.
1757        */
1758       OLEDD_TrackStateChange(trackerInfo, mousePos, wParam);
1759
1760       break;
1761     }
1762   }
1763
1764   /*
1765    * This is a window proc after all. Let's call the default.
1766    */
1767   return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1768 }
1769
1770 /***
1771  * OLEDD_TrackMouseMove()
1772  *
1773  * This method is invoked while a drag and drop operation is in effect.
1774  * it will generate the appropriate callbacks in the drop source
1775  * and drop target. It will also provide the expected feedback to
1776  * the user.
1777  *
1778  * params:
1779  *    trackerInfo - Pointer to the structure identifying the
1780  *                  drag & drop operation that is currently
1781  *                  active.
1782  *    mousePos    - Current position of the mouse in screen
1783  *                  coordinates.
1784  *    keyState    - Contains the state of the shift keys and the
1785  *                  mouse buttons (MK_LBUTTON and the like)
1786  */
1787 static void OLEDD_TrackMouseMove(
1788   TrackerWindowInfo* trackerInfo,
1789   POINT            mousePos,
1790   DWORD              keyState)
1791 {
1792   HWND   hwndNewTarget = 0;
1793   HRESULT  hr = S_OK;
1794
1795   /*
1796    * Get the handle of the window under the mouse
1797    */
1798   hwndNewTarget = WindowFromPoint(mousePos);
1799
1800   /*
1801    * Every time, we re-initialize the effects passed to the
1802    * IDropTarget to the effects allowed by the source.
1803    */
1804   *trackerInfo->pdwEffect = trackerInfo->dwOKEffect;
1805
1806   /*
1807    * If we are hovering over the same target as before, send the
1808    * DragOver notification
1809    */
1810   if ( (trackerInfo->curDragTarget != 0) && 
1811        (trackerInfo->curDragTargetHWND==hwndNewTarget) )
1812   {
1813     POINTL  mousePosParam;
1814     
1815     /*
1816      * The documentation tells me that the coordinate should be in the target
1817      * window's coordinate space. However, the tests I made tell me the
1818      * coordinates should be in screen coordinates.
1819      */
1820     mousePosParam.x = mousePos.x;
1821     mousePosParam.y = mousePos.y;
1822     
1823     IDropTarget_DragOver(trackerInfo->curDragTarget,
1824                          keyState,
1825                          mousePosParam,
1826                          trackerInfo->pdwEffect);
1827   }
1828   else
1829   {
1830     DropTargetNode* newDropTargetNode = 0;
1831     
1832     /*
1833      * If we changed window, we have to notify our old target and check for
1834      * the new one.
1835      */
1836     if (trackerInfo->curDragTarget!=0)
1837     {
1838       IDropTarget_DragLeave(trackerInfo->curDragTarget);
1839     }
1840     
1841     /*
1842      * Make sure we're hovering over a window.
1843      */
1844     if (hwndNewTarget!=0)
1845     {
1846       /*
1847        * Find-out if there is a drag target under the mouse
1848        */
1849       newDropTargetNode = OLEDD_FindDropTarget(hwndNewTarget);
1850       
1851       trackerInfo->curDragTargetHWND = hwndNewTarget;
1852       trackerInfo->curDragTarget     = newDropTargetNode ? newDropTargetNode->dropTarget : 0;
1853       
1854       /*
1855        * If there is, notify it that we just dragged-in
1856        */
1857       if (trackerInfo->curDragTarget!=0)
1858       {
1859         POINTL  mousePosParam;
1860         
1861         /*
1862          * The documentation tells me that the coordinate should be in the target
1863          * window's coordinate space. However, the tests I made tell me the
1864          * coordinates should be in screen coordinates.
1865          */
1866         mousePosParam.x = mousePos.x;
1867         mousePosParam.y = mousePos.y;
1868         
1869         IDropTarget_DragEnter(trackerInfo->curDragTarget,
1870                               trackerInfo->dataObject,
1871                               keyState,
1872                               mousePosParam,
1873                               trackerInfo->pdwEffect);
1874       }
1875     }
1876     else
1877     {
1878       /*
1879        * The mouse is not over a window so we don't track anything.
1880        */
1881       trackerInfo->curDragTargetHWND = 0;
1882       trackerInfo->curDragTarget     = 0;
1883     }
1884   }
1885
1886   /*
1887    * Now that we have done that, we have to tell the source to give 
1888    * us feedback on the work being done by the target.  If we don't 
1889    * have a target, simulate no effect.
1890    */
1891   if (trackerInfo->curDragTarget==0)
1892   {
1893     *trackerInfo->pdwEffect = DROPEFFECT_NONE;
1894   }
1895
1896   hr = IDropSource_GiveFeedback(trackerInfo->dropSource,
1897                                 *trackerInfo->pdwEffect);
1898
1899   /*
1900    * When we ask for feedback from the drop source, sometimes it will
1901    * do all the necessary work and sometimes it will not handle it
1902    * when that's the case, we must display the standard drag and drop
1903    * cursors.
1904    */
1905   if (hr==DRAGDROP_S_USEDEFAULTCURSORS)
1906   {
1907     if ( (*trackerInfo->pdwEffect & DROPEFFECT_MOVE) ||
1908          (*trackerInfo->pdwEffect & DROPEFFECT_COPY) ||
1909          (*trackerInfo->pdwEffect & DROPEFFECT_LINK) )
1910     {
1911       SetCursor(LoadCursorA(0, IDC_SIZEALLA));
1912     }
1913     else
1914     {
1915       SetCursor(LoadCursorA(0, IDC_NOA));
1916     }
1917   }  
1918 }
1919
1920 /***
1921  * OLEDD_TrackStateChange()
1922  *
1923  * This method is invoked while a drag and drop operation is in effect.
1924  * It is used to notify the drop target/drop source callbacks when
1925  * the state of the keyboard or mouse button change.
1926  *
1927  * params:
1928  *    trackerInfo - Pointer to the structure identifying the
1929  *                  drag & drop operation that is currently
1930  *                  active.
1931  *    mousePos    - Current position of the mouse in screen
1932  *                  coordinates.
1933  *    keyState    - Contains the state of the shift keys and the
1934  *                  mouse buttons (MK_LBUTTON and the like)
1935  */
1936 static void OLEDD_TrackStateChange(
1937   TrackerWindowInfo* trackerInfo,
1938   POINT            mousePos,
1939   DWORD              keyState)
1940 {
1941   /*
1942    * Ask the drop source what to do with the operation.
1943    */
1944   trackerInfo->returnValue = IDropSource_QueryContinueDrag(
1945                                trackerInfo->dropSource,
1946                                trackerInfo->escPressed, 
1947                                keyState);
1948   
1949   /*
1950    * All the return valued will stop the operation except the S_OK
1951    * return value.
1952    */
1953   if (trackerInfo->returnValue!=S_OK)
1954   {
1955     /*
1956      * Make sure the message loop in DoDragDrop stops
1957      */
1958     trackerInfo->trackingDone = TRUE;
1959
1960     /*
1961      * Release the mouse in case the drop target decides to show a popup 
1962      * or a menu or something.
1963      */
1964     ReleaseCapture();
1965     
1966     /*
1967      * If we end-up over a target, drop the object in the target or 
1968      * inform the target that the operation was cancelled.
1969      */
1970     if (trackerInfo->curDragTarget!=0)
1971     {
1972       switch (trackerInfo->returnValue)
1973       {
1974         /*
1975          * If the source wants us to complete the operation, we tell 
1976          * the drop target that we just dropped the object in it.
1977          */
1978         case DRAGDROP_S_DROP:
1979         {
1980           POINTL  mousePosParam;
1981         
1982           /*
1983            * The documentation tells me that the coordinate should be 
1984            * in the target window's coordinate space. However, the tests
1985            * I made tell me the coordinates should be in screen coordinates.
1986            */
1987           mousePosParam.x = mousePos.x;
1988           mousePosParam.y = mousePos.y;
1989           
1990           IDropTarget_Drop(trackerInfo->curDragTarget,
1991                            trackerInfo->dataObject,
1992                            keyState,
1993                            mousePosParam,
1994                            trackerInfo->pdwEffect);
1995           break;
1996         }
1997         /*
1998          * If the source told us that we should cancel, fool the drop 
1999          * target by telling it that the mouse left it's window.
2000          * Also set the drop effect to "NONE" in case the application 
2001          * ignores the result of DoDragDrop.
2002          */
2003         case DRAGDROP_S_CANCEL:
2004           IDropTarget_DragLeave(trackerInfo->curDragTarget);
2005           *trackerInfo->pdwEffect = DROPEFFECT_NONE;
2006           break;
2007       }
2008     }
2009   }
2010 }
2011
2012 /***
2013  * OLEDD_GetButtonState()
2014  *
2015  * This method will use the current state of the keyboard to build
2016  * a button state mask equivalent to the one passed in the
2017  * WM_MOUSEMOVE wParam.
2018  */
2019 static DWORD OLEDD_GetButtonState()
2020 {
2021   BYTE  keyboardState[256];
2022   DWORD keyMask = 0;
2023
2024   GetKeyboardState(keyboardState);
2025
2026   if ( (keyboardState[VK_SHIFT] & 0x80) !=0)
2027     keyMask |= MK_SHIFT;
2028
2029   if ( (keyboardState[VK_CONTROL] & 0x80) !=0)
2030     keyMask |= MK_CONTROL;
2031
2032   if ( (keyboardState[VK_LBUTTON] & 0x80) !=0)
2033     keyMask |= MK_LBUTTON;
2034
2035   if ( (keyboardState[VK_RBUTTON] & 0x80) !=0)
2036     keyMask |= MK_RBUTTON;
2037
2038   if ( (keyboardState[VK_MBUTTON] & 0x80) !=0)
2039     keyMask |= MK_MBUTTON;
2040
2041   return keyMask;
2042 }
2043
2044 /***
2045  * OLEDD_GetButtonState()
2046  *
2047  * This method will read the default value of the registry key in
2048  * parameter and extract a DWORD value from it. The registry key value
2049  * can be in a string key or a DWORD key.
2050  *
2051  * params:
2052  *     regKey   - Key to read the default value from
2053  *     pdwValue - Pointer to the location where the DWORD 
2054  *                value is returned. This value is not modified
2055  *                if the value is not found.
2056  */
2057
2058 static void OLEUTL_ReadRegistryDWORDValue(
2059   HKEY   regKey, 
2060   DWORD* pdwValue)
2061 {
2062   char  buffer[20];
2063   DWORD dwKeyType;
2064   DWORD cbData = 20;
2065   LONG  lres;
2066
2067   lres = RegQueryValueExA(regKey,
2068                           "",
2069                           NULL,
2070                           &dwKeyType,
2071                           (LPBYTE)buffer,
2072                           &cbData);
2073
2074   if (lres==ERROR_SUCCESS)
2075   {
2076     switch (dwKeyType)
2077     {
2078       case REG_DWORD:
2079         *pdwValue = *(DWORD*)buffer;
2080         break;
2081       case REG_EXPAND_SZ:
2082       case REG_MULTI_SZ:
2083       case REG_SZ:
2084         *pdwValue = (DWORD)strtoul(buffer, NULL, 10);
2085         break;
2086     }
2087   }
2088 }
2089
2090 /******************************************************************************
2091  * OleMetaFilePictFromIconAndLabel
2092  *
2093  * Returns a global memory handle to a metafile which contains the icon and
2094  * label given.
2095  * I guess the result of that should look somehow like desktop icons.
2096  * If no hIcon is given, we load the icon via lpszSourceFile and iIconIndex.
2097  * This code might be wrong at some places.
2098  */
2099 HGLOBAL16 WINAPI OleMetaFilePictFromIconAndLabel16(
2100         HICON16 hIcon,
2101         LPCOLESTR16 lpszLabel,
2102         LPCOLESTR16 lpszSourceFile,
2103         UINT16 iIconIndex
2104 ) {
2105     METAFILEPICT16 *mf;
2106     HGLOBAL16 hmf;
2107     HDC16 hdc;
2108
2109     FIXME("(%04x, '%s', '%s', %d): incorrect metrics, please try to correct them !\n\n\n", hIcon, lpszLabel, lpszSourceFile, iIconIndex);
2110
2111     if (!hIcon) {
2112         if (lpszSourceFile) {
2113             HINSTANCE16 hInstance = LoadLibrary16(lpszSourceFile);
2114
2115             /* load the icon at index from lpszSourceFile */
2116             hIcon = (HICON16)LoadIconA(hInstance, (LPCSTR)(DWORD)iIconIndex);
2117             FreeLibrary16(hInstance);
2118         } else
2119             return (HGLOBAL)NULL;
2120     }
2121
2122     hdc = CreateMetaFile16(NULL);
2123     DrawIcon(hdc, 0, 0, hIcon); /* FIXME */
2124     TextOut16(hdc, 0, 0, lpszLabel, 1); /* FIXME */
2125     hmf = GlobalAlloc16(0, sizeof(METAFILEPICT16));
2126     mf = (METAFILEPICT16 *)GlobalLock16(hmf);
2127     mf->mm = MM_ANISOTROPIC;
2128     mf->xExt = 20; /* FIXME: bogus */
2129     mf->yExt = 20; /* dito */
2130     mf->hMF = CloseMetaFile16(hdc);
2131     return hmf;
2132 }