source: trunk/src/ole32/oleMenu.cpp@ 1036

Last change on this file since 1036 was 872, checked in by davidr, 26 years ago

Added CVS ID lines to all source files

File size: 19.2 KB
Line 
1/* $Id: oleMenu.cpp,v 1.2 1999-09-08 11:29:28 davidr Exp $ */
2/*
3 *
4 * Project Odin Software License can be found in LICENSE.TXT
5 *
6 */
7/*
8 * OLEMENU functions.
9 *
10 * 4/9/99
11 *
12 * Copyright 1999 David J. Raison
13 *
14 * Some portions from Wine Implementation (2/9/99)
15 * Copyright 1995 Martin von Loewis
16 * Copyright 1999 Francis Beaudet
17 * Copyright 1999 Noel Borthwick
18 */
19
20#include "ole32.h"
21#include "commctrl.h"
22#include "oString.h"
23#include <assert.h>
24
25// ======================================================================
26// Local Data
27// ======================================================================
28
29typedef struct tagOleMenuDescriptor /* OleMenuDescriptor */
30{
31 HWND hwndFrame; /* The containers frame window */
32 HWND hwndActiveObject; /* The active objects window */
33 OLEMENUGROUPWIDTHS mgw; /* OLE menu group widths for the shared menu */
34 HMENU hmenuCombined; /* The combined menu */
35 BOOL bIsServerItem; /* True if the currently open popup belongs to the server */
36} OleMenuDescriptor;
37
38typedef struct tagOleMenuHookItem /* OleMenu hook item in per thread hook list */
39{
40 DWORD tid; /* Thread Id */
41 HANDLE hHeap; /* Heap this is allocated from */
42 HHOOK GetMsg_hHook; /* message hook for WH_GETMESSAGE */
43 HHOOK CallWndProc_hHook; /* message hook for WH_CALLWNDPROC */
44} OleMenuHookItem;
45
46/*
47 * Dynamic pointer array of per thread message hooks (maintained by OleSetMenuDescriptor)
48 */
49static HDPA OLEMenu_MsgHookDPA = NULL;
50
51
52// ======================================================================
53// Prototypes.
54// ======================================================================
55static void OLEUTL_ReadRegistryDWORDValue(HKEY regKey, DWORD* pdwValue);
56
57// These are the prototypes of the utility methods used to manage a shared menu
58extern void OLEMenu_Initialize();
59extern void OLEMenu_UnInitialize();
60
61BOOL OLEMenu_InstallHooks( DWORD tid );
62BOOL OLEMenu_UnInstallHooks( DWORD tid );
63OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook );
64static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos );
65BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor );
66LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam);
67LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam);
68
69// ======================================================================
70// Public API's
71// ======================================================================
72
73/*
74 * OleCreateMenuDescriptor [OLE32.97]
75 * Creates an OLE menu descriptor for OLE to use when dispatching
76 * menu messages and commands.
77 *
78 * PARAMS:
79 * hmenuCombined - Handle to the objects combined menu
80 * lpMenuWidths - Pointer to array of 6 LONG's indicating menus per group
81 *
82 */
83HOLEMENU WIN32API OleCreateMenuDescriptor(
84 HMENU hmenuCombined,
85 LPOLEMENUGROUPWIDTHS lpMenuWidths)
86{
87 HOLEMENU hOleMenu;
88 OleMenuDescriptor *pOleMenuDescriptor;
89 int i;
90
91 if ( !hmenuCombined || !lpMenuWidths )
92 return 0;
93
94 /* Create an OLE menu descriptor */
95 if ( !(hOleMenu = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
96 sizeof(OleMenuDescriptor) ) ) )
97 return 0;
98
99 pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
100 if ( !pOleMenuDescriptor )
101 return 0;
102
103 /* Initialize menu group widths and hmenu */
104 for ( i = 0; i < 6; i++ )
105 pOleMenuDescriptor->mgw.width[i] = lpMenuWidths->width[i];
106
107 pOleMenuDescriptor->hmenuCombined = hmenuCombined;
108 pOleMenuDescriptor->bIsServerItem = FALSE;
109 GlobalUnlock( hOleMenu );
110
111 return hOleMenu;
112}
113
114/*
115 * OleDestroyMenuDescriptor [OLE32.99]
116 * Destroy the shared menu descriptor
117 */
118HRESULT WIN32API OleDestroyMenuDescriptor(
119 HOLEMENU hmenuDescriptor)
120{
121 if ( hmenuDescriptor )
122 GlobalFree( hmenuDescriptor );
123 return S_OK;
124}
125
126/*
127 * OleSetMenuDescriptor [OLE32.129]
128 * Installs or removes OLE dispatching code for the containers frame window
129 * FIXME: The lpFrame and lpActiveObject parameters are currently ignored
130 * OLE should install context sensitive help F1 filtering for the app when
131 * these are non null.
132 *
133 * PARAMS:
134 * hOleMenu Handle to composite menu descriptor
135 * hwndFrame Handle to containers frame window
136 * hwndActiveObject Handle to objects in-place activation window
137 * lpFrame Pointer to IOleInPlaceFrame on containers window
138 * lpActiveObject Pointer to IOleInPlaceActiveObject on active in-place object
139 *
140 * RETURNS:
141 * S_OK - menu installed correctly
142 * E_FAIL, E_INVALIDARG, E_UNEXPECTED - failure
143 */
144HRESULT WIN32API OleSetMenuDescriptor(
145 HOLEMENU hOleMenu,
146 HWND hwndFrame,
147 HWND hwndActiveObject,
148 LPOLEINPLACEFRAME lpFrame,
149 LPOLEINPLACEACTIVEOBJECT lpActiveObject)
150{
151 OleMenuDescriptor *pOleMenuDescriptor = NULL;
152
153 /* Check args */
154 if ( !hwndFrame || (hOleMenu && !hwndActiveObject) )
155 return E_INVALIDARG;
156
157 if ( lpFrame || lpActiveObject )
158 {
159 dprintf(("OLE32: OleSetMenuDescriptor(%x, %x, %x, %p, %p), Context sensitive help filtering not implemented!\n",
160 (unsigned int)hOleMenu,
161 hwndFrame,
162 hwndActiveObject,
163 lpFrame,
164 lpActiveObject));
165 }
166
167 /* Set up a message hook to intercept the containers frame window messages.
168 * The message filter is responsible for dispatching menu messages from the
169 * shared menu which are intended for the object.
170 */
171
172 if ( hOleMenu ) /* Want to install dispatching code */
173 {
174 /* If OLEMenu hooks are already installed for this thread, fail
175 * Note: This effectively means that OleSetMenuDescriptor cannot
176 * be called twice in succession on the same frame window
177 * without first calling it with a null hOleMenu to uninstall */
178 if ( OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) )
179 return E_FAIL;
180
181 /* Get the menu descriptor */
182 pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
183 if ( !pOleMenuDescriptor )
184 return E_UNEXPECTED;
185
186 /* Update the menu descriptor */
187 pOleMenuDescriptor->hwndFrame = hwndFrame;
188 pOleMenuDescriptor->hwndActiveObject = hwndActiveObject;
189
190 GlobalUnlock( hOleMenu );
191 pOleMenuDescriptor = NULL;
192
193 /* Add a menu descriptor windows property to the frame window */
194 SetPropA( hwndFrame, "PROP_OLEMenuDescriptor", hOleMenu );
195
196 /* Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC */
197 if ( !OLEMenu_InstallHooks( GetCurrentThreadId() ) )
198 return E_FAIL;
199 }
200 else /* Want to uninstall dispatching code */
201 {
202 /* Uninstall the hooks */
203 if ( !OLEMenu_UnInstallHooks( GetCurrentThreadId() ) )
204 return E_FAIL;
205
206 /* Remove the menu descriptor property from the frame window */
207 RemovePropA( hwndFrame, "PROP_OLEMenuDescriptor" );
208 }
209
210 return S_OK;
211}
212
213/*
214 * OLEMenu_Initialize()
215 *
216 * Initializes the OLEMENU data structures.
217 */
218extern void OLEMenu_Initialize()
219{
220 dprintf(("OLE32: OLEMenu_Initialize"));
221
222 /* Create a dynamic pointer array to store the hook handles */
223 if ( !OLEMenu_MsgHookDPA )
224 OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() );
225}
226
227/*
228 * OLEMenu_UnInitialize()
229 *
230 * Releases the OLEMENU data structures.
231 */
232extern void OLEMenu_UnInitialize()
233{
234 dprintf(("OLE32: OLEMenu_UnInitialize"));
235
236 /* Release the hook table */
237 if ( OLEMenu_MsgHookDPA )
238 DPA_Destroy( OLEMenu_MsgHookDPA );
239
240 OLEMenu_MsgHookDPA = NULL;
241}
242
243// ======================================================================
244// Private functions.
245// ======================================================================
246
247/*
248 * Internal methods to manage the shared OLE menu in response to the
249 * OLE***MenuDescriptor API
250 */
251
252/*
253 * OLEMenu_InstallHooks
254 * Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
255 *
256 * RETURNS: TRUE if message hooks were succesfully installed
257 * FALSE on failure
258 */
259BOOL OLEMenu_InstallHooks( DWORD tid )
260{
261 OleMenuHookItem *pHookItem = NULL;
262
263 if ( !OLEMenu_MsgHookDPA ) /* No hook table? Create one */
264 {
265 /* Create a dynamic pointer array to store the hook handles */
266 if ( !(OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() )) )
267 return FALSE;
268 }
269
270 /* Create an entry for the hook table */
271 if ( !(pHookItem = (OleMenuHookItem *)HeapAlloc(GetProcessHeap(), 0,
272 sizeof(OleMenuHookItem)) ) )
273 return FALSE;
274
275 pHookItem->tid = tid;
276 pHookItem->hHeap = GetProcessHeap();
277
278 /* Install a thread scope message hook for WH_GETMESSAGE */
279 pHookItem->GetMsg_hHook = SetWindowsHookExA( WH_GETMESSAGE, OLEMenu_GetMsgProc,
280 0, GetCurrentThreadId() );
281 if ( !pHookItem->GetMsg_hHook )
282 goto CLEANUP;
283
284 /* Install a thread scope message hook for WH_CALLWNDPROC */
285 pHookItem->CallWndProc_hHook = SetWindowsHookExA( WH_CALLWNDPROC, OLEMenu_CallWndProc,
286 0, GetCurrentThreadId() );
287 if ( !pHookItem->CallWndProc_hHook )
288 goto CLEANUP;
289
290 /* Insert the hook table entry */
291 if ( -1 == DPA_InsertPtr( OLEMenu_MsgHookDPA, 0, pHookItem ) )
292 goto CLEANUP;
293
294 return TRUE;
295
296CLEANUP:
297 /* Unhook any hooks */
298 if ( pHookItem->GetMsg_hHook )
299 UnhookWindowsHookEx( pHookItem->GetMsg_hHook );
300 if ( pHookItem->CallWndProc_hHook )
301 UnhookWindowsHookEx( pHookItem->CallWndProc_hHook );
302 /* Release the hook table entry */
303 HeapFree(pHookItem->hHeap, 0, pHookItem );
304
305 return FALSE;
306}
307
308/*
309 * OLEMenu_UnInstallHooks
310 * UnInstall thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
311 *
312 * RETURNS: TRUE if message hooks were succesfully installed
313 * FALSE on failure
314 */
315BOOL OLEMenu_UnInstallHooks( DWORD tid )
316{
317 INT ixHook;
318 OleMenuHookItem *pHookItem = NULL;
319
320 if ( !OLEMenu_MsgHookDPA ) /* No hooks set */
321 return TRUE;
322
323 /* Lookup the hHook index for this tid */
324 if ( !OLEMenu_IsHookInstalled( tid , &ixHook ) )
325 return TRUE;
326
327 /* Remove the hook entry from the table(the pointer itself is not deleted) */
328 if ( !( pHookItem = (OleMenuHookItem *)DPA_DeletePtr(OLEMenu_MsgHookDPA, ixHook) ) )
329 return FALSE;
330
331 /* Uninstall the hooks installed for this thread */
332 if ( !UnhookWindowsHookEx( pHookItem->GetMsg_hHook ) )
333 goto CLEANUP;
334 if ( !UnhookWindowsHookEx( pHookItem->CallWndProc_hHook ) )
335 goto CLEANUP;
336
337 /* Release the hook table entry */
338 HeapFree(pHookItem->hHeap, 0, pHookItem );
339
340 return TRUE;
341
342CLEANUP:
343 /* Release the hook table entry */
344 if (pHookItem)
345 HeapFree(pHookItem->hHeap, 0, pHookItem );
346
347 return FALSE;
348}
349
350/*
351 * OLEMenu_IsHookInstalled
352 * Tests if OLEMenu hooks have been installed for a thread
353 *
354 * RETURNS: The pointer and index of the hook table entry for the tid
355 * NULL and -1 for the index if no hooks were installed for this thread
356 */
357OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook )
358{
359 INT ixHook;
360 OleMenuHookItem *pHookItem = NULL;
361
362 if ( pixHook )
363 *pixHook = -1;
364
365 if ( !OLEMenu_MsgHookDPA ) /* No hooks set */
366 return NULL;
367
368 /* Do a simple linear search for an entry whose tid matches ours.
369 * We really need a map but efficiency is not a concern here. */
370 for( ixHook = 0; ; ixHook++ )
371 {
372 /* Retrieve the hook entry */
373 if ( !( pHookItem = (OleMenuHookItem *)DPA_GetPtr(OLEMenu_MsgHookDPA, ixHook) ) )
374 return NULL;
375
376 if ( tid == pHookItem->tid )
377 {
378 if ( pixHook )
379 *pixHook = ixHook;
380 return pHookItem;
381 }
382 }
383
384 return NULL;
385}
386
387/*
388 * OLEMenu_FindMainMenuIndex
389 *
390 * Used by OLEMenu API to find the top level group a menu item belongs to.
391 * On success pnPos contains the index of the item in the top level menu group
392 *
393 * RETURNS: TRUE if the ID was found, FALSE on failure
394 */
395static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos )
396{
397 UINT i, nItems;
398
399 nItems = GetMenuItemCount( hMainMenu );
400
401 for (i = 0; i < nItems; i++)
402 {
403 HMENU hsubmenu;
404
405 /* Is the current item a submenu? */
406 if ( (hsubmenu = GetSubMenu(hMainMenu, i)) != NULL)
407 {
408 /* If the handle is the same we're done */
409 if ( hsubmenu == hPopupMenu )
410 {
411 if (pnPos)
412 *pnPos = i;
413 return TRUE;
414 }
415 /* Recursively search without updating pnPos */
416 else if ( OLEMenu_FindMainMenuIndex( hsubmenu, hPopupMenu, NULL ) )
417 {
418 if (pnPos)
419 *pnPos = i;
420 return TRUE;
421 }
422 }
423 }
424
425 return FALSE;
426}
427
428/*
429 * OLEMenu_SetIsServerMenu
430 *
431 * Checks whether a popup menu belongs to a shared menu group which is
432 * owned by the server, and sets the menu descriptor state accordingly.
433 * All menu messages from these groups should be routed to the server.
434 *
435 * RETURNS: TRUE if the popup menu is part of a server owned group
436 * FASE if the popup menu is part of a container owned group
437 */
438BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor )
439{
440 UINT nPos = 0, nWidth, i;
441
442 pOleMenuDescriptor->bIsServerItem = FALSE;
443
444 /* Don't bother searching if the popup is the combined menu itself */
445 if ( hmenu == pOleMenuDescriptor->hmenuCombined )
446 return FALSE;
447
448 /* Find the menu item index in the shared OLE menu that this item belongs to */
449 if ( !OLEMenu_FindMainMenuIndex( pOleMenuDescriptor->hmenuCombined, hmenu, &nPos ) )
450 return FALSE;
451
452 /* The group widths array has counts for the number of elements
453 * in the groups File, Edit, Container, Object, Window, Help.
454 * The Edit, Object & Help groups belong to the server object
455 * and the other three belong to the container.
456 * Loop thru the group widths and locate the group we are a member of.
457 */
458 for ( i = 0, nWidth = 0; i < 6; i++ )
459 {
460 nWidth += pOleMenuDescriptor->mgw.width[i];
461 if ( nPos < nWidth )
462 {
463 /* Odd elements are server menu widths */
464 pOleMenuDescriptor->bIsServerItem = (i%2) ? TRUE : FALSE;
465 break;
466 }
467 }
468
469 return pOleMenuDescriptor->bIsServerItem;
470}
471
472/*
473 * OLEMenu_CallWndProc
474 * Thread scope WH_CALLWNDPROC hook proc filter function (callback)
475 * This is invoked from a message hook installed in OleSetMenuDescriptor.
476 */
477LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam)
478{
479 LPCWPSTRUCT pMsg = NULL;
480 HOLEMENU hOleMenu = 0;
481 OleMenuDescriptor *pOleMenuDescriptor = NULL;
482 OleMenuHookItem *pHookItem = NULL;
483 WORD fuFlags;
484
485 dprintf(("OLE32: OLEMenu_CallWndProc %i, %04x, %08x", code, wParam, (unsigned)lParam));
486
487 /* Check if we're being asked to process the message */
488 if ( HC_ACTION != code )
489 goto NEXTHOOK;
490
491 /* Retrieve the current message being dispatched from lParam */
492 pMsg = (LPCWPSTRUCT)lParam;
493
494 /* Check if the message is destined for a window we are interested in:
495 * If the window has an OLEMenu property we may need to dispatch
496 * the menu message to its active objects window instead. */
497
498 hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
499 if ( !hOleMenu )
500 goto NEXTHOOK;
501
502 /* Get the menu descriptor */
503 pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
504 if ( !pOleMenuDescriptor ) /* Bad descriptor! */
505 goto NEXTHOOK;
506
507 /* Process menu messages */
508 switch( pMsg->message )
509 {
510 case WM_INITMENU:
511 {
512 /* Reset the menu descriptor state */
513 pOleMenuDescriptor->bIsServerItem = FALSE;
514
515 /* Send this message to the server as well */
516 SendMessageA( pOleMenuDescriptor->hwndActiveObject,
517 pMsg->message, pMsg->wParam, pMsg->lParam );
518 goto NEXTHOOK;
519 }
520
521 case WM_INITMENUPOPUP:
522 {
523 /* Save the state for whether this is a server owned menu */
524 OLEMenu_SetIsServerMenu( (HMENU)pMsg->wParam, pOleMenuDescriptor );
525 break;
526 }
527
528 case WM_MENUSELECT:
529 {
530 fuFlags = HIWORD(pMsg->wParam); /* Get flags */
531 if ( fuFlags & MF_SYSMENU )
532 goto NEXTHOOK;
533
534 /* Save the state for whether this is a server owned popup menu */
535 else if ( fuFlags & MF_POPUP )
536 OLEMenu_SetIsServerMenu( (HMENU)pMsg->lParam, pOleMenuDescriptor );
537
538 break;
539 }
540
541 case WM_DRAWITEM:
542 {
543 LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) pMsg->lParam;
544 if ( pMsg->wParam != 0 || lpdis->CtlType != ODT_MENU )
545 goto NEXTHOOK; /* Not a menu message */
546
547 break;
548 }
549
550 default:
551 goto NEXTHOOK;
552 }
553
554 /* If the message was for the server dispatch it accordingly */
555 if ( pOleMenuDescriptor->bIsServerItem )
556 {
557 SendMessageA( pOleMenuDescriptor->hwndActiveObject,
558 pMsg->message, pMsg->wParam, pMsg->lParam );
559 }
560
561NEXTHOOK:
562 if ( pOleMenuDescriptor )
563 GlobalUnlock( hOleMenu );
564
565 /* Lookup the hook item for the current thread */
566 if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) ) )
567 {
568 /* This should never fail!! */
569 dprintf(("Warning: Could not retrieve hHook for current thread!"));
570 return 0;
571 }
572
573 /* Pass on the message to the next hooker */
574 return CallNextHookEx( pHookItem->CallWndProc_hHook, code, wParam, lParam );
575}
576
577/*
578 * OLEMenu_GetMsgProc
579 * Thread scope WH_GETMESSAGE hook proc filter function (callback)
580 * This is invoked from a message hook installed in OleSetMenuDescriptor.
581 */
582LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam)
583{
584 LPMSG pMsg = NULL;
585 HOLEMENU hOleMenu = 0;
586 OleMenuDescriptor *pOleMenuDescriptor = NULL;
587 OleMenuHookItem *pHookItem = NULL;
588 WORD wCode;
589
590 dprintf(("OLE32: OLEMenu_GetMsgProc %i, %04x, %08x", code, wParam, (unsigned)lParam ));
591
592 /* Check if we're being asked to process a messages */
593 if ( HC_ACTION != code )
594 goto NEXTHOOK;
595
596 /* Retrieve the current message being dispatched from lParam */
597 pMsg = (LPMSG)lParam;
598
599 /* Check if the message is destined for a window we are interested in:
600 * If the window has an OLEMenu property we may need to dispatch
601 * the menu message to its active objects window instead. */
602
603 hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
604 if ( !hOleMenu )
605 goto NEXTHOOK;
606
607 /* Process menu messages */
608 switch( pMsg->message )
609 {
610 case WM_COMMAND:
611 {
612 wCode = HIWORD(pMsg->wParam); /* Get notification code */
613 if ( wCode )
614 goto NEXTHOOK; /* Not a menu message */
615 break;
616 }
617 default:
618 goto NEXTHOOK;
619 }
620
621 /* Get the menu descriptor */
622 pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu );
623 if ( !pOleMenuDescriptor ) /* Bad descriptor! */
624 goto NEXTHOOK;
625
626 /* If the message was for the server dispatch it accordingly */
627 if ( pOleMenuDescriptor->bIsServerItem )
628 {
629 /* Change the hWnd in the message to the active objects hWnd.
630 * The message loop which reads this message will automatically
631 * dispatch it to the embedded objects window. */
632 pMsg->hwnd = pOleMenuDescriptor->hwndActiveObject;
633 }
634
635NEXTHOOK:
636 if ( pOleMenuDescriptor )
637 GlobalUnlock( hOleMenu );
638
639 /* Lookup the hook item for the current thread */
640 if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) ) )
641 {
642 /* This should never fail!! */
643 dprintf(("Warning: Could not retrieve hHook for current thread!\n" ));
644 return FALSE;
645 }
646
647 /* Pass on the message to the next hooker */
648 return CallNextHookEx( pHookItem->GetMsg_hHook, code, wParam, lParam );
649}
650
Note: See TracBrowser for help on using the repository browser.