wine/dlls/ole32/ole2.c
2003-06-27 04:03:40 +00:00

2310 lines
59 KiB
C

/*
* OLE2 library
*
* Copyright 1995 Martin von Loewis
* Copyright 1999 Francis Beaudet
* Copyright 1999 Noel Borthwick
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "commctrl.h"
#include "ole2.h"
#include "ole2ver.h"
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winuser.h"
#include "winreg.h"
#include "wownt32.h"
#include "wine/winbase16.h"
#include "wine/wingdi16.h"
#include "wine/winuser16.h"
#include "ole32_main.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
WINE_DECLARE_DEBUG_CHANNEL(accel);
#define HICON_16(h32) (LOWORD(h32))
#define HICON_32(h16) ((HICON)(ULONG_PTR)(h16))
#define HINSTANCE_32(h16) ((HINSTANCE)(ULONG_PTR)(h16))
/******************************************************************************
* These are static/global variables and internal data structures that the
* OLE module uses to maintain it's state.
*/
typedef struct tagDropTargetNode
{
HWND hwndTarget;
IDropTarget* dropTarget;
struct tagDropTargetNode* prevDropTarget;
struct tagDropTargetNode* nextDropTarget;
} DropTargetNode;
typedef struct tagTrackerWindowInfo
{
IDataObject* dataObject;
IDropSource* dropSource;
DWORD dwOKEffect;
DWORD* pdwEffect;
BOOL trackingDone;
HRESULT returnValue;
BOOL escPressed;
HWND curTargetHWND; /* window the mouse is hovering over */
HWND curDragTargetHWND; /* might be a ancestor of curTargetHWND */
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 */
struct tagOleMenuHookItem *next;
} OleMenuHookItem;
static OleMenuHookItem *hook_list;
/*
* This is the lock count on the OLE library. It is controlled by the
* OLEInitialize/OLEUninitialize methods.
*/
static ULONG OLE_moduleLockCount = 0;
/*
* Name of our registered window class.
*/
static const char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32";
/*
* This is the head of the Drop target container.
*/
static DropTargetNode* targetListHead = NULL;
/******************************************************************************
* These are the prototypes of miscelaneous utility methods
*/
static void OLEUTL_ReadRegistryDWORDValue(HKEY regKey, DWORD* pdwValue);
/******************************************************************************
* 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 );
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 OLE Clipboard initialization methods (in clipboard.c)
*/
void OLEClipbrd_UnInitialize();
void OLEClipbrd_Initialize();
/******************************************************************************
* These are the prototypes of the utility methods used for OLE Drag n Drop
*/
static void OLEDD_Initialize();
static void OLEDD_UnInitialize();
static void OLEDD_InsertDropTarget(
DropTargetNode* nodeToAdd);
static DropTargetNode* OLEDD_ExtractDropTarget(
HWND hwndOfTarget);
static DropTargetNode* OLEDD_FindDropTarget(
HWND hwndOfTarget);
static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
static void OLEDD_TrackMouseMove(
TrackerWindowInfo* trackerInfo,
POINT mousePos,
DWORD keyState);
static void OLEDD_TrackStateChange(
TrackerWindowInfo* trackerInfo,
POINT mousePos,
DWORD keyState);
static DWORD OLEDD_GetButtonState();
/******************************************************************************
* OleBuildVersion [OLE2.1]
* OleBuildVersion [OLE32.84]
*/
DWORD WINAPI OleBuildVersion(void)
{
TRACE("Returning version %d, build %d.\n", rmm, rup);
return (rmm<<16)+rup;
}
/***********************************************************************
* OleInitialize (OLE2.2)
* OleInitialize (OLE32.108)
*/
HRESULT WINAPI OleInitialize(LPVOID reserved)
{
HRESULT hr;
TRACE("(%p)\n", reserved);
/*
* The first duty of the OleInitialize is to initialize the COM libraries.
*/
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
/*
* If the CoInitializeEx call failed, the OLE libraries can't be
* initialized.
*/
if (FAILED(hr))
return hr;
/*
* Then, it has to initialize the OLE specific modules.
* This includes:
* Clipboard
* Drag and Drop
* Object linking and Embedding
* In-place activation
*/
if (OLE_moduleLockCount==0)
{
/*
* Initialize the libraries.
*/
TRACE("() - Initializing the OLE libraries\n");
/*
* OLE Clipboard
*/
OLEClipbrd_Initialize();
/*
* Drag and Drop
*/
OLEDD_Initialize();
/*
* OLE shared menu
*/
OLEMenu_Initialize();
}
/*
* Then, we increase the lock count on the OLE module.
*/
OLE_moduleLockCount++;
return hr;
}
/******************************************************************************
* CoGetCurrentProcess [COMPOBJ.34]
* CoGetCurrentProcess [OLE32.18]
*
* NOTES
* Is DWORD really the correct return type for this function?
*/
DWORD WINAPI CoGetCurrentProcess(void)
{
return GetCurrentProcessId();
}
/******************************************************************************
* OleUninitialize [OLE2.3]
* OleUninitialize [OLE32.131]
*/
void WINAPI OleUninitialize(void)
{
TRACE("()\n");
/*
* Decrease the lock count on the OLE module.
*/
OLE_moduleLockCount--;
/*
* If we hit the bottom of the lock stack, free the libraries.
*/
if (OLE_moduleLockCount==0)
{
/*
* Actually free the libraries.
*/
TRACE("() - Freeing the last reference count\n");
/*
* OLE Clipboard
*/
OLEClipbrd_UnInitialize();
/*
* Drag and Drop
*/
OLEDD_UnInitialize();
/*
* OLE shared menu
*/
OLEMenu_UnInitialize();
}
/*
* Then, uninitialize the COM libraries.
*/
CoUninitialize();
}
/******************************************************************************
* CoRegisterMessageFilter [OLE32.38]
*/
HRESULT WINAPI CoRegisterMessageFilter(
LPMESSAGEFILTER lpMessageFilter, /* [in] Pointer to interface */
LPMESSAGEFILTER *lplpMessageFilter /* [out] Indirect pointer to prior instance if non-NULL */
) {
FIXME("stub\n");
if (lplpMessageFilter) {
*lplpMessageFilter = NULL;
}
return S_OK;
}
/******************************************************************************
* OleInitializeWOW [OLE32.109]
*/
HRESULT WINAPI OleInitializeWOW(DWORD x) {
FIXME("(0x%08lx),stub!\n",x);
return 0;
}
/***********************************************************************
* RegisterDragDrop (OLE2.35)
*/
HRESULT WINAPI RegisterDragDrop16(
HWND16 hwnd,
LPDROPTARGET pDropTarget
) {
FIXME("(0x%04x,%p),stub!\n",hwnd,pDropTarget);
return S_OK;
}
/***********************************************************************
* RegisterDragDrop (OLE32.139)
*/
HRESULT WINAPI RegisterDragDrop(
HWND hwnd,
LPDROPTARGET pDropTarget)
{
DropTargetNode* dropTargetInfo;
TRACE("(%p,%p)\n", hwnd, pDropTarget);
/*
* First, check if the window is already registered.
*/
dropTargetInfo = OLEDD_FindDropTarget(hwnd);
if (dropTargetInfo!=NULL)
return DRAGDROP_E_ALREADYREGISTERED;
/*
* If it's not there, we can add it. We first create a node for it.
*/
dropTargetInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(DropTargetNode));
if (dropTargetInfo==NULL)
return E_OUTOFMEMORY;
dropTargetInfo->hwndTarget = hwnd;
dropTargetInfo->prevDropTarget = NULL;
dropTargetInfo->nextDropTarget = NULL;
/*
* Don't forget that this is an interface pointer, need to nail it down since
* we keep a copy of it.
*/
dropTargetInfo->dropTarget = pDropTarget;
IDropTarget_AddRef(dropTargetInfo->dropTarget);
OLEDD_InsertDropTarget(dropTargetInfo);
return S_OK;
}
/***********************************************************************
* RevokeDragDrop (OLE2.36)
*/
HRESULT WINAPI RevokeDragDrop16(
HWND16 hwnd
) {
FIXME("(0x%04x),stub!\n",hwnd);
return S_OK;
}
/***********************************************************************
* RevokeDragDrop (OLE32.141)
*/
HRESULT WINAPI RevokeDragDrop(
HWND hwnd)
{
DropTargetNode* dropTargetInfo;
TRACE("(%p)\n", hwnd);
/*
* First, check if the window is already registered.
*/
dropTargetInfo = OLEDD_ExtractDropTarget(hwnd);
/*
* If it ain't in there, it's an error.
*/
if (dropTargetInfo==NULL)
return DRAGDROP_E_NOTREGISTERED;
/*
* If it's in there, clean-up it's used memory and
* references
*/
IDropTarget_Release(dropTargetInfo->dropTarget);
HeapFree(GetProcessHeap(), 0, dropTargetInfo);
return S_OK;
}
/***********************************************************************
* OleRegGetUserType (OLE32.122)
*
* This implementation of OleRegGetUserType ignores the dwFormOfType
* parameter and always returns the full name of the object. This is
* not too bad since this is the case for many objects because of the
* way they are registered.
*/
HRESULT WINAPI OleRegGetUserType(
REFCLSID clsid,
DWORD dwFormOfType,
LPOLESTR* pszUserType)
{
char keyName[60];
DWORD dwKeyType;
DWORD cbData;
HKEY clsidKey;
LONG hres;
LPBYTE buffer;
HRESULT retVal;
/*
* Initialize the out parameter.
*/
*pszUserType = NULL;
/*
* Build the key name we're looking for
*/
sprintf( keyName, "CLSID\\{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\\",
clsid->Data1, clsid->Data2, clsid->Data3,
clsid->Data4[0], clsid->Data4[1], clsid->Data4[2], clsid->Data4[3],
clsid->Data4[4], clsid->Data4[5], clsid->Data4[6], clsid->Data4[7] );
TRACE("(%s, %ld, %p)\n", keyName, dwFormOfType, pszUserType);
/*
* Open the class id Key
*/
hres = RegOpenKeyA(HKEY_CLASSES_ROOT,
keyName,
&clsidKey);
if (hres != ERROR_SUCCESS)
return REGDB_E_CLASSNOTREG;
/*
* Retrieve the size of the name string.
*/
cbData = 0;
hres = RegQueryValueExA(clsidKey,
"",
NULL,
&dwKeyType,
NULL,
&cbData);
if (hres!=ERROR_SUCCESS)
{
RegCloseKey(clsidKey);
return REGDB_E_READREGDB;
}
/*
* Allocate a buffer for the registry value.
*/
*pszUserType = CoTaskMemAlloc(cbData*2);
if (*pszUserType==NULL)
{
RegCloseKey(clsidKey);
return E_OUTOFMEMORY;
}
buffer = HeapAlloc(GetProcessHeap(), 0, cbData);
if (buffer == NULL)
{
RegCloseKey(clsidKey);
CoTaskMemFree(*pszUserType);
*pszUserType=NULL;
return E_OUTOFMEMORY;
}
hres = RegQueryValueExA(clsidKey,
"",
NULL,
&dwKeyType,
buffer,
&cbData);
RegCloseKey(clsidKey);
if (hres!=ERROR_SUCCESS)
{
CoTaskMemFree(*pszUserType);
*pszUserType=NULL;
retVal = REGDB_E_READREGDB;
}
else
{
MultiByteToWideChar( CP_ACP, 0, buffer, -1, *pszUserType, cbData /*FIXME*/ );
retVal = S_OK;
}
HeapFree(GetProcessHeap(), 0, buffer);
return retVal;
}
/***********************************************************************
* DoDragDrop [OLE32.65]
*/
HRESULT WINAPI DoDragDrop (
IDataObject *pDataObject, /* [in] ptr to the data obj */
IDropSource* pDropSource, /* [in] ptr to the source obj */
DWORD dwOKEffect, /* [in] effects allowed by the source */
DWORD *pdwEffect) /* [out] ptr to effects of the source */
{
TrackerWindowInfo trackerInfo;
HWND hwndTrackWindow;
MSG msg;
TRACE("(DataObject %p, DropSource %p)\n", pDataObject, pDropSource);
/*
* Setup the drag n drop tracking window.
*/
if (!IsValidInterface((LPUNKNOWN)pDropSource))
return E_INVALIDARG;
trackerInfo.dataObject = pDataObject;
trackerInfo.dropSource = pDropSource;
trackerInfo.dwOKEffect = dwOKEffect;
trackerInfo.pdwEffect = pdwEffect;
trackerInfo.trackingDone = FALSE;
trackerInfo.escPressed = FALSE;
trackerInfo.curDragTargetHWND = 0;
trackerInfo.curTargetHWND = 0;
trackerInfo.curDragTarget = 0;
hwndTrackWindow = CreateWindowA(OLEDD_DRAGTRACKERCLASS,
"TrackerWindow",
WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
0,
0,
0,
(LPVOID)&trackerInfo);
if (hwndTrackWindow!=0)
{
/*
* Capture the mouse input
*/
SetCapture(hwndTrackWindow);
/*
* Pump messages. All mouse input should go the the capture window.
*/
while (!trackerInfo.trackingDone && GetMessageA(&msg, 0, 0, 0) )
{
if ( (msg.message >= WM_KEYFIRST) &&
(msg.message <= WM_KEYLAST) )
{
/*
* When keyboard messages are sent to windows on this thread, we
* want to ignore notify the drop source that the state changed.
* in the case of the Escape key, we also notify the drop source
* we give it a special meaning.
*/
if ( (msg.message==WM_KEYDOWN) &&
(msg.wParam==VK_ESCAPE) )
{
trackerInfo.escPressed = TRUE;
}
/*
* Notify the drop source.
*/
OLEDD_TrackStateChange(&trackerInfo,
msg.pt,
OLEDD_GetButtonState());
}
else
{
/*
* Dispatch the messages only when it's not a keyboard message.
*/
DispatchMessageA(&msg);
}
}
/*
* Destroy the temporary window.
*/
DestroyWindow(hwndTrackWindow);
return trackerInfo.returnValue;
}
return E_FAIL;
}
/***********************************************************************
* OleQueryLinkFromData [OLE32.118]
*/
HRESULT WINAPI OleQueryLinkFromData(
IDataObject* pSrcDataObject)
{
FIXME("(%p),stub!\n", pSrcDataObject);
return S_OK;
}
/***********************************************************************
* OleRegGetMiscStatus [OLE32.121]
*/
HRESULT WINAPI OleRegGetMiscStatus(
REFCLSID clsid,
DWORD dwAspect,
DWORD* pdwStatus)
{
char keyName[60];
HKEY clsidKey;
HKEY miscStatusKey;
HKEY aspectKey;
LONG result;
/*
* Initialize the out parameter.
*/
*pdwStatus = 0;
/*
* Build the key name we're looking for
*/
sprintf( keyName, "CLSID\\{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\\",
clsid->Data1, clsid->Data2, clsid->Data3,
clsid->Data4[0], clsid->Data4[1], clsid->Data4[2], clsid->Data4[3],
clsid->Data4[4], clsid->Data4[5], clsid->Data4[6], clsid->Data4[7] );
TRACE("(%s, %ld, %p)\n", keyName, dwAspect, pdwStatus);
/*
* Open the class id Key
*/
result = RegOpenKeyA(HKEY_CLASSES_ROOT,
keyName,
&clsidKey);
if (result != ERROR_SUCCESS)
return REGDB_E_CLASSNOTREG;
/*
* Get the MiscStatus
*/
result = RegOpenKeyA(clsidKey,
"MiscStatus",
&miscStatusKey);
if (result != ERROR_SUCCESS)
{
RegCloseKey(clsidKey);
return REGDB_E_READREGDB;
}
/*
* Read the default value
*/
OLEUTL_ReadRegistryDWORDValue(miscStatusKey, pdwStatus);
/*
* Open the key specific to the requested aspect.
*/
sprintf(keyName, "%ld", dwAspect);
result = RegOpenKeyA(miscStatusKey,
keyName,
&aspectKey);
if (result == ERROR_SUCCESS)
{
OLEUTL_ReadRegistryDWORDValue(aspectKey, pdwStatus);
RegCloseKey(aspectKey);
}
/*
* Cleanup
*/
RegCloseKey(miscStatusKey);
RegCloseKey(clsidKey);
return S_OK;
}
/******************************************************************************
* OleSetContainedObject [OLE32.128]
*/
HRESULT WINAPI OleSetContainedObject(
LPUNKNOWN pUnknown,
BOOL fContained)
{
IRunnableObject* runnable = NULL;
HRESULT hres;
TRACE("(%p,%x), stub!\n", pUnknown, fContained);
hres = IUnknown_QueryInterface(pUnknown,
&IID_IRunnableObject,
(void**)&runnable);
if (SUCCEEDED(hres))
{
hres = IRunnableObject_SetContainedObject(runnable, fContained);
IRunnableObject_Release(runnable);
return hres;
}
return S_OK;
}
/******************************************************************************
* OleLoad [OLE32.112]
*/
HRESULT WINAPI OleLoad(
LPSTORAGE pStg,
REFIID riid,
LPOLECLIENTSITE pClientSite,
LPVOID* ppvObj)
{
IPersistStorage* persistStorage = NULL;
IOleObject* oleObject = NULL;
STATSTG storageInfo;
HRESULT hres;
TRACE("(%p,%p,%p,%p)\n", pStg, riid, pClientSite, ppvObj);
/*
* TODO, Conversion ... OleDoAutoConvert
*/
/*
* Get the class ID for the object.
*/
hres = IStorage_Stat(pStg, &storageInfo, STATFLAG_NONAME);
/*
* Now, try and create the handler for the object
*/
hres = CoCreateInstance(&storageInfo.clsid,
NULL,
CLSCTX_INPROC_HANDLER,
&IID_IOleObject,
(void**)&oleObject);
/*
* If that fails, as it will most times, load the default
* OLE handler.
*/
if (FAILED(hres))
{
hres = OleCreateDefaultHandler(&storageInfo.clsid,
NULL,
&IID_IOleObject,
(void**)&oleObject);
}
/*
* If we couldn't find a handler... this is bad. Abort the whole thing.
*/
if (FAILED(hres))
return hres;
/*
* Inform the new object of it's client site.
*/
hres = IOleObject_SetClientSite(oleObject, pClientSite);
/*
* Initialize the object with it's IPersistStorage interface.
*/
hres = IOleObject_QueryInterface(oleObject,
&IID_IPersistStorage,
(void**)&persistStorage);
if (SUCCEEDED(hres))
{
IPersistStorage_Load(persistStorage, pStg);
IPersistStorage_Release(persistStorage);
persistStorage = NULL;
}
/*
* Return the requested interface to the caller.
*/
hres = IOleObject_QueryInterface(oleObject, riid, ppvObj);
/*
* Cleanup interfaces used internally
*/
IOleObject_Release(oleObject);
return hres;
}
/***********************************************************************
* OleSave [OLE32.124]
*/
HRESULT WINAPI OleSave(
LPPERSISTSTORAGE pPS,
LPSTORAGE pStg,
BOOL fSameAsLoad)
{
HRESULT hres;
CLSID objectClass;
TRACE("(%p,%p,%x)\n", pPS, pStg, fSameAsLoad);
/*
* First, we transfer the class ID (if available)
*/
hres = IPersistStorage_GetClassID(pPS, &objectClass);
if (SUCCEEDED(hres))
{
WriteClassStg(pStg, &objectClass);
}
/*
* Then, we ask the object to save itself to the
* storage. If it is successful, we commit the storage.
*/
hres = IPersistStorage_Save(pPS, pStg, fSameAsLoad);
if (SUCCEEDED(hres))
{
IStorage_Commit(pStg,
STGC_DEFAULT);
}
return hres;
}
/******************************************************************************
* OleLockRunning [OLE32.114]
*/
HRESULT WINAPI OleLockRunning(LPUNKNOWN pUnknown, BOOL fLock, BOOL fLastUnlockCloses)
{
IRunnableObject* runnable = NULL;
HRESULT hres;
TRACE("(%p,%x,%x)\n", pUnknown, fLock, fLastUnlockCloses);
hres = IUnknown_QueryInterface(pUnknown,
&IID_IRunnableObject,
(void**)&runnable);
if (SUCCEEDED(hres))
{
hres = IRunnableObject_LockRunning(runnable, fLock, fLastUnlockCloses);
IRunnableObject_Release(runnable);
return hres;
}
else
return E_INVALIDARG;
}
/**************************************************************************
* 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()
{
}
/***
* OLEMenu_UnInitialize()
*
* Releases the OLEMENU data structures.
*/
static void OLEMenu_UnInitialize()
{
}
/*************************************************************************
* 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;
/* 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 */
pHookItem->next = hook_list;
hook_list = pHookItem;
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 )
{
OleMenuHookItem *pHookItem = NULL;
OleMenuHookItem **ppHook = &hook_list;
while (*ppHook)
{
if ((*ppHook)->tid == tid)
{
pHookItem = *ppHook;
*ppHook = pHookItem->next;
break;
}
ppHook = &(*ppHook)->next;
}
if (!pHookItem) 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 )
{
OleMenuHookItem *pHookItem = 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 (pHookItem = hook_list; pHookItem; pHookItem = pHookItem->next)
{
if ( tid == pHookItem->tid )
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 through 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("%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() ) ) )
{
/* This should never fail!! */
WARN("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("%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() ) ) )
{
/* This should never fail!! */
WARN("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)
{
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)
{
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 hOleMenu,
HWND hwndFrame,
HWND hwndActiveObject,
LPOLEINPLACEFRAME lpFrame,
LPOLEINPLACEACTIVEOBJECT lpActiveObject)
{
OleMenuDescriptor *pOleMenuDescriptor = NULL;
/* Check args */
if ( !hwndFrame || (hOleMenu && !hwndActiveObject) )
return E_INVALIDARG;
if ( lpFrame || lpActiveObject )
{
FIXME("(%x, %p, %p, %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() ) )
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;
}
/******************************************************************************
* IsAccelerator [OLE32.75]
* Mostly copied from controls/menu.c TranslateAccelerator implementation
*/
BOOL WINAPI IsAccelerator(HACCEL hAccel, int cAccelEntries, LPMSG lpMsg, WORD* lpwCmd)
{
/* YES, Accel16! */
LPACCEL16 lpAccelTbl;
int i;
if(!lpMsg) return FALSE;
if (!hAccel || !(lpAccelTbl = (LPACCEL16)LockResource16(HACCEL_16(hAccel))))
{
WARN_(accel)("invalid accel handle=%p\n", hAccel);
return FALSE;
}
if((lpMsg->message != WM_KEYDOWN &&
lpMsg->message != WM_KEYUP &&
lpMsg->message != WM_SYSKEYDOWN &&
lpMsg->message != WM_SYSKEYUP &&
lpMsg->message != WM_CHAR)) return FALSE;
TRACE_(accel)("hAccel=%p, cAccelEntries=%d,"
"msg->hwnd=%p, msg->message=%04x, wParam=%08x, lParam=%08lx\n",
hAccel, cAccelEntries,
lpMsg->hwnd, lpMsg->message, lpMsg->wParam, lpMsg->lParam);
for(i = 0; i < cAccelEntries; i++)
{
if(lpAccelTbl[i].key != lpMsg->wParam)
continue;
if(lpMsg->message == WM_CHAR)
{
if(!(lpAccelTbl[i].fVirt & FALT) && !(lpAccelTbl[i].fVirt & FVIRTKEY))
{
TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", lpMsg->wParam & 0xff);
goto found;
}
}
else
{
if(lpAccelTbl[i].fVirt & FVIRTKEY)
{
INT mask = 0;
TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
lpMsg->wParam, HIWORD(lpMsg->lParam) & 0xff);
if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
if(mask == (lpAccelTbl[i].fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
TRACE_(accel)("incorrect SHIFT/CTRL/ALT-state\n");
}
else
{
if(!(lpMsg->lParam & 0x01000000)) /* no special_key */
{
if((lpAccelTbl[i].fVirt & FALT) && (lpMsg->lParam & 0x20000000))
{ /* ^^ ALT pressed */
TRACE_(accel)("found accel for Alt-%c\n", lpMsg->wParam & 0xff);
goto found;
}
}
}
}
}
WARN_(accel)("couldn't translate accelerator key\n");
return FALSE;
found:
if(lpwCmd) *lpwCmd = lpAccelTbl[i].cmd;
return TRUE;
}
/***********************************************************************
* ReleaseStgMedium [OLE32.140]
*/
void WINAPI ReleaseStgMedium(
STGMEDIUM* pmedium)
{
switch (pmedium->tymed)
{
case TYMED_HGLOBAL:
{
if ( (pmedium->pUnkForRelease==0) &&
(pmedium->u.hGlobal!=0) )
GlobalFree(pmedium->u.hGlobal);
break;
}
case TYMED_FILE:
{
if (pmedium->u.lpszFileName!=0)
{
if (pmedium->pUnkForRelease==0)
{
DeleteFileW(pmedium->u.lpszFileName);
}
CoTaskMemFree(pmedium->u.lpszFileName);
}
break;
}
case TYMED_ISTREAM:
{
if (pmedium->u.pstm!=0)
{
IStream_Release(pmedium->u.pstm);
}
break;
}
case TYMED_ISTORAGE:
{
if (pmedium->u.pstg!=0)
{
IStorage_Release(pmedium->u.pstg);
}
break;
}
case TYMED_GDI:
{
if ( (pmedium->pUnkForRelease==0) &&
(pmedium->u.hBitmap!=0) )
DeleteObject(pmedium->u.hBitmap);
break;
}
case TYMED_MFPICT:
{
if ( (pmedium->pUnkForRelease==0) &&
(pmedium->u.hMetaFilePict!=0) )
{
LPMETAFILEPICT pMP = GlobalLock(pmedium->u.hMetaFilePict);
DeleteMetaFile(pMP->hMF);
GlobalUnlock(pmedium->u.hMetaFilePict);
GlobalFree(pmedium->u.hMetaFilePict);
}
break;
}
case TYMED_ENHMF:
{
if ( (pmedium->pUnkForRelease==0) &&
(pmedium->u.hEnhMetaFile!=0) )
{
DeleteEnhMetaFile(pmedium->u.hEnhMetaFile);
}
break;
}
case TYMED_NULL:
default:
break;
}
pmedium->tymed=TYMED_NULL;
/*
* After cleaning up, the unknown is released
*/
if (pmedium->pUnkForRelease!=0)
{
IUnknown_Release(pmedium->pUnkForRelease);
pmedium->pUnkForRelease = 0;
}
}
/***
* OLEDD_Initialize()
*
* Initializes the OLE drag and drop data structures.
*/
static void OLEDD_Initialize()
{
WNDCLASSA wndClass;
ZeroMemory (&wndClass, sizeof(WNDCLASSA));
wndClass.style = CS_GLOBALCLASS;
wndClass.lpfnWndProc = (WNDPROC)OLEDD_DragTrackerWindowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = sizeof(TrackerWindowInfo*);
wndClass.hCursor = 0;
wndClass.hbrBackground = 0;
wndClass.lpszClassName = OLEDD_DRAGTRACKERCLASS;
RegisterClassA (&wndClass);
}
/***
* OLEDD_UnInitialize()
*
* Releases the OLE drag and drop data structures.
*/
static void OLEDD_UnInitialize()
{
/*
* Simply empty the list.
*/
while (targetListHead!=NULL)
{
RevokeDragDrop(targetListHead->hwndTarget);
}
}
/***
* OLEDD_InsertDropTarget()
*
* Insert the target node in the tree.
*/
static void OLEDD_InsertDropTarget(DropTargetNode* nodeToAdd)
{
DropTargetNode* curNode;
DropTargetNode** parentNodeLink;
/*
* Iterate the tree to find the insertion point.
*/
curNode = targetListHead;
parentNodeLink = &targetListHead;
while (curNode!=NULL)
{
if (nodeToAdd->hwndTarget<curNode->hwndTarget)
{
/*
* If the node we want to add has a smaller HWND, go left
*/
parentNodeLink = &curNode->prevDropTarget;
curNode = curNode->prevDropTarget;
}
else if (nodeToAdd->hwndTarget>curNode->hwndTarget)
{
/*
* If the node we want to add has a larger HWND, go right
*/
parentNodeLink = &curNode->nextDropTarget;
curNode = curNode->nextDropTarget;
}
else
{
/*
* The item was found in the list. It shouldn't have been there
*/
assert(FALSE);
return;
}
}
/*
* If we get here, we have found a spot for our item. The parentNodeLink
* pointer points to the pointer that we have to modify.
* The curNode should be NULL. We just have to establish the link and Voila!
*/
assert(curNode==NULL);
assert(parentNodeLink!=NULL);
assert(*parentNodeLink==NULL);
*parentNodeLink=nodeToAdd;
}
/***
* OLEDD_ExtractDropTarget()
*
* Removes the target node from the tree.
*/
static DropTargetNode* OLEDD_ExtractDropTarget(HWND hwndOfTarget)
{
DropTargetNode* curNode;
DropTargetNode** parentNodeLink;
/*
* Iterate the tree to find the insertion point.
*/
curNode = targetListHead;
parentNodeLink = &targetListHead;
while (curNode!=NULL)
{
if (hwndOfTarget<curNode->hwndTarget)
{
/*
* If the node we want to add has a smaller HWND, go left
*/
parentNodeLink = &curNode->prevDropTarget;
curNode = curNode->prevDropTarget;
}
else if (hwndOfTarget>curNode->hwndTarget)
{
/*
* If the node we want to add has a larger HWND, go right
*/
parentNodeLink = &curNode->nextDropTarget;
curNode = curNode->nextDropTarget;
}
else
{
/*
* The item was found in the list. Detach it from it's parent and
* re-insert it's kids in the tree.
*/
assert(parentNodeLink!=NULL);
assert(*parentNodeLink==curNode);
/*
* We arbitrately re-attach the left sub-tree to the parent.
*/
*parentNodeLink = curNode->prevDropTarget;
/*
* And we re-insert the right subtree
*/
if (curNode->nextDropTarget!=NULL)
{
OLEDD_InsertDropTarget(curNode->nextDropTarget);
}
/*
* The node we found is still a valid node once we complete
* the unlinking of the kids.
*/
curNode->nextDropTarget=NULL;
curNode->prevDropTarget=NULL;
return curNode;
}
}
/*
* If we get here, the node is not in the tree
*/
return NULL;
}
/***
* OLEDD_FindDropTarget()
*
* Finds information about the drop target.
*/
static DropTargetNode* OLEDD_FindDropTarget(HWND hwndOfTarget)
{
DropTargetNode* curNode;
/*
* Iterate the tree to find the HWND value.
*/
curNode = targetListHead;
while (curNode!=NULL)
{
if (hwndOfTarget<curNode->hwndTarget)
{
/*
* If the node we want to add has a smaller HWND, go left
*/
curNode = curNode->prevDropTarget;
}
else if (hwndOfTarget>curNode->hwndTarget)
{
/*
* If the node we want to add has a larger HWND, go right
*/
curNode = curNode->nextDropTarget;
}
else
{
/*
* The item was found in the list.
*/
return curNode;
}
}
/*
* If we get here, the item is not in the list
*/
return NULL;
}
/***
* OLEDD_DragTrackerWindowProc()
*
* This method is the WindowProcedure of the drag n drop tracking
* window. During a drag n Drop operation, an invisible window is created
* to receive the user input and act upon it. This procedure is in charge
* of this behavior.
*/
static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
{
LPCREATESTRUCTA createStruct = (LPCREATESTRUCTA)lParam;
SetWindowLongA(hwnd, 0, (LONG)createStruct->lpCreateParams);
break;
}
case WM_MOUSEMOVE:
{
TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
POINT mousePos;
/*
* Get the current mouse position in screen coordinates.
*/
mousePos.x = LOWORD(lParam);
mousePos.y = HIWORD(lParam);
ClientToScreen(hwnd, &mousePos);
/*
* Track the movement of the mouse.
*/
OLEDD_TrackMouseMove(trackerInfo, mousePos, wParam);
break;
}
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
{
TrackerWindowInfo* trackerInfo = (TrackerWindowInfo*)GetWindowLongA(hwnd, 0);
POINT mousePos;
/*
* Get the current mouse position in screen coordinates.
*/
mousePos.x = LOWORD(lParam);
mousePos.y = HIWORD(lParam);
ClientToScreen(hwnd, &mousePos);
/*
* Notify everyone that the button state changed
* TODO: Check if the "escape" key was pressed.
*/
OLEDD_TrackStateChange(trackerInfo, mousePos, wParam);
break;
}
}
/*
* This is a window proc after all. Let's call the default.
*/
return DefWindowProcA (hwnd, uMsg, wParam, lParam);
}
/***
* OLEDD_TrackMouseMove()
*
* This method is invoked while a drag and drop operation is in effect.
* it will generate the appropriate callbacks in the drop source
* and drop target. It will also provide the expected feedback to
* the user.
*
* params:
* trackerInfo - Pointer to the structure identifying the
* drag & drop operation that is currently
* active.
* mousePos - Current position of the mouse in screen
* coordinates.
* keyState - Contains the state of the shift keys and the
* mouse buttons (MK_LBUTTON and the like)
*/
static void OLEDD_TrackMouseMove(
TrackerWindowInfo* trackerInfo,
POINT mousePos,
DWORD keyState)
{
HWND hwndNewTarget = 0;
HRESULT hr = S_OK;
/*
* Get the handle of the window under the mouse
*/
hwndNewTarget = WindowFromPoint(mousePos);
/*
* Every time, we re-initialize the effects passed to the
* IDropTarget to the effects allowed by the source.
*/
*trackerInfo->pdwEffect = trackerInfo->dwOKEffect;
/*
* If we are hovering over the same target as before, send the
* DragOver notification
*/
if ( (trackerInfo->curDragTarget != 0) &&
(trackerInfo->curTargetHWND == hwndNewTarget) )
{
POINTL mousePosParam;
/*
* The documentation tells me that the coordinate should be in the target
* window's coordinate space. However, the tests I made tell me the
* coordinates should be in screen coordinates.
*/
mousePosParam.x = mousePos.x;
mousePosParam.y = mousePos.y;
IDropTarget_DragOver(trackerInfo->curDragTarget,
keyState,
mousePosParam,
trackerInfo->pdwEffect);
}
else
{
DropTargetNode* newDropTargetNode = 0;
/*
* If we changed window, we have to notify our old target and check for
* the new one.
*/
if (trackerInfo->curDragTarget!=0)
{
IDropTarget_DragLeave(trackerInfo->curDragTarget);
}
/*
* Make sure we're hovering over a window.
*/
if (hwndNewTarget!=0)
{
/*
* Find-out if there is a drag target under the mouse
*/
HWND nexttar = hwndNewTarget;
trackerInfo->curTargetHWND = hwndNewTarget;
do {
newDropTargetNode = OLEDD_FindDropTarget(nexttar);
} while (!newDropTargetNode && (nexttar = GetParent(nexttar)) != 0);
if(nexttar) hwndNewTarget = nexttar;
trackerInfo->curDragTargetHWND = hwndNewTarget;
trackerInfo->curDragTarget = newDropTargetNode ? newDropTargetNode->dropTarget : 0;
/*
* If there is, notify it that we just dragged-in
*/
if (trackerInfo->curDragTarget!=0)
{
POINTL mousePosParam;
/*
* The documentation tells me that the coordinate should be in the target
* window's coordinate space. However, the tests I made tell me the
* coordinates should be in screen coordinates.
*/
mousePosParam.x = mousePos.x;
mousePosParam.y = mousePos.y;
IDropTarget_DragEnter(trackerInfo->curDragTarget,
trackerInfo->dataObject,
keyState,
mousePosParam,
trackerInfo->pdwEffect);
}
}
else
{
/*
* The mouse is not over a window so we don't track anything.
*/
trackerInfo->curDragTargetHWND = 0;
trackerInfo->curTargetHWND = 0;
trackerInfo->curDragTarget = 0;
}
}
/*
* Now that we have done that, we have to tell the source to give
* us feedback on the work being done by the target. If we don't
* have a target, simulate no effect.
*/
if (trackerInfo->curDragTarget==0)
{
*trackerInfo->pdwEffect = DROPEFFECT_NONE;
}
hr = IDropSource_GiveFeedback(trackerInfo->dropSource,
*trackerInfo->pdwEffect);
/*
* When we ask for feedback from the drop source, sometimes it will
* do all the necessary work and sometimes it will not handle it
* when that's the case, we must display the standard drag and drop
* cursors.
*/
if (hr==DRAGDROP_S_USEDEFAULTCURSORS)
{
if (*trackerInfo->pdwEffect & DROPEFFECT_MOVE)
{
SetCursor(LoadCursorA(OLE32_hInstance, MAKEINTRESOURCEA(1)));
}
else if (*trackerInfo->pdwEffect & DROPEFFECT_COPY)
{
SetCursor(LoadCursorA(OLE32_hInstance, MAKEINTRESOURCEA(2)));
}
else if (*trackerInfo->pdwEffect & DROPEFFECT_LINK)
{
SetCursor(LoadCursorA(OLE32_hInstance, MAKEINTRESOURCEA(3)));
}
else
{
SetCursor(LoadCursorA(OLE32_hInstance, MAKEINTRESOURCEA(0)));
}
}
}
/***
* OLEDD_TrackStateChange()
*
* This method is invoked while a drag and drop operation is in effect.
* It is used to notify the drop target/drop source callbacks when
* the state of the keyboard or mouse button change.
*
* params:
* trackerInfo - Pointer to the structure identifying the
* drag & drop operation that is currently
* active.
* mousePos - Current position of the mouse in screen
* coordinates.
* keyState - Contains the state of the shift keys and the
* mouse buttons (MK_LBUTTON and the like)
*/
static void OLEDD_TrackStateChange(
TrackerWindowInfo* trackerInfo,
POINT mousePos,
DWORD keyState)
{
/*
* Ask the drop source what to do with the operation.
*/
trackerInfo->returnValue = IDropSource_QueryContinueDrag(
trackerInfo->dropSource,
trackerInfo->escPressed,
keyState);
/*
* All the return valued will stop the operation except the S_OK
* return value.
*/
if (trackerInfo->returnValue!=S_OK)
{
/*
* Make sure the message loop in DoDragDrop stops
*/
trackerInfo->trackingDone = TRUE;
/*
* Release the mouse in case the drop target decides to show a popup
* or a menu or something.
*/
ReleaseCapture();
/*
* If we end-up over a target, drop the object in the target or
* inform the target that the operation was cancelled.
*/
if (trackerInfo->curDragTarget!=0)
{
switch (trackerInfo->returnValue)
{
/*
* If the source wants us to complete the operation, we tell
* the drop target that we just dropped the object in it.
*/
case DRAGDROP_S_DROP:
{
POINTL mousePosParam;
/*
* The documentation tells me that the coordinate should be
* in the target window's coordinate space. However, the tests
* I made tell me the coordinates should be in screen coordinates.
*/
mousePosParam.x = mousePos.x;
mousePosParam.y = mousePos.y;
IDropTarget_Drop(trackerInfo->curDragTarget,
trackerInfo->dataObject,
keyState,
mousePosParam,
trackerInfo->pdwEffect);
break;
}
/*
* If the source told us that we should cancel, fool the drop
* target by telling it that the mouse left it's window.
* Also set the drop effect to "NONE" in case the application
* ignores the result of DoDragDrop.
*/
case DRAGDROP_S_CANCEL:
IDropTarget_DragLeave(trackerInfo->curDragTarget);
*trackerInfo->pdwEffect = DROPEFFECT_NONE;
break;
}
}
}
}
/***
* OLEDD_GetButtonState()
*
* This method will use the current state of the keyboard to build
* a button state mask equivalent to the one passed in the
* WM_MOUSEMOVE wParam.
*/
static DWORD OLEDD_GetButtonState()
{
BYTE keyboardState[256];
DWORD keyMask = 0;
GetKeyboardState(keyboardState);
if ( (keyboardState[VK_SHIFT] & 0x80) !=0)
keyMask |= MK_SHIFT;
if ( (keyboardState[VK_CONTROL] & 0x80) !=0)
keyMask |= MK_CONTROL;
if ( (keyboardState[VK_LBUTTON] & 0x80) !=0)
keyMask |= MK_LBUTTON;
if ( (keyboardState[VK_RBUTTON] & 0x80) !=0)
keyMask |= MK_RBUTTON;
if ( (keyboardState[VK_MBUTTON] & 0x80) !=0)
keyMask |= MK_MBUTTON;
return keyMask;
}
/***
* OLEDD_GetButtonState()
*
* This method will read the default value of the registry key in
* parameter and extract a DWORD value from it. The registry key value
* can be in a string key or a DWORD key.
*
* params:
* regKey - Key to read the default value from
* pdwValue - Pointer to the location where the DWORD
* value is returned. This value is not modified
* if the value is not found.
*/
static void OLEUTL_ReadRegistryDWORDValue(
HKEY regKey,
DWORD* pdwValue)
{
char buffer[20];
DWORD dwKeyType;
DWORD cbData = 20;
LONG lres;
lres = RegQueryValueExA(regKey,
"",
NULL,
&dwKeyType,
(LPBYTE)buffer,
&cbData);
if (lres==ERROR_SUCCESS)
{
switch (dwKeyType)
{
case REG_DWORD:
*pdwValue = *(DWORD*)buffer;
break;
case REG_EXPAND_SZ:
case REG_MULTI_SZ:
case REG_SZ:
*pdwValue = (DWORD)strtoul(buffer, NULL, 10);
break;
}
}
}
/******************************************************************************
* OleMetaFilePictFromIconAndLabel (OLE2.56)
*
* Returns a global memory handle to a metafile which contains the icon and
* label given.
* I guess the result of that should look somehow like desktop icons.
* If no hIcon is given, we load the icon via lpszSourceFile and iIconIndex.
* This code might be wrong at some places.
*/
HGLOBAL16 WINAPI OleMetaFilePictFromIconAndLabel16(
HICON16 hIcon,
LPCOLESTR16 lpszLabel,
LPCOLESTR16 lpszSourceFile,
UINT16 iIconIndex
) {
METAFILEPICT16 *mf;
HGLOBAL16 hmf;
HDC hdc;
FIXME("(%04x, '%s', '%s', %d): incorrect metrics, please try to correct them !\n\n\n", hIcon, lpszLabel, lpszSourceFile, iIconIndex);
if (!hIcon) {
if (lpszSourceFile) {
HINSTANCE16 hInstance = LoadLibrary16(lpszSourceFile);
/* load the icon at index from lpszSourceFile */
hIcon = HICON_16(LoadIconA(HINSTANCE_32(hInstance), (LPCSTR)(DWORD)iIconIndex));
FreeLibrary16(hInstance);
} else
return 0;
}
hdc = CreateMetaFileA(NULL);
DrawIcon(hdc, 0, 0, HICON_32(hIcon)); /* FIXME */
TextOutA(hdc, 0, 0, lpszLabel, 1); /* FIXME */
hmf = GlobalAlloc16(0, sizeof(METAFILEPICT16));
mf = (METAFILEPICT16 *)GlobalLock16(hmf);
mf->mm = MM_ANISOTROPIC;
mf->xExt = 20; /* FIXME: bogus */
mf->yExt = 20; /* dito */
mf->hMF = CloseMetaFile16(HDC_16(hdc));
return hmf;
}
/******************************************************************************
* OleDraw (OLE32.101)
*
* The operation of this function is documented literally in the WinAPI
* documentation to involve a QueryInterface for the IViewObject interface,
* followed by a call to IViewObject::Draw.
*/
HRESULT WINAPI OleDraw(
IUnknown *pUnk,
DWORD dwAspect,
HDC hdcDraw,
LPCRECT lprcBounds)
{
HRESULT hres;
IViewObject *viewobject;
hres = IUnknown_QueryInterface(pUnk,
&IID_IViewObject,
(void**)&viewobject);
if (SUCCEEDED(hres))
{
RECTL rectl;
rectl.left = lprcBounds->left;
rectl.right = lprcBounds->right;
rectl.top = lprcBounds->top;
rectl.bottom = lprcBounds->bottom;
hres = IViewObject_Draw(viewobject, dwAspect, -1, 0, 0, 0, hdcDraw, &rectl, 0, 0, 0);
IViewObject_Release(viewobject);
return hres;
}
else
{
return DV_E_NOIVIEWOBJECT;
}
}
/******************************************************************************
* DllDebugObjectRPCHook (OLE32.62)
* turns on and off internal debugging, pointer is only used on macintosh
*/
BOOL WINAPI DllDebugObjectRPCHook(BOOL b, void *dummy)
{
FIXME("stub\n");
return TRUE;
}