From 0aef461def73c00afbad6b28dec413e47004533c Mon Sep 17 00:00:00 2001 From: Noel Borthwick Date: Thu, 1 Apr 1999 11:48:01 +0000 Subject: [PATCH] Implementation for OleCreateMenuDescriptor, OleSetMenuDescriptor and OleDestroyMenuDescriptor along with a bunch of internally used methods and data structures. --- ole/ole2.c | 587 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 582 insertions(+), 5 deletions(-) diff --git a/ole/ole2.c b/ole/ole2.c index d9bc52f90f8..b947d2ef180 100644 --- a/ole/ole2.c +++ b/ole/ole2.c @@ -3,6 +3,7 @@ * * Copyright 1995 Martin von Loewis * Copyright 1999 Francis Beaudet + * Copyright 1999 Noel Borthwick */ #include @@ -11,6 +12,8 @@ #include "winerror.h" #include "ole2.h" #include "process.h" +#include "hook.h" +#include "commctrl.h" #include "wine/obj_clientserver.h" #include "debug.h" #include "ole2ver.h" @@ -41,6 +44,28 @@ typedef struct tagTrackerWindowInfo IDropTarget* curDragTarget; } TrackerWindowInfo; +typedef struct tagOleMenuDescriptor /* OleMenuDescriptor */ +{ + HWND hwndFrame; /* The containers frame window */ + HWND hwndActiveObject; /* The active objects window */ + OLEMENUGROUPWIDTHS mgw; /* OLE menu group widths for the shared menu */ + HMENU hmenuCombined; /* The combined menu */ + BOOL bIsServerItem; /* True if the currently open popup belongs to the server */ +} OleMenuDescriptor; + +typedef struct tagOleMenuHookItem /* OleMenu hook item in per thread hook list */ +{ + DWORD tid; /* Thread Id */ + HANDLE hHeap; /* Heap this is allocated from */ + HHOOK GetMsg_hHook; /* message hook for WH_GETMESSAGE */ + HHOOK CallWndProc_hHook; /* message hook for WH_CALLWNDPROC */ +} OleMenuHookItem; + +/* + * Dynamic pointer array of per thread message hooks (maintained by OleSetMenuDescriptor) + */ +static HDPA OLEMenu_MsgHookDPA = NULL; + /* * This is the lock count on the OLE library. It is controlled by the * OLEInitialize/OLEUninitialize methods. @@ -57,6 +82,19 @@ static const char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32"; */ static DropTargetNode* targetListHead = NULL; +/****************************************************************************** + * These are the prototypes of the utility methods used to manage a shared menu + */ +static void OLEMenu_Initialize(); +static void OLEMenu_UnInitialize(); +BOOL OLEMenu_InstallHooks( DWORD tid ); +BOOL OLEMenu_UnInstallHooks( DWORD tid ); +OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook ); +static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos ); +BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor ); +LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam); + /****************************************************************************** * These are the prototypes of the utility methods used for OLE Drag n Drop */ @@ -133,6 +171,11 @@ HRESULT WINAPI OleInitialize(LPVOID reserved) * Drag and Drop */ OLEDD_Initialize(); + + /* + * OLE shared menu + */ + OLEMenu_Initialize(); } /* @@ -179,6 +222,11 @@ void WINAPI OleUninitialize(void) * Drag and Drop */ OLEDD_UnInitialize(); + + /* + * OLE shared menu + */ + OLEMenu_UnInitialize(); } /* @@ -460,46 +508,575 @@ HRESULT WINAPI OleGetClipboard( return E_FAIL; } + +/************************************************************************** + * Internal methods to manage the shared OLE menu in response to the + * OLE***MenuDescriptor API + */ + +/*** + * OLEMenu_Initialize() + * + * Initializes the OLEMENU data structures. + */ +static void OLEMenu_Initialize() +{ + /* Create a dynamic pointer array to store the hook handles */ + if ( !OLEMenu_MsgHookDPA ) + OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() ); +} + +/*** + * OLEMenu_UnInitialize() + * + * Releases the OLEMENU data structures. + */ +static void OLEMenu_UnInitialize() +{ + /* Release the hook table */ + if ( OLEMenu_MsgHookDPA ) + DPA_Destroy( OLEMenu_MsgHookDPA ); + + OLEMenu_MsgHookDPA = NULL; +} + +/************************************************************************* + * OLEMenu_InstallHooks + * Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC + * + * RETURNS: TRUE if message hooks were succesfully installed + * FALSE on failure + */ +BOOL OLEMenu_InstallHooks( DWORD tid ) +{ + OleMenuHookItem *pHookItem = NULL; + + if ( !OLEMenu_MsgHookDPA ) /* No hook table? Create one */ + { + /* Create a dynamic pointer array to store the hook handles */ + if ( !(OLEMenu_MsgHookDPA = DPA_CreateEx( 2, GetProcessHeap() )) ) + return FALSE; + } + + /* Create an entry for the hook table */ + if ( !(pHookItem = HeapAlloc(GetProcessHeap(), 0, + sizeof(OleMenuHookItem)) ) ) + return FALSE; + + pHookItem->tid = tid; + pHookItem->hHeap = GetProcessHeap(); + + /* Install a thread scope message hook for WH_GETMESSAGE */ + pHookItem->GetMsg_hHook = SetWindowsHookExA( WH_GETMESSAGE, OLEMenu_GetMsgProc, + 0, GetCurrentThreadId() ); + if ( !pHookItem->GetMsg_hHook ) + goto CLEANUP; + + /* Install a thread scope message hook for WH_CALLWNDPROC */ + pHookItem->CallWndProc_hHook = SetWindowsHookExA( WH_CALLWNDPROC, OLEMenu_CallWndProc, + 0, GetCurrentThreadId() ); + if ( !pHookItem->CallWndProc_hHook ) + goto CLEANUP; + + /* Insert the hook table entry */ + if ( -1 == DPA_InsertPtr( OLEMenu_MsgHookDPA, 0, pHookItem ) ) + goto CLEANUP; + + return TRUE; + +CLEANUP: + /* Unhook any hooks */ + if ( pHookItem->GetMsg_hHook ) + UnhookWindowsHookEx( pHookItem->GetMsg_hHook ); + if ( pHookItem->CallWndProc_hHook ) + UnhookWindowsHookEx( pHookItem->CallWndProc_hHook ); + /* Release the hook table entry */ + HeapFree(pHookItem->hHeap, 0, pHookItem ); + + return FALSE; +} + +/************************************************************************* + * OLEMenu_UnInstallHooks + * UnInstall thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC + * + * RETURNS: TRUE if message hooks were succesfully installed + * FALSE on failure + */ +BOOL OLEMenu_UnInstallHooks( DWORD tid ) +{ + INT ixHook; + OleMenuHookItem *pHookItem = NULL; + + if ( !OLEMenu_MsgHookDPA ) /* No hooks set */ + return TRUE; + + /* Lookup the hHook index for this tid */ + if ( !OLEMenu_IsHookInstalled( tid , &ixHook ) ) + return TRUE; + + /* Remove the hook entry from the table(the pointer itself is not deleted) */ + if ( !( pHookItem = DPA_DeletePtr(OLEMenu_MsgHookDPA, ixHook) ) ) + return FALSE; + + /* Uninstall the hooks installed for this thread */ + if ( !UnhookWindowsHookEx( pHookItem->GetMsg_hHook ) ) + goto CLEANUP; + if ( !UnhookWindowsHookEx( pHookItem->CallWndProc_hHook ) ) + goto CLEANUP; + + /* Release the hook table entry */ + HeapFree(pHookItem->hHeap, 0, pHookItem ); + + return TRUE; + +CLEANUP: + /* Release the hook table entry */ + if (pHookItem) + HeapFree(pHookItem->hHeap, 0, pHookItem ); + + return FALSE; +} + +/************************************************************************* + * OLEMenu_IsHookInstalled + * Tests if OLEMenu hooks have been installed for a thread + * + * RETURNS: The pointer and index of the hook table entry for the tid + * NULL and -1 for the index if no hooks were installed for this thread + */ +OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid, INT *pixHook ) +{ + INT ixHook; + OleMenuHookItem *pHookItem = NULL; + + if ( pixHook ) + *pixHook = -1; + + if ( !OLEMenu_MsgHookDPA ) /* No hooks set */ + return NULL; + + /* Do a simple linear search for an entry whose tid matches ours. + * We really need a map but efficiency is not a concern here. */ + for( ixHook = 0; ; ixHook++ ) + { + /* Retrieve the hook entry */ + if ( !( pHookItem = DPA_GetPtr(OLEMenu_MsgHookDPA, ixHook) ) ) + return NULL; + + if ( tid == pHookItem->tid ) + { + if ( pixHook ) + *pixHook = ixHook; + return pHookItem; + } + } + + return NULL; +} + +/*********************************************************************** + * OLEMenu_FindMainMenuIndex + * + * Used by OLEMenu API to find the top level group a menu item belongs to. + * On success pnPos contains the index of the item in the top level menu group + * + * RETURNS: TRUE if the ID was found, FALSE on failure + */ +static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos ) +{ + UINT i, nItems; + + nItems = GetMenuItemCount( hMainMenu ); + + for (i = 0; i < nItems; i++) + { + HMENU hsubmenu; + + /* Is the current item a submenu? */ + if ( (hsubmenu = GetSubMenu(hMainMenu, i)) ) + { + /* If the handle is the same we're done */ + if ( hsubmenu == hPopupMenu ) + { + if (pnPos) + *pnPos = i; + return TRUE; + } + /* Recursively search without updating pnPos */ + else if ( OLEMenu_FindMainMenuIndex( hsubmenu, hPopupMenu, NULL ) ) + { + if (pnPos) + *pnPos = i; + return TRUE; + } + } + } + + return FALSE; +} + +/*********************************************************************** + * OLEMenu_SetIsServerMenu + * + * Checks whether a popup menu belongs to a shared menu group which is + * owned by the server, and sets the menu descriptor state accordingly. + * All menu messages from these groups should be routed to the server. + * + * RETURNS: TRUE if the popup menu is part of a server owned group + * FASE if the popup menu is part of a container owned group + */ +BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor ) +{ + UINT nPos = 0, nWidth, i; + + pOleMenuDescriptor->bIsServerItem = FALSE; + + /* Don't bother searching if the popup is the combined menu itself */ + if ( hmenu == pOleMenuDescriptor->hmenuCombined ) + return FALSE; + + /* Find the menu item index in the shared OLE menu that this item belongs to */ + if ( !OLEMenu_FindMainMenuIndex( pOleMenuDescriptor->hmenuCombined, hmenu, &nPos ) ) + return FALSE; + + /* The group widths array has counts for the number of elements + * in the groups File, Edit, Container, Object, Window, Help. + * The Edit, Object & Help groups belong to the server object + * and the other three belong to the container. + * Loop thru the group widths and locate the group we are a member of. + */ + for ( i = 0, nWidth = 0; i < 6; i++ ) + { + nWidth += pOleMenuDescriptor->mgw.width[i]; + if ( nPos < nWidth ) + { + /* Odd elements are server menu widths */ + pOleMenuDescriptor->bIsServerItem = (i%2) ? TRUE : FALSE; + break; + } + } + + return pOleMenuDescriptor->bIsServerItem; +} + +/************************************************************************* + * OLEMenu_CallWndProc + * Thread scope WH_CALLWNDPROC hook proc filter function (callback) + * This is invoked from a message hook installed in OleSetMenuDescriptor. + */ +LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam) +{ + LPCWPSTRUCT pMsg = NULL; + HOLEMENU hOleMenu = 0; + OleMenuDescriptor *pOleMenuDescriptor = NULL; + OleMenuHookItem *pHookItem = NULL; + WORD fuFlags; + + TRACE(ole,"%i, %04x, %08x\n", code, wParam, (unsigned)lParam ); + + /* Check if we're being asked to process the message */ + if ( HC_ACTION != code ) + goto NEXTHOOK; + + /* Retrieve the current message being dispatched from lParam */ + pMsg = (LPCWPSTRUCT)lParam; + + /* Check if the message is destined for a window we are interested in: + * If the window has an OLEMenu property we may need to dispatch + * the menu message to its active objects window instead. */ + + hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" ); + if ( !hOleMenu ) + goto NEXTHOOK; + + /* Get the menu descriptor */ + pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu ); + if ( !pOleMenuDescriptor ) /* Bad descriptor! */ + goto NEXTHOOK; + + /* Process menu messages */ + switch( pMsg->message ) + { + case WM_INITMENU: + { + /* Reset the menu descriptor state */ + pOleMenuDescriptor->bIsServerItem = FALSE; + + /* Send this message to the server as well */ + SendMessageA( pOleMenuDescriptor->hwndActiveObject, + pMsg->message, pMsg->wParam, pMsg->lParam ); + goto NEXTHOOK; + } + + case WM_INITMENUPOPUP: + { + /* Save the state for whether this is a server owned menu */ + OLEMenu_SetIsServerMenu( (HMENU)pMsg->wParam, pOleMenuDescriptor ); + break; + } + + case WM_MENUSELECT: + { + fuFlags = HIWORD(pMsg->wParam); /* Get flags */ + if ( fuFlags & MF_SYSMENU ) + goto NEXTHOOK; + + /* Save the state for whether this is a server owned popup menu */ + else if ( fuFlags & MF_POPUP ) + OLEMenu_SetIsServerMenu( (HMENU)pMsg->lParam, pOleMenuDescriptor ); + + break; + } + + case WM_DRAWITEM: + { + LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) pMsg->lParam; + if ( pMsg->wParam != 0 || lpdis->CtlType != ODT_MENU ) + goto NEXTHOOK; /* Not a menu message */ + + break; + } + + default: + goto NEXTHOOK; + } + + /* If the message was for the server dispatch it accordingly */ + if ( pOleMenuDescriptor->bIsServerItem ) + { + SendMessageA( pOleMenuDescriptor->hwndActiveObject, + pMsg->message, pMsg->wParam, pMsg->lParam ); + } + +NEXTHOOK: + if ( pOleMenuDescriptor ) + GlobalUnlock( hOleMenu ); + + /* Lookup the hook item for the current thread */ + if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) ) ) + { + /* This should never fail!! */ + WARN(ole, "could not retrieve hHook for current thread!\n" ); + return 0; + } + + /* Pass on the message to the next hooker */ + return CallNextHookEx( pHookItem->CallWndProc_hHook, code, wParam, lParam ); +} + +/************************************************************************* + * OLEMenu_GetMsgProc + * Thread scope WH_GETMESSAGE hook proc filter function (callback) + * This is invoked from a message hook installed in OleSetMenuDescriptor. + */ +LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam) +{ + LPMSG pMsg = NULL; + HOLEMENU hOleMenu = 0; + OleMenuDescriptor *pOleMenuDescriptor = NULL; + OleMenuHookItem *pHookItem = NULL; + WORD wCode; + + TRACE(ole,"%i, %04x, %08x\n", code, wParam, (unsigned)lParam ); + + /* Check if we're being asked to process a messages */ + if ( HC_ACTION != code ) + goto NEXTHOOK; + + /* Retrieve the current message being dispatched from lParam */ + pMsg = (LPMSG)lParam; + + /* Check if the message is destined for a window we are interested in: + * If the window has an OLEMenu property we may need to dispatch + * the menu message to its active objects window instead. */ + + hOleMenu = (HOLEMENU)GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" ); + if ( !hOleMenu ) + goto NEXTHOOK; + + /* Process menu messages */ + switch( pMsg->message ) + { + case WM_COMMAND: + { + wCode = HIWORD(pMsg->wParam); /* Get notification code */ + if ( wCode ) + goto NEXTHOOK; /* Not a menu message */ + break; + } + default: + goto NEXTHOOK; + } + + /* Get the menu descriptor */ + pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu ); + if ( !pOleMenuDescriptor ) /* Bad descriptor! */ + goto NEXTHOOK; + + /* If the message was for the server dispatch it accordingly */ + if ( pOleMenuDescriptor->bIsServerItem ) + { + /* Change the hWnd in the message to the active objects hWnd. + * The message loop which reads this message will automatically + * dispatch it to the embedded objects window. */ + pMsg->hwnd = pOleMenuDescriptor->hwndActiveObject; + } + +NEXTHOOK: + if ( pOleMenuDescriptor ) + GlobalUnlock( hOleMenu ); + + /* Lookup the hook item for the current thread */ + if ( !( pHookItem = OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) ) ) + { + /* This should never fail!! */ + WARN(ole, "could not retrieve hHook for current thread!\n" ); + return FALSE; + } + + /* Pass on the message to the next hooker */ + return CallNextHookEx( pHookItem->GetMsg_hHook, code, wParam, lParam ); +} + /*********************************************************************** * OleCreateMenuDescriptor [OLE32.97] + * Creates an OLE menu descriptor for OLE to use when dispatching + * menu messages and commands. + * + * PARAMS: + * hmenuCombined - Handle to the objects combined menu + * lpMenuWidths - Pointer to array of 6 LONG's indicating menus per group + * */ HOLEMENU WINAPI OleCreateMenuDescriptor( HMENU hmenuCombined, LPOLEMENUGROUPWIDTHS lpMenuWidths) { - FIXME(ole,"(%x,%p),stub!\n", hmenuCombined, lpMenuWidths); + HOLEMENU hOleMenu; + OleMenuDescriptor *pOleMenuDescriptor; + int i; + if ( !hmenuCombined || !lpMenuWidths ) + return 0; + + /* Create an OLE menu descriptor */ + if ( !(hOleMenu = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, + sizeof(OleMenuDescriptor) ) ) ) return 0; + + pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu ); + if ( !pOleMenuDescriptor ) + return 0; + + /* Initialize menu group widths and hmenu */ + for ( i = 0; i < 6; i++ ) + pOleMenuDescriptor->mgw.width[i] = lpMenuWidths->width[i]; + + pOleMenuDescriptor->hmenuCombined = hmenuCombined; + pOleMenuDescriptor->bIsServerItem = FALSE; + GlobalUnlock( hOleMenu ); + + return hOleMenu; } /*********************************************************************** * OleDestroyMenuDescriptor [OLE32.99] + * Destroy the shared menu descriptor */ HRESULT WINAPI OleDestroyMenuDescriptor( HOLEMENU hmenuDescriptor) { - FIXME(ole,"(%x),stub!\n", (unsigned int)hmenuDescriptor); + if ( hmenuDescriptor ) + GlobalFree( hmenuDescriptor ); return S_OK; } /*********************************************************************** * OleSetMenuDescriptor [OLE32.129] + * Installs or removes OLE dispatching code for the containers frame window + * FIXME: The lpFrame and lpActiveObject parameters are currently ignored + * OLE should install context sensitive help F1 filtering for the app when + * these are non null. + * + * PARAMS: + * hOleMenu Handle to composite menu descriptor + * hwndFrame Handle to containers frame window + * hwndActiveObject Handle to objects in-place activation window + * lpFrame Pointer to IOleInPlaceFrame on containers window + * lpActiveObject Pointer to IOleInPlaceActiveObject on active in-place object + * + * RETURNS: + * S_OK - menu installed correctly + * E_FAIL, E_INVALIDARG, E_UNEXPECTED - failure */ HRESULT WINAPI OleSetMenuDescriptor( - HOLEMENU hmenuDescriptor, + HOLEMENU hOleMenu, HWND hwndFrame, HWND hwndActiveObject, LPOLEINPLACEFRAME lpFrame, LPOLEINPLACEACTIVEOBJECT lpActiveObject) { - FIXME(ole,"(%x, %x, %x, %p, %p),stub!\n", - (unsigned int)hmenuDescriptor, + OleMenuDescriptor *pOleMenuDescriptor = NULL; + + /* Check args */ + if ( !hwndFrame || (hOleMenu && !hwndActiveObject) ) + return E_INVALIDARG; + + if ( lpFrame || lpActiveObject ) + { + FIXME(ole,"(%x, %x, %x, %p, %p), Context sensitive help filtering not implemented!\n", + (unsigned int)hOleMenu, hwndFrame, hwndActiveObject, lpFrame, lpActiveObject); + } + /* Set up a message hook to intercept the containers frame window messages. + * The message filter is responsible for dispatching menu messages from the + * shared menu which are intended for the object. + */ + + if ( hOleMenu ) /* Want to install dispatching code */ + { + /* If OLEMenu hooks are already installed for this thread, fail + * Note: This effectively means that OleSetMenuDescriptor cannot + * be called twice in succession on the same frame window + * without first calling it with a null hOleMenu to uninstall */ + if ( OLEMenu_IsHookInstalled( GetCurrentThreadId(), NULL ) ) return E_FAIL; + + /* Get the menu descriptor */ + pOleMenuDescriptor = (OleMenuDescriptor *) GlobalLock( hOleMenu ); + if ( !pOleMenuDescriptor ) + return E_UNEXPECTED; + + /* Update the menu descriptor */ + pOleMenuDescriptor->hwndFrame = hwndFrame; + pOleMenuDescriptor->hwndActiveObject = hwndActiveObject; + + GlobalUnlock( hOleMenu ); + pOleMenuDescriptor = NULL; + + /* Add a menu descriptor windows property to the frame window */ + SetPropA( hwndFrame, "PROP_OLEMenuDescriptor", hOleMenu ); + + /* Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC */ + if ( !OLEMenu_InstallHooks( GetCurrentThreadId() ) ) + return E_FAIL; + } + else /* Want to uninstall dispatching code */ + { + /* Uninstall the hooks */ + if ( !OLEMenu_UnInstallHooks( GetCurrentThreadId() ) ) + return E_FAIL; + + /* Remove the menu descriptor property from the frame window */ + RemovePropA( hwndFrame, "PROP_OLEMenuDescriptor" ); + } + + return S_OK; } /***********************************************************************