No longer directly accessing debuggee memory.
[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 <stdio.h>
12 #include <string.h>
13
14 #include "windef.h"
15 #include "wingdi.h"
16 #include "winuser.h"
17 #include "winerror.h"
18 #include "ole2.h"
19 #include "process.h"
20 #include "commctrl.h"
21 #include "wine/obj_clientserver.h"
22 #include "wine/wingdi16.h"
23 #include "debugtools.h"
24 #include "ole2ver.h"
25 #include "winreg.h"
26
27 DEFAULT_DEBUG_CHANNEL(ole)
28
29 /******************************************************************************
30  * These are static/global variables and internal data structures that the 
31  * OLE module uses to maintain it's state.
32  */
33 typedef struct tagDropTargetNode
34 {
35   HWND          hwndTarget;
36   IDropTarget*    dropTarget;
37   struct tagDropTargetNode* prevDropTarget;
38   struct tagDropTargetNode* nextDropTarget;
39 } DropTargetNode;
40
41 typedef struct tagTrackerWindowInfo
42 {
43   IDataObject* dataObject;
44   IDropSource* dropSource;
45   DWORD        dwOKEffect;
46   DWORD*       pdwEffect;
47   BOOL       trackingDone;
48   HRESULT      returnValue;
49
50   BOOL       escPressed;
51   HWND       curDragTargetHWND;
52   IDropTarget* curDragTarget;
53 } TrackerWindowInfo;
54
55 typedef struct tagOleMenuDescriptor  /* OleMenuDescriptor */
56 {
57   HWND               hwndFrame;         /* The containers frame window */
58   HWND               hwndActiveObject;  /* The active objects window */
59   OLEMENUGROUPWIDTHS mgw;               /* OLE menu group widths for the shared menu */
60   HMENU              hmenuCombined;     /* The combined menu */
61   BOOL               bIsServerItem;     /* True if the currently open popup belongs to the server */
62 } OleMenuDescriptor;
63
64 typedef struct tagOleMenuHookItem   /* OleMenu hook item in per thread hook list */
65 {
66   DWORD tid;                /* Thread Id  */
67   HANDLE hHeap;             /* Heap this is allocated from */
68   HHOOK GetMsg_hHook;       /* message hook for WH_GETMESSAGE */
69   HHOOK CallWndProc_hHook;  /* message hook for WH_CALLWNDPROC */
70   struct tagOleMenuHookItem *next;
71 } OleMenuHookItem;
72
73 static OleMenuHookItem *hook_list;
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 );
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("Returning version %d, build %d.\n", rmm, rup);
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(clsidKey,
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 }
827
828 /***
829  * OLEMenu_UnInitialize()
830  *
831  * Releases the OLEMENU data structures.
832  */
833 static void OLEMenu_UnInitialize()
834 {
835 }
836
837 /*************************************************************************
838  * OLEMenu_InstallHooks
839  * Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
840  *
841  * RETURNS: TRUE if message hooks were succesfully installed
842  *          FALSE on failure
843  */
844 BOOL OLEMenu_InstallHooks( DWORD tid )
845 {
846   OleMenuHookItem *pHookItem = NULL;
847
848   /* Create an entry for the hook table */
849   if ( !(pHookItem = HeapAlloc(GetProcessHeap(), 0,
850                                sizeof(OleMenuHookItem)) ) )
851     return FALSE;
852
853   pHookItem->tid = tid;
854   pHookItem->hHeap = GetProcessHeap();
855   
856   /* Install a thread scope message hook for WH_GETMESSAGE */
857   pHookItem->GetMsg_hHook = SetWindowsHookExA( WH_GETMESSAGE, OLEMenu_GetMsgProc,
858                                                0, GetCurrentThreadId() );
859   if ( !pHookItem->GetMsg_hHook )
860     goto CLEANUP;
861
862   /* Install a thread scope message hook for WH_CALLWNDPROC */
863   pHookItem->CallWndProc_hHook = SetWindowsHookExA( WH_CALLWNDPROC, OLEMenu_CallWndProc,
864                                                     0, GetCurrentThreadId() );
865   if ( !pHookItem->CallWndProc_hHook )
866     goto CLEANUP;
867
868   /* Insert the hook table entry */
869   pHookItem->next = hook_list;
870   hook_list = pHookItem;
871   
872   return TRUE;
873   
874 CLEANUP:
875   /* Unhook any hooks */
876   if ( pHookItem->GetMsg_hHook )
877     UnhookWindowsHookEx( pHookItem->GetMsg_hHook );
878   if ( pHookItem->CallWndProc_hHook )
879     UnhookWindowsHookEx( pHookItem->CallWndProc_hHook );
880   /* Release the hook table entry */
881   HeapFree(pHookItem->hHeap, 0, pHookItem );
882   
883   return FALSE;
884 }
885
886 /*************************************************************************
887  * OLEMenu_UnInstallHooks
888  * UnInstall thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
889  *
890  * RETURNS: TRUE if message hooks were succesfully installed
891  *          FALSE on failure
892  */
893 BOOL OLEMenu_UnInstallHooks( DWORD tid )
894 {
895   OleMenuHookItem *pHookItem = NULL;
896   OleMenuHookItem **ppHook = &hook_list;
897
898   while (*ppHook)
899   {
900       if ((*ppHook)->tid == tid)
901       {
902           pHookItem = *ppHook;
903           *ppHook = pHookItem->next;
904           break;
905       }
906       ppHook = &(*ppHook)->next;
907   }
908   if (!pHookItem) return FALSE;
909
910   /* Uninstall the hooks installed for this thread */
911   if ( !UnhookWindowsHookEx( pHookItem->GetMsg_hHook ) )
912     goto CLEANUP;
913   if ( !UnhookWindowsHookEx( pHookItem->CallWndProc_hHook ) )
914     goto CLEANUP;
915
916   /* Release the hook table entry */
917   HeapFree(pHookItem->hHeap, 0, pHookItem );
918
919   return TRUE;
920
921 CLEANUP:
922   /* Release the hook table entry */
923   if (pHookItem)
924     HeapFree(pHookItem->hHeap, 0, pHookItem );
925
926   return FALSE;
927 }
928
929 /*************************************************************************
930  * OLEMenu_IsHookInstalled
931  * Tests if OLEMenu hooks have been installed for a thread
932  *
933  * RETURNS: The pointer and index of the hook table entry for the tid
934  *          NULL and -1 for the index if no hooks were installed for this thread
935  */
936 OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid )
937 {
938   OleMenuHookItem *pHookItem = NULL;
939
940   /* Do a simple linear search for an entry whose tid matches ours.
941    * We really need a map but efficiency is not a concern here. */
942   for (pHookItem = hook_list; pHookItem; pHookItem = pHookItem->next)
943   {
944     if ( tid == pHookItem->tid )
945       return pHookItem;
946   }
947   
948   return NULL;
949 }
950
951 /***********************************************************************
952  *           OLEMenu_FindMainMenuIndex
953  *
954  * Used by OLEMenu API to find the top level group a menu item belongs to.
955  * On success pnPos contains the index of the item in the top level menu group
956  *
957  * RETURNS: TRUE if the ID was found, FALSE on failure
958  */
959 static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos )
960 {
961   UINT i, nItems;
962
963   nItems = GetMenuItemCount( hMainMenu );
964
965   for (i = 0; i < nItems; i++)
966   {
967     HMENU hsubmenu;
968       
969     /*  Is the current item a submenu? */
970     if ( (hsubmenu = GetSubMenu(hMainMenu, i)) )
971     {
972       /* If the handle is the same we're done */
973       if ( hsubmenu == hPopupMenu )
974       {
975         if (pnPos)
976           *pnPos = i;
977         return TRUE;
978       }
979       /* Recursively search without updating pnPos */
980       else if ( OLEMenu_FindMainMenuIndex( hsubmenu, hPopupMenu, NULL ) )
981       {
982         if (pnPos)
983           *pnPos = i;
984         return TRUE;
985       }
986     }
987   }
988
989   return FALSE;
990 }
991
992 /***********************************************************************
993  *           OLEMenu_SetIsServerMenu
994  *
995  * Checks whether a popup menu belongs to a shared menu group which is
996  * owned by the server, and sets the menu descriptor state accordingly.
997  * All menu messages from these groups should be routed to the server.
998  *
999  * RETURNS: TRUE if the popup menu is part of a server owned group
1000  *          FASE if the popup menu is part of a container owned group
1001  */
1002 BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor )
1003 {
1004   UINT nPos = 0, nWidth, i;
1005
1006   pOleMenuDescriptor->bIsServerItem = FALSE;
1007
1008   /* Don't bother searching if the popup is the combined menu itself */
1009   if ( hmenu == pOleMenuDescriptor->hmenuCombined )
1010     return FALSE;
1011
1012   /* Find the menu item index in the shared OLE menu that this item belongs to */
1013   if ( !OLEMenu_FindMainMenuIndex( pOleMenuDescriptor->hmenuCombined, hmenu,  &nPos ) )
1014     return FALSE;
1015   
1016   /* The group widths array has counts for the number of elements
1017    * in the groups File, Edit, Container, Object, Window, Help.
1018    * The Edit, Object & Help groups belong to the server object
1019    * and the other three belong to the container.
1020    * Loop thru the group widths and locate the group we are a member of.
1021    */
1022   for ( i = 0, nWidth = 0; i < 6; i++ )
1023   {
1024     nWidth += pOleMenuDescriptor->mgw.width[i];
1025     if ( nPos < nWidth )
1026     {
1027       /* Odd elements are server menu widths */
1028       pOleMenuDescriptor->bIsServerItem = (i%2) ? TRUE : FALSE;
1029       break;
1030     }
1031   }
1032
1033   return pOleMenuDescriptor->bIsServerItem;
1034 }
1035
1036 /*************************************************************************
1037  * OLEMenu_CallWndProc
1038  * Thread scope WH_CALLWNDPROC hook proc filter function (callback)
1039  * This is invoked from a message hook installed in OleSetMenuDescriptor.
1040  */
1041 LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam)
1042 {
1043   LPCWPSTRUCT pMsg = NULL;
1044   HOLEMENU hOleMenu = 0;
1045   OleMenuDescriptor *pOleMenuDescriptor = NULL;
1046   OleMenuHookItem *pHookItem = NULL;
1047   WORD fuFlags;
1048   
1049   TRACE("%i, %04x, %08x\n", code, wParam, (unsigned)lParam );
1050
1051   /* Check if we're being asked to process the message */
1052   if ( HC_ACTION != code )
1053     goto NEXTHOOK;
1054       
1055   /* Retrieve the current message being dispatched from lParam */
1056   pMsg = (LPCWPSTRUCT)lParam;
1057
1058   /* Check if the message is destined for a window we are interested in:
1059    * If the window has an OLEMenu property we may need to dispatch
1060    * the menu message to its active objects window instead. */
1061
1062   hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
1063   if ( !hOleMenu )
1064     goto NEXTHOOK;
1065
1066   /* Get the menu descriptor */
1067   pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
1068   if ( !pOleMenuDescriptor ) /* Bad descriptor! */
1069     goto NEXTHOOK;
1070
1071   /* Process menu messages */
1072   switch( pMsg->message )
1073   {
1074     case WM_INITMENU:
1075     {
1076       /* Reset the menu descriptor state */
1077       pOleMenuDescriptor->bIsServerItem = FALSE;
1078
1079       /* Send this message to the server as well */
1080       SendMessageA( pOleMenuDescriptor->hwndActiveObject,
1081                   pMsg->message, pMsg->wParam, pMsg->lParam );
1082       goto NEXTHOOK;
1083     }
1084     
1085     case WM_INITMENUPOPUP:
1086     {
1087       /* Save the state for whether this is a server owned menu */
1088       OLEMenu_SetIsServerMenu( (HMENU)pMsg->wParam, pOleMenuDescriptor );
1089       break;
1090     }
1091     
1092     case WM_MENUSELECT:
1093     {
1094       fuFlags = HIWORD(pMsg->wParam);  /* Get flags */
1095       if ( fuFlags & MF_SYSMENU )
1096          goto NEXTHOOK;
1097
1098       /* Save the state for whether this is a server owned popup menu */
1099       else if ( fuFlags & MF_POPUP )
1100         OLEMenu_SetIsServerMenu( (HMENU)pMsg->lParam, pOleMenuDescriptor );
1101
1102       break;
1103     }
1104     
1105     case WM_DRAWITEM:
1106     {
1107       LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) pMsg->lParam;
1108       if ( pMsg->wParam != 0 || lpdis->CtlType != ODT_MENU )
1109         goto NEXTHOOK;  /* Not a menu message */
1110
1111       break;
1112     }
1113
1114     default:
1115       goto NEXTHOOK;
1116   }
1117
1118   /* If the message was for the server dispatch it accordingly */
1119   if ( pOleMenuDescriptor->bIsServerItem )
1120   {
1121     SendMessageA( pOleMenuDescriptor->hwndActiveObject,
1122                   pMsg->message, pMsg->wParam, pMsg->lParam );
1123   }
1124     
1125 NEXTHOOK:
1126   if ( pOleMenuDescriptor )
1127     GlobalUnlock( hOleMenu );
1128   
1129   /* Lookup the hook item for the current thread */
1130   if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId() ) ) )
1131   {
1132     /* This should never fail!! */
1133     WARN("could not retrieve hHook for current thread!\n" );
1134     return 0;
1135   }
1136   
1137   /* Pass on the message to the next hooker */
1138   return CallNextHookEx( pHookItem->CallWndProc_hHook, code, wParam, lParam );
1139 }
1140
1141 /*************************************************************************
1142  * OLEMenu_GetMsgProc
1143  * Thread scope WH_GETMESSAGE hook proc filter function (callback)
1144  * This is invoked from a message hook installed in OleSetMenuDescriptor.
1145  */
1146 LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam)
1147 {
1148   LPMSG pMsg = NULL;
1149   HOLEMENU hOleMenu = 0;
1150   OleMenuDescriptor *pOleMenuDescriptor = NULL;
1151   OleMenuHookItem *pHookItem = NULL;
1152   WORD wCode;
1153   
1154   TRACE("%i, %04x, %08x\n", code, wParam, (unsigned)lParam );
1155
1156   /* Check if we're being asked to process a  messages */
1157   if ( HC_ACTION != code )
1158     goto NEXTHOOK;
1159       
1160   /* Retrieve the current message being dispatched from lParam */
1161   pMsg = (LPMSG)lParam;
1162
1163   /* Check if the message is destined for a window we are interested in:
1164    * If the window has an OLEMenu property we may need to dispatch
1165    * the menu message to its active objects window instead. */
1166
1167   hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
1168   if ( !hOleMenu )
1169     goto NEXTHOOK;
1170
1171   /* Process menu messages */
1172   switch( pMsg->message )
1173   {
1174     case WM_COMMAND:
1175     {
1176       wCode = HIWORD(pMsg->wParam);  /* Get notification code */
1177       if ( wCode )
1178         goto NEXTHOOK;  /* Not a menu message */
1179       break;
1180     }
1181     default:
1182       goto NEXTHOOK;
1183   }
1184
1185   /* Get the menu descriptor */
1186   pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
1187   if ( !pOleMenuDescriptor ) /* Bad descriptor! */
1188     goto NEXTHOOK;
1189
1190   /* If the message was for the server dispatch it accordingly */
1191   if ( pOleMenuDescriptor->bIsServerItem )
1192   {
1193     /* Change the hWnd in the message to the active objects hWnd.
1194      * The message loop which reads this message will automatically
1195      * dispatch it to the embedded objects window. */
1196     pMsg->hwnd = pOleMenuDescriptor->hwndActiveObject;
1197   }
1198     
1199 NEXTHOOK:
1200   if ( pOleMenuDescriptor )
1201     GlobalUnlock( hOleMenu );
1202   
1203   /* Lookup the hook item for the current thread */
1204   if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId() ) ) )
1205   {
1206     /* This should never fail!! */
1207     WARN("could not retrieve hHook for current thread!\n" );
1208     return FALSE;
1209   }
1210   
1211   /* Pass on the message to the next hooker */
1212   return CallNextHookEx( pHookItem->GetMsg_hHook, code, wParam, lParam );
1213 }
1214
1215 /***********************************************************************
1216  * OleCreateMenuDescriptor [OLE32.97]
1217  * Creates an OLE menu descriptor for OLE to use when dispatching
1218  * menu messages and commands.
1219  *
1220  * PARAMS:
1221  *    hmenuCombined  -  Handle to the objects combined menu
1222  *    lpMenuWidths   -  Pointer to array of 6 LONG's indicating menus per group
1223  *
1224  */
1225 HOLEMENU WINAPI OleCreateMenuDescriptor(
1226   HMENU                hmenuCombined,
1227   LPOLEMENUGROUPWIDTHS lpMenuWidths)
1228 {
1229   HOLEMENU hOleMenu;
1230   OleMenuDescriptor *pOleMenuDescriptor;
1231   int i;
1232
1233   if ( !hmenuCombined || !lpMenuWidths )
1234     return 0;
1235
1236   /* Create an OLE menu descriptor */
1237   if ( !(hOleMenu = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
1238                                 sizeof(OleMenuDescriptor) ) ) )
1239   return 0;
1240
1241   pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
1242   if ( !pOleMenuDescriptor )
1243     return 0;
1244
1245   /* Initialize menu group widths and hmenu */
1246   for ( i = 0; i < 6; i++ )
1247     pOleMenuDescriptor->mgw.width[i] = lpMenuWidths->width[i];
1248   
1249   pOleMenuDescriptor->hmenuCombined = hmenuCombined;
1250   pOleMenuDescriptor->bIsServerItem = FALSE;
1251   GlobalUnlock( hOleMenu );
1252       
1253   return hOleMenu;
1254 }
1255
1256 /***********************************************************************
1257  * OleDestroyMenuDescriptor [OLE32.99]
1258  * Destroy the shared menu descriptor
1259  */
1260 HRESULT WINAPI OleDestroyMenuDescriptor(
1261   HOLEMENU hmenuDescriptor)
1262 {
1263   if ( hmenuDescriptor )
1264     GlobalFree( hmenuDescriptor );
1265         return S_OK;
1266 }
1267
1268 /***********************************************************************
1269  * OleSetMenuDescriptor [OLE32.129]
1270  * Installs or removes OLE dispatching code for the containers frame window
1271  * FIXME: The lpFrame and lpActiveObject parameters are currently ignored
1272  * OLE should install context sensitive help F1 filtering for the app when
1273  * these are non null.
1274  * 
1275  * PARAMS:
1276  *     hOleMenu         Handle to composite menu descriptor
1277  *     hwndFrame        Handle to containers frame window
1278  *     hwndActiveObject Handle to objects in-place activation window
1279  *     lpFrame          Pointer to IOleInPlaceFrame on containers window
1280  *     lpActiveObject   Pointer to IOleInPlaceActiveObject on active in-place object
1281  *
1282  * RETURNS:
1283  *      S_OK                               - menu installed correctly
1284  *      E_FAIL, E_INVALIDARG, E_UNEXPECTED - failure
1285  */
1286 HRESULT WINAPI OleSetMenuDescriptor(
1287   HOLEMENU               hOleMenu,
1288   HWND                   hwndFrame,
1289   HWND                   hwndActiveObject,
1290   LPOLEINPLACEFRAME        lpFrame,
1291   LPOLEINPLACEACTIVEOBJECT lpActiveObject)
1292 {
1293   OleMenuDescriptor *pOleMenuDescriptor = NULL;
1294
1295   /* Check args */
1296   if ( !hwndFrame || (hOleMenu && !hwndActiveObject) )
1297     return E_INVALIDARG;
1298
1299   if ( lpFrame || lpActiveObject )
1300   {
1301      FIXME("(%x, %x, %x, %p, %p), Context sensitive help filtering not implemented!\n",
1302         (unsigned int)hOleMenu,
1303         hwndFrame,
1304         hwndActiveObject,
1305         lpFrame,
1306         lpActiveObject);
1307   }
1308
1309   /* Set up a message hook to intercept the containers frame window messages.
1310    * The message filter is responsible for dispatching menu messages from the
1311    * shared menu which are intended for the object.
1312    */
1313
1314   if ( hOleMenu )  /* Want to install dispatching code */
1315   {
1316     /* If OLEMenu hooks are already installed for this thread, fail
1317      * Note: This effectively means that OleSetMenuDescriptor cannot
1318      * be called twice in succession on the same frame window
1319      * without first calling it with a null hOleMenu to uninstall */
1320     if ( OLEMenu_IsHookInstalled( GetCurrentThreadId() ) )
1321   return E_FAIL;
1322         
1323     /* Get the menu descriptor */
1324     pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
1325     if ( !pOleMenuDescriptor )
1326       return E_UNEXPECTED;
1327
1328     /* Update the menu descriptor */
1329     pOleMenuDescriptor->hwndFrame = hwndFrame;
1330     pOleMenuDescriptor->hwndActiveObject = hwndActiveObject;
1331
1332     GlobalUnlock( hOleMenu );
1333     pOleMenuDescriptor = NULL;
1334     
1335     /* Add a menu descriptor windows property to the frame window */
1336     SetPropA( hwndFrame, "PROP_OLEMenuDescriptor", hOleMenu );
1337
1338     /* Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC */
1339     if ( !OLEMenu_InstallHooks( GetCurrentThreadId() ) )
1340       return E_FAIL;
1341   }
1342   else  /* Want to uninstall dispatching code */
1343   {
1344     /* Uninstall the hooks */
1345     if ( !OLEMenu_UnInstallHooks( GetCurrentThreadId() ) )
1346       return E_FAIL;
1347     
1348     /* Remove the menu descriptor property from the frame window */
1349     RemovePropA( hwndFrame, "PROP_OLEMenuDescriptor" );
1350   }
1351       
1352   return S_OK;
1353 }
1354
1355 /***********************************************************************
1356  * ReleaseStgMedium [OLE32.140]
1357  */
1358 void WINAPI ReleaseStgMedium(
1359   STGMEDIUM* pmedium)
1360 {
1361   switch (pmedium->tymed)
1362   {
1363     case TYMED_HGLOBAL:
1364     {
1365       if ( (pmedium->pUnkForRelease==0) && 
1366            (pmedium->u.hGlobal!=0) )
1367         GlobalFree(pmedium->u.hGlobal);
1368
1369       pmedium->u.hGlobal = 0;
1370       break;
1371     }
1372     case TYMED_FILE:
1373     {
1374       if (pmedium->u.lpszFileName!=0)
1375       {
1376         if (pmedium->pUnkForRelease==0)
1377         {
1378           DeleteFileW(pmedium->u.lpszFileName);
1379         }
1380         
1381         CoTaskMemFree(pmedium->u.lpszFileName);
1382       }
1383
1384       pmedium->u.lpszFileName = 0;
1385       break;
1386     }
1387     case TYMED_ISTREAM:
1388     {
1389       if (pmedium->u.pstm!=0)
1390       {
1391         IStream_Release(pmedium->u.pstm);
1392       }
1393
1394       pmedium->u.pstm = 0;
1395       break;
1396     }
1397     case TYMED_ISTORAGE:
1398     {
1399       if (pmedium->u.pstg!=0)
1400       {
1401         IStorage_Release(pmedium->u.pstg);
1402       }
1403
1404       pmedium->u.pstg = 0;
1405       break;
1406     }
1407     case TYMED_GDI:
1408     {
1409       if ( (pmedium->pUnkForRelease==0) && 
1410            (pmedium->u.hGlobal!=0) )
1411         DeleteObject(pmedium->u.hGlobal);
1412
1413       pmedium->u.hGlobal = 0;
1414       break;
1415     }
1416     case TYMED_MFPICT:
1417     {
1418       if ( (pmedium->pUnkForRelease==0) && 
1419            (pmedium->u.hMetaFilePict!=0) )
1420       {
1421         DeleteMetaFile(pmedium->u.hMetaFilePict);
1422         GlobalFree(pmedium->u.hMetaFilePict);
1423       }
1424
1425       pmedium->u.hMetaFilePict = 0;
1426       break;
1427     }
1428     case TYMED_ENHMF:
1429     {
1430       if ( (pmedium->pUnkForRelease==0) && 
1431            (pmedium->u.hEnhMetaFile!=0) )
1432       {
1433         DeleteEnhMetaFile(pmedium->u.hEnhMetaFile);
1434       }
1435
1436       pmedium->u.hEnhMetaFile = 0;
1437       break;
1438     }
1439     case TYMED_NULL:
1440     default:
1441       break;
1442   }
1443
1444   /*
1445    * After cleaning up, the unknown is released
1446    */
1447   if (pmedium->pUnkForRelease!=0)
1448   {
1449     IUnknown_Release(pmedium->pUnkForRelease);
1450     pmedium->pUnkForRelease = 0;
1451   }
1452 }
1453
1454 /***
1455  * OLEDD_Initialize()
1456  *
1457  * Initializes the OLE drag and drop data structures.
1458  */
1459 static void OLEDD_Initialize()
1460 {
1461     WNDCLASSA wndClass;
1462
1463     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1464     wndClass.style         = CS_GLOBALCLASS;
1465     wndClass.lpfnWndProc   = (WNDPROC)OLEDD_DragTrackerWindowProc;
1466     wndClass.cbClsExtra    = 0;
1467     wndClass.cbWndExtra    = sizeof(TrackerWindowInfo*);
1468     wndClass.hCursor       = 0;
1469     wndClass.hbrBackground = 0;
1470     wndClass.lpszClassName = OLEDD_DRAGTRACKERCLASS;
1471  
1472     RegisterClassA (&wndClass);
1473 }
1474
1475 /***
1476  * OLEDD_UnInitialize()
1477  *
1478  * Releases the OLE drag and drop data structures.
1479  */
1480 static void OLEDD_UnInitialize()
1481 {
1482   /*
1483    * Simply empty the list.
1484    */
1485   while (targetListHead!=NULL)
1486   {
1487     RevokeDragDrop(targetListHead->hwndTarget);
1488   }
1489 }
1490
1491 /***
1492  * OLEDD_InsertDropTarget()
1493  *
1494  * Insert the target node in the tree.
1495  */
1496 static void OLEDD_InsertDropTarget(DropTargetNode* nodeToAdd)
1497 {
1498   DropTargetNode*  curNode;
1499   DropTargetNode** parentNodeLink;
1500
1501   /*
1502    * Iterate the tree to find the insertion point.
1503    */
1504   curNode        = targetListHead;
1505   parentNodeLink = &targetListHead;
1506
1507   while (curNode!=NULL)
1508   {
1509     if (nodeToAdd->hwndTarget<curNode->hwndTarget)
1510     {
1511       /*
1512        * If the node we want to add has a smaller HWND, go left
1513        */
1514       parentNodeLink = &curNode->prevDropTarget;
1515       curNode        =  curNode->prevDropTarget;
1516     }
1517     else if (nodeToAdd->hwndTarget>curNode->hwndTarget)
1518     {
1519       /*
1520        * If the node we want to add has a larger HWND, go right
1521        */
1522       parentNodeLink = &curNode->nextDropTarget;
1523       curNode        =  curNode->nextDropTarget;
1524     }
1525     else
1526     {
1527       /*
1528        * The item was found in the list. It shouldn't have been there
1529        */
1530       assert(FALSE);
1531       return;
1532     }
1533   }
1534
1535   /*
1536    * If we get here, we have found a spot for our item. The parentNodeLink
1537    * pointer points to the pointer that we have to modify. 
1538    * The curNode should be NULL. We just have to establish the link and Voila!
1539    */
1540   assert(curNode==NULL);
1541   assert(parentNodeLink!=NULL);
1542   assert(*parentNodeLink==NULL);
1543
1544   *parentNodeLink=nodeToAdd;
1545 }
1546
1547 /***
1548  * OLEDD_ExtractDropTarget()
1549  *
1550  * Removes the target node from the tree.
1551  */
1552 static DropTargetNode* OLEDD_ExtractDropTarget(HWND hwndOfTarget)
1553 {
1554   DropTargetNode*  curNode;
1555   DropTargetNode** parentNodeLink;
1556
1557   /*
1558    * Iterate the tree to find the insertion point.
1559    */
1560   curNode        = targetListHead;
1561   parentNodeLink = &targetListHead;
1562
1563   while (curNode!=NULL)
1564   {
1565     if (hwndOfTarget<curNode->hwndTarget)
1566     {
1567       /*
1568        * If the node we want to add has a smaller HWND, go left
1569        */
1570       parentNodeLink = &curNode->prevDropTarget;
1571       curNode        =  curNode->prevDropTarget;
1572     }
1573     else if (hwndOfTarget>curNode->hwndTarget)
1574     {
1575       /*
1576        * If the node we want to add has a larger HWND, go right
1577        */
1578       parentNodeLink = &curNode->nextDropTarget;
1579       curNode        =  curNode->nextDropTarget;
1580     }
1581     else
1582     {
1583       /*
1584        * The item was found in the list. Detach it from it's parent and 
1585        * re-insert it's kids in the tree.
1586        */
1587       assert(parentNodeLink!=NULL);
1588       assert(*parentNodeLink==curNode);
1589
1590       /*
1591        * We arbitrately re-attach the left sub-tree to the parent.
1592        */
1593       *parentNodeLink = curNode->prevDropTarget;
1594
1595       /*
1596        * And we re-insert the right subtree
1597        */
1598       if (curNode->nextDropTarget!=NULL)
1599       {
1600         OLEDD_InsertDropTarget(curNode->nextDropTarget);
1601       }
1602
1603       /*
1604        * The node we found is still a valid node once we complete
1605        * the unlinking of the kids.
1606        */
1607       curNode->nextDropTarget=NULL;
1608       curNode->prevDropTarget=NULL;
1609
1610       return curNode;
1611     }
1612   }
1613
1614   /*
1615    * If we get here, the node is not in the tree
1616    */
1617   return NULL;
1618 }
1619
1620 /***
1621  * OLEDD_FindDropTarget()
1622  *
1623  * Finds information about the drop target.
1624  */
1625 static DropTargetNode* OLEDD_FindDropTarget(HWND hwndOfTarget)
1626 {
1627   DropTargetNode*  curNode;
1628
1629   /*
1630    * Iterate the tree to find the HWND value.
1631    */
1632   curNode        = targetListHead;
1633
1634   while (curNode!=NULL)
1635   {
1636     if (hwndOfTarget<curNode->hwndTarget)
1637     {
1638       /*
1639        * If the node we want to add has a smaller HWND, go left
1640        */
1641       curNode =  curNode->prevDropTarget;
1642     }
1643     else if (hwndOfTarget>curNode->hwndTarget)
1644     {
1645       /*
1646        * If the node we want to add has a larger HWND, go right
1647        */
1648       curNode =  curNode->nextDropTarget;
1649     }
1650     else
1651     {
1652       /*
1653        * The item was found in the list.
1654        */
1655       return curNode;
1656     }
1657   }
1658
1659   /*
1660    * If we get here, the item is not in the list
1661    */
1662   return NULL;
1663 }
1664
1665 /***
1666  * OLEDD_DragTrackerWindowProc()
1667  *
1668  * This method is the WindowProcedure of the drag n drop tracking
1669  * window. During a drag n Drop operation, an invisible window is created
1670  * to receive the user input and act upon it. This procedure is in charge
1671  * of this behavior.
1672  */
1673 static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
1674                          HWND   hwnd, 
1675                          UINT   uMsg,
1676                          WPARAM wParam, 
1677                          LPARAM   lParam)
1678 {
1679   switch (uMsg)
1680   {
1681     case WM_CREATE:
1682     {
1683       LPCREATESTRUCTA createStruct = (LPCREATESTRUCTA)lParam;
1684
1685       SetWindowLongA(hwnd, 0, (LONG)createStruct->lpCreateParams); 
1686
1687       
1688       break;
1689     }
1690     case WM_MOUSEMOVE:
1691     {
1692       TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
1693       POINT            mousePos;
1694
1695       /*
1696        * Get the current mouse position in screen coordinates.
1697        */
1698       mousePos.x = LOWORD(lParam);
1699       mousePos.y = HIWORD(lParam);
1700       ClientToScreen(hwnd, &mousePos);
1701
1702       /*
1703        * Track the movement of the mouse.
1704        */
1705       OLEDD_TrackMouseMove(trackerInfo, mousePos, wParam);
1706
1707       break;
1708     }
1709     case WM_LBUTTONUP:
1710     case WM_MBUTTONUP:
1711     case WM_RBUTTONUP:
1712     case WM_LBUTTONDOWN:
1713     case WM_MBUTTONDOWN:
1714     case WM_RBUTTONDOWN:
1715     {
1716       TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
1717       POINT            mousePos;
1718
1719       /*
1720        * Get the current mouse position in screen coordinates.
1721        */
1722       mousePos.x = LOWORD(lParam);
1723       mousePos.y = HIWORD(lParam);
1724       ClientToScreen(hwnd, &mousePos);
1725
1726       /*
1727        * Notify everyone that the button state changed
1728        * TODO: Check if the "escape" key was pressed.
1729        */
1730       OLEDD_TrackStateChange(trackerInfo, mousePos, wParam);
1731
1732       break;
1733     }
1734   }
1735
1736   /*
1737    * This is a window proc after all. Let's call the default.
1738    */
1739   return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1740 }
1741
1742 /***
1743  * OLEDD_TrackMouseMove()
1744  *
1745  * This method is invoked while a drag and drop operation is in effect.
1746  * it will generate the appropriate callbacks in the drop source
1747  * and drop target. It will also provide the expected feedback to
1748  * the user.
1749  *
1750  * params:
1751  *    trackerInfo - Pointer to the structure identifying the
1752  *                  drag & drop operation that is currently
1753  *                  active.
1754  *    mousePos    - Current position of the mouse in screen
1755  *                  coordinates.
1756  *    keyState    - Contains the state of the shift keys and the
1757  *                  mouse buttons (MK_LBUTTON and the like)
1758  */
1759 static void OLEDD_TrackMouseMove(
1760   TrackerWindowInfo* trackerInfo,
1761   POINT            mousePos,
1762   DWORD              keyState)
1763 {
1764   HWND   hwndNewTarget = 0;
1765   HRESULT  hr = S_OK;
1766
1767   /*
1768    * Get the handle of the window under the mouse
1769    */
1770   hwndNewTarget = WindowFromPoint(mousePos);
1771
1772   /*
1773    * Every time, we re-initialize the effects passed to the
1774    * IDropTarget to the effects allowed by the source.
1775    */
1776   *trackerInfo->pdwEffect = trackerInfo->dwOKEffect;
1777
1778   /*
1779    * If we are hovering over the same target as before, send the
1780    * DragOver notification
1781    */
1782   if ( (trackerInfo->curDragTarget != 0) && 
1783        (trackerInfo->curDragTargetHWND==hwndNewTarget) )
1784   {
1785     POINTL  mousePosParam;
1786     
1787     /*
1788      * The documentation tells me that the coordinate should be in the target
1789      * window's coordinate space. However, the tests I made tell me the
1790      * coordinates should be in screen coordinates.
1791      */
1792     mousePosParam.x = mousePos.x;
1793     mousePosParam.y = mousePos.y;
1794     
1795     IDropTarget_DragOver(trackerInfo->curDragTarget,
1796                          keyState,
1797                          mousePosParam,
1798                          trackerInfo->pdwEffect);
1799   }
1800   else
1801   {
1802     DropTargetNode* newDropTargetNode = 0;
1803     
1804     /*
1805      * If we changed window, we have to notify our old target and check for
1806      * the new one.
1807      */
1808     if (trackerInfo->curDragTarget!=0)
1809     {
1810       IDropTarget_DragLeave(trackerInfo->curDragTarget);
1811     }
1812     
1813     /*
1814      * Make sure we're hovering over a window.
1815      */
1816     if (hwndNewTarget!=0)
1817     {
1818       /*
1819        * Find-out if there is a drag target under the mouse
1820        */
1821       newDropTargetNode = OLEDD_FindDropTarget(hwndNewTarget);
1822       
1823       trackerInfo->curDragTargetHWND = hwndNewTarget;
1824       trackerInfo->curDragTarget     = newDropTargetNode ? newDropTargetNode->dropTarget : 0;
1825       
1826       /*
1827        * If there is, notify it that we just dragged-in
1828        */
1829       if (trackerInfo->curDragTarget!=0)
1830       {
1831         POINTL  mousePosParam;
1832         
1833         /*
1834          * The documentation tells me that the coordinate should be in the target
1835          * window's coordinate space. However, the tests I made tell me the
1836          * coordinates should be in screen coordinates.
1837          */
1838         mousePosParam.x = mousePos.x;
1839         mousePosParam.y = mousePos.y;
1840         
1841         IDropTarget_DragEnter(trackerInfo->curDragTarget,
1842                               trackerInfo->dataObject,
1843                               keyState,
1844                               mousePosParam,
1845                               trackerInfo->pdwEffect);
1846       }
1847     }
1848     else
1849     {
1850       /*
1851        * The mouse is not over a window so we don't track anything.
1852        */
1853       trackerInfo->curDragTargetHWND = 0;
1854       trackerInfo->curDragTarget     = 0;
1855     }
1856   }
1857
1858   /*
1859    * Now that we have done that, we have to tell the source to give 
1860    * us feedback on the work being done by the target.  If we don't 
1861    * have a target, simulate no effect.
1862    */
1863   if (trackerInfo->curDragTarget==0)
1864   {
1865     *trackerInfo->pdwEffect = DROPEFFECT_NONE;
1866   }
1867
1868   hr = IDropSource_GiveFeedback(trackerInfo->dropSource,
1869                                 *trackerInfo->pdwEffect);
1870
1871   /*
1872    * When we ask for feedback from the drop source, sometimes it will
1873    * do all the necessary work and sometimes it will not handle it
1874    * when that's the case, we must display the standard drag and drop
1875    * cursors.
1876    */
1877   if (hr==DRAGDROP_S_USEDEFAULTCURSORS)
1878   {
1879     if ( (*trackerInfo->pdwEffect & DROPEFFECT_MOVE) ||
1880          (*trackerInfo->pdwEffect & DROPEFFECT_COPY) ||
1881          (*trackerInfo->pdwEffect & DROPEFFECT_LINK) )
1882     {
1883       SetCursor(LoadCursorA(0, IDC_SIZEALLA));
1884     }
1885     else
1886     {
1887       SetCursor(LoadCursorA(0, IDC_NOA));
1888     }
1889   }  
1890 }
1891
1892 /***
1893  * OLEDD_TrackStateChange()
1894  *
1895  * This method is invoked while a drag and drop operation is in effect.
1896  * It is used to notify the drop target/drop source callbacks when
1897  * the state of the keyboard or mouse button change.
1898  *
1899  * params:
1900  *    trackerInfo - Pointer to the structure identifying the
1901  *                  drag & drop operation that is currently
1902  *                  active.
1903  *    mousePos    - Current position of the mouse in screen
1904  *                  coordinates.
1905  *    keyState    - Contains the state of the shift keys and the
1906  *                  mouse buttons (MK_LBUTTON and the like)
1907  */
1908 static void OLEDD_TrackStateChange(
1909   TrackerWindowInfo* trackerInfo,
1910   POINT            mousePos,
1911   DWORD              keyState)
1912 {
1913   /*
1914    * Ask the drop source what to do with the operation.
1915    */
1916   trackerInfo->returnValue = IDropSource_QueryContinueDrag(
1917                                trackerInfo->dropSource,
1918                                trackerInfo->escPressed, 
1919                                keyState);
1920   
1921   /*
1922    * All the return valued will stop the operation except the S_OK
1923    * return value.
1924    */
1925   if (trackerInfo->returnValue!=S_OK)
1926   {
1927     /*
1928      * Make sure the message loop in DoDragDrop stops
1929      */
1930     trackerInfo->trackingDone = TRUE;
1931
1932     /*
1933      * Release the mouse in case the drop target decides to show a popup 
1934      * or a menu or something.
1935      */
1936     ReleaseCapture();
1937     
1938     /*
1939      * If we end-up over a target, drop the object in the target or 
1940      * inform the target that the operation was cancelled.
1941      */
1942     if (trackerInfo->curDragTarget!=0)
1943     {
1944       switch (trackerInfo->returnValue)
1945       {
1946         /*
1947          * If the source wants us to complete the operation, we tell 
1948          * the drop target that we just dropped the object in it.
1949          */
1950         case DRAGDROP_S_DROP:
1951         {
1952           POINTL  mousePosParam;
1953         
1954           /*
1955            * The documentation tells me that the coordinate should be 
1956            * in the target window's coordinate space. However, the tests
1957            * I made tell me the coordinates should be in screen coordinates.
1958            */
1959           mousePosParam.x = mousePos.x;
1960           mousePosParam.y = mousePos.y;
1961           
1962           IDropTarget_Drop(trackerInfo->curDragTarget,
1963                            trackerInfo->dataObject,
1964                            keyState,
1965                            mousePosParam,
1966                            trackerInfo->pdwEffect);
1967           break;
1968         }
1969         /*
1970          * If the source told us that we should cancel, fool the drop 
1971          * target by telling it that the mouse left it's window.
1972          * Also set the drop effect to "NONE" in case the application 
1973          * ignores the result of DoDragDrop.
1974          */
1975         case DRAGDROP_S_CANCEL:
1976           IDropTarget_DragLeave(trackerInfo->curDragTarget);
1977           *trackerInfo->pdwEffect = DROPEFFECT_NONE;
1978           break;
1979       }
1980     }
1981   }
1982 }
1983
1984 /***
1985  * OLEDD_GetButtonState()
1986  *
1987  * This method will use the current state of the keyboard to build
1988  * a button state mask equivalent to the one passed in the
1989  * WM_MOUSEMOVE wParam.
1990  */
1991 static DWORD OLEDD_GetButtonState()
1992 {
1993   BYTE  keyboardState[256];
1994   DWORD keyMask = 0;
1995
1996   GetKeyboardState(keyboardState);
1997
1998   if ( (keyboardState[VK_SHIFT] & 0x80) !=0)
1999     keyMask |= MK_SHIFT;
2000
2001   if ( (keyboardState[VK_CONTROL] & 0x80) !=0)
2002     keyMask |= MK_CONTROL;
2003
2004   if ( (keyboardState[VK_LBUTTON] & 0x80) !=0)
2005     keyMask |= MK_LBUTTON;
2006
2007   if ( (keyboardState[VK_RBUTTON] & 0x80) !=0)
2008     keyMask |= MK_RBUTTON;
2009
2010   if ( (keyboardState[VK_MBUTTON] & 0x80) !=0)
2011     keyMask |= MK_MBUTTON;
2012
2013   return keyMask;
2014 }
2015
2016 /***
2017  * OLEDD_GetButtonState()
2018  *
2019  * This method will read the default value of the registry key in
2020  * parameter and extract a DWORD value from it. The registry key value
2021  * can be in a string key or a DWORD key.
2022  *
2023  * params:
2024  *     regKey   - Key to read the default value from
2025  *     pdwValue - Pointer to the location where the DWORD 
2026  *                value is returned. This value is not modified
2027  *                if the value is not found.
2028  */
2029
2030 static void OLEUTL_ReadRegistryDWORDValue(
2031   HKEY   regKey, 
2032   DWORD* pdwValue)
2033 {
2034   char  buffer[20];
2035   DWORD dwKeyType;
2036   DWORD cbData = 20;
2037   LONG  lres;
2038
2039   lres = RegQueryValueExA(regKey,
2040                           "",
2041                           NULL,
2042                           &dwKeyType,
2043                           (LPBYTE)buffer,
2044                           &cbData);
2045
2046   if (lres==ERROR_SUCCESS)
2047   {
2048     switch (dwKeyType)
2049     {
2050       case REG_DWORD:
2051         *pdwValue = *(DWORD*)buffer;
2052         break;
2053       case REG_EXPAND_SZ:
2054       case REG_MULTI_SZ:
2055       case REG_SZ:
2056         *pdwValue = (DWORD)strtoul(buffer, NULL, 10);
2057         break;
2058     }
2059   }
2060 }
2061
2062 /******************************************************************************
2063  * OleMetaFilePictFromIconAndLabel
2064  *
2065  * Returns a global memory handle to a metafile which contains the icon and
2066  * label given.
2067  * I guess the result of that should look somehow like desktop icons.
2068  * If no hIcon is given, we load the icon via lpszSourceFile and iIconIndex.
2069  * This code might be wrong at some places.
2070  */
2071 HGLOBAL16 WINAPI OleMetaFilePictFromIconAndLabel16(
2072         HICON16 hIcon,
2073         LPCOLESTR16 lpszLabel,
2074         LPCOLESTR16 lpszSourceFile,
2075         UINT16 iIconIndex
2076 ) {
2077     METAFILEPICT16 *mf;
2078     HGLOBAL16 hmf;
2079     HDC16 hdc;
2080
2081     FIXME("(%04x, '%s', '%s', %d): incorrect metrics, please try to correct them !\n\n\n", hIcon, lpszLabel, lpszSourceFile, iIconIndex);
2082
2083     if (!hIcon) {
2084         if (lpszSourceFile) {
2085             HINSTANCE16 hInstance = LoadLibrary16(lpszSourceFile);
2086
2087             /* load the icon at index from lpszSourceFile */
2088             hIcon = (HICON16)LoadIconA(hInstance, (LPCSTR)(DWORD)iIconIndex);
2089             FreeLibrary16(hInstance);
2090         } else
2091             return (HGLOBAL)NULL;
2092     }
2093
2094     hdc = CreateMetaFile16(NULL);
2095     DrawIcon(hdc, 0, 0, hIcon); /* FIXME */
2096     TextOut16(hdc, 0, 0, lpszLabel, 1); /* FIXME */
2097     hmf = GlobalAlloc16(0, sizeof(METAFILEPICT16));
2098     mf = (METAFILEPICT16 *)GlobalLock16(hmf);
2099     mf->mm = MM_ANISOTROPIC;
2100     mf->xExt = 20; /* FIXME: bogus */
2101     mf->yExt = 20; /* dito */
2102     mf->hMF = CloseMetaFile16(hdc);
2103     return hmf;
2104 }