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