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