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