wine/dlls/ole32/ole2.c
Nikolay Sivov 233042bd75 ole32: Build without WINE_NO_LONG_TYPES.
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Huw Davies <huw@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2022-02-09 10:58:46 +01:00

3165 lines
86 KiB
C

/*
* OLE2 library
*
* Copyright 1995 Martin von Loewis
* Copyright 1999 Francis Beaudet
* Copyright 1999 Noel Borthwick
* Copyright 1999, 2000 Marcus Meissner
* Copyright 1999-2000 Abey George
* Copyright 2005 Juan Lang
* Copyright 2011 Adam Martinson for CodeWeavers
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <assert.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#define COBJMACROS
#define NONAMELESSUNION
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "winreg.h"
#include "ole2.h"
#include "ole2ver.h"
#include "compobj_private.h"
#include "olestd.h"
#include "wine/list.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
WINE_DECLARE_DEBUG_CHANNEL(accel);
/******************************************************************************
* These are static/global variables and internal data structures that the
* OLE module uses to maintain its state.
*/
typedef struct tagTrackerWindowInfo
{
IDataObject* dataObject;
IDropSource* dropSource;
DWORD dwOKEffect;
DWORD* pdwEffect;
BOOL trackingDone;
BOOL inTrackCall;
HRESULT returnValue;
BOOL escPressed;
HWND curTargetHWND; /* window the mouse is hovering over */
IDropTarget* curDragTarget;
POINTL curMousePos; /* current position of the mouse in screen coordinates */
DWORD dwKeyState; /* current state of the shift and ctrl keys and the mouse buttons */
} 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 LONG OLE_moduleLockCount = 0;
/*
* Name of our registered window class.
*/
static const WCHAR OLEDD_DRAGTRACKERCLASS[] = L"WineDragDropTracker32";
/*
* Name of menu descriptor property.
*/
static const WCHAR prop_olemenuW[] = L"PROP_OLEMenuDescriptor";
/* property to store IDropTarget pointer */
static const WCHAR prop_oledroptarget[] = L"OleDropTargetInterface";
/* property to store Marshalled IDropTarget pointer */
static const WCHAR prop_marshalleddroptarget[] = L"WineMarshalledDropTarget";
/******************************************************************************
* These are the prototypes of miscellaneous 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(void);
static void OLEMenu_UnInitialize(void);
static BOOL OLEMenu_InstallHooks( DWORD tid );
static BOOL OLEMenu_UnInstallHooks( DWORD tid );
static OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid );
static BOOL OLEMenu_FindMainMenuIndex( HMENU hMainMenu, HMENU hPopupMenu, UINT *pnPos );
static BOOL OLEMenu_SetIsServerMenu( HMENU hmenu, OleMenuDescriptor *pOleMenuDescriptor );
static LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam);
/******************************************************************************
* This is a prototype of the OLE Clipboard uninitialization method (in clipboard.c)
*/
extern void clipbrd_uninitialize(void);
/******************************************************************************
* These are the prototypes of the utility methods used for OLE Drag n Drop
*/
static void OLEDD_Initialize(void);
static LRESULT WINAPI OLEDD_DragTrackerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo);
static DWORD OLEDD_GetButtonState(void);
/******************************************************************************
* OleBuildVersion [OLE32.@]
*/
DWORD WINAPI OleBuildVersion(void)
{
TRACE("Returning version %d, build %d.\n", rmm, rup);
return (rmm<<16)+rup;
}
/***********************************************************************
* OleInitialize (OLE32.@)
*/
HRESULT WINAPI DECLSPEC_HOTPATCH 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;
if (!COM_CurrentInfo()->ole_inits)
hr = S_OK;
else
hr = S_FALSE;
/*
* Then, it has to initialize the OLE specific modules.
* This includes:
* Clipboard
* Drag and Drop
* Object linking and Embedding
* In-place activation
*/
if (!COM_CurrentInfo()->ole_inits++ &&
InterlockedIncrement(&OLE_moduleLockCount) == 1)
{
/*
* Initialize the libraries.
*/
TRACE("() - Initializing the OLE libraries\n");
/*
* Drag and Drop
*/
OLEDD_Initialize();
/*
* OLE shared menu
*/
OLEMenu_Initialize();
}
return hr;
}
/******************************************************************************
* OleUninitialize [OLE32.@]
*/
void WINAPI DECLSPEC_HOTPATCH OleUninitialize(void)
{
TRACE("()\n");
if (COM_CurrentInfo()->ole_inits == 0)
{
WARN("ole_inits is already 0\n");
return ;
}
/*
* If we hit the bottom of the lock stack, free the libraries.
*/
if (!--COM_CurrentInfo()->ole_inits && !InterlockedDecrement(&OLE_moduleLockCount))
{
/*
* Actually free the libraries.
*/
TRACE("() - Freeing the last reference count\n");
/*
* OLE Clipboard
*/
clipbrd_uninitialize();
/*
* OLE shared menu
*/
OLEMenu_UnInitialize();
}
/*
* Then, uninitialize the COM libraries.
*/
CoUninitialize();
}
/******************************************************************************
* OleInitializeWOW [OLE32.@]
*/
HRESULT WINAPI OleInitializeWOW(DWORD x, DWORD y)
{
FIXME("%#lx, %#lx stub!\n", x, y);
return 0;
}
/*************************************************************
* get_droptarget_handle
*
* Retrieve a handle to the map containing the marshalled IDropTarget.
* This handle belongs to the process that called RegisterDragDrop.
* See get_droptarget_local_handle().
*/
static inline HANDLE get_droptarget_handle(HWND hwnd)
{
return GetPropW(hwnd, prop_marshalleddroptarget);
}
/*************************************************************
* is_droptarget
*
* Is the window a droptarget.
*/
static inline BOOL is_droptarget(HWND hwnd)
{
return get_droptarget_handle(hwnd) != 0;
}
/*************************************************************
* get_droptarget_local_handle
*
* Retrieve a handle to the map containing the marshalled IDropTarget.
* The handle should be closed when finished with.
*/
static HANDLE get_droptarget_local_handle(HWND hwnd)
{
HANDLE handle, local_handle = 0;
handle = get_droptarget_handle(hwnd);
if(handle)
{
DWORD pid;
HANDLE process;
GetWindowThreadProcessId(hwnd, &pid);
process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
if(process)
{
DuplicateHandle(process, handle, GetCurrentProcess(), &local_handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
CloseHandle(process);
}
}
return local_handle;
}
/***********************************************************************
* create_map_from_stream
*
* Helper for RegisterDragDrop. Creates a file mapping object
* with the contents of the provided stream. The stream must
* be a global memory backed stream.
*/
static HRESULT create_map_from_stream(IStream *stream, HANDLE *map)
{
HGLOBAL hmem;
DWORD size;
HRESULT hr;
void *data;
hr = GetHGlobalFromStream(stream, &hmem);
if(FAILED(hr)) return hr;
size = GlobalSize(hmem);
*map = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, NULL);
if(!*map) return E_OUTOFMEMORY;
data = MapViewOfFile(*map, FILE_MAP_WRITE, 0, 0, size);
memcpy(data, GlobalLock(hmem), size);
GlobalUnlock(hmem);
UnmapViewOfFile(data);
return S_OK;
}
/***********************************************************************
* create_stream_from_map
*
* Creates a stream from the provided map.
*/
static HRESULT create_stream_from_map(HANDLE map, IStream **stream)
{
HRESULT hr = E_OUTOFMEMORY;
HGLOBAL hmem;
void *data;
MEMORY_BASIC_INFORMATION info;
data = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
if(!data) return hr;
VirtualQuery(data, &info, sizeof(info));
TRACE("size %d\n", (int)info.RegionSize);
hmem = GlobalAlloc(GMEM_MOVEABLE, info.RegionSize);
if(hmem)
{
memcpy(GlobalLock(hmem), data, info.RegionSize);
GlobalUnlock(hmem);
hr = CreateStreamOnHGlobal(hmem, TRUE, stream);
}
UnmapViewOfFile(data);
return hr;
}
/* This is to work around apps which break COM rules by not implementing
* IDropTarget::QueryInterface(). Windows doesn't expose this because it
* doesn't call CoMarshallInterface() in RegisterDragDrop().
* The wrapper is only used internally, and only exists for the life of
* the marshal. We don't want to hold a ref on the app provided target
* as some apps destroy this prior to CoUninitialize without calling
* RevokeDragDrop. The only (long-term) ref is held by the window prop. */
typedef struct {
IDropTarget IDropTarget_iface;
HWND hwnd;
LONG refs;
} DropTargetWrapper;
static inline DropTargetWrapper* impl_from_IDropTarget(IDropTarget* iface)
{
return CONTAINING_RECORD(iface, DropTargetWrapper, IDropTarget_iface);
}
static HRESULT WINAPI DropTargetWrapper_QueryInterface(IDropTarget* iface,
REFIID riid,
void** ppvObject)
{
DropTargetWrapper* This = impl_from_IDropTarget(iface);
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IDropTarget))
{
IDropTarget_AddRef(&This->IDropTarget_iface);
*ppvObject = &This->IDropTarget_iface;
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI DropTargetWrapper_AddRef(IDropTarget* iface)
{
DropTargetWrapper* This = impl_from_IDropTarget(iface);
return InterlockedIncrement(&This->refs);
}
static ULONG WINAPI DropTargetWrapper_Release(IDropTarget* iface)
{
DropTargetWrapper* This = impl_from_IDropTarget(iface);
ULONG refs = InterlockedDecrement(&This->refs);
if (!refs) HeapFree(GetProcessHeap(), 0, This);
return refs;
}
static inline HRESULT get_target_from_wrapper( IDropTarget *wrapper, IDropTarget **target )
{
DropTargetWrapper* This = impl_from_IDropTarget( wrapper );
*target = GetPropW( This->hwnd, prop_oledroptarget );
if (!*target) return DRAGDROP_E_NOTREGISTERED;
IDropTarget_AddRef( *target );
return S_OK;
}
static HRESULT WINAPI DropTargetWrapper_DragEnter(IDropTarget* iface,
IDataObject* pDataObj,
DWORD grfKeyState,
POINTL pt,
DWORD* pdwEffect)
{
IDropTarget *target;
HRESULT r = get_target_from_wrapper( iface, &target );
if (SUCCEEDED( r ))
{
r = IDropTarget_DragEnter( target, pDataObj, grfKeyState, pt, pdwEffect );
IDropTarget_Release( target );
}
return r;
}
static HRESULT WINAPI DropTargetWrapper_DragOver(IDropTarget* iface,
DWORD grfKeyState,
POINTL pt,
DWORD* pdwEffect)
{
IDropTarget *target;
HRESULT r = get_target_from_wrapper( iface, &target );
if (SUCCEEDED( r ))
{
r = IDropTarget_DragOver( target, grfKeyState, pt, pdwEffect );
IDropTarget_Release( target );
}
return r;
}
static HRESULT WINAPI DropTargetWrapper_DragLeave(IDropTarget* iface)
{
IDropTarget *target;
HRESULT r = get_target_from_wrapper( iface, &target );
if (SUCCEEDED( r ))
{
r = IDropTarget_DragLeave( target );
IDropTarget_Release( target );
}
return r;
}
static HRESULT WINAPI DropTargetWrapper_Drop(IDropTarget* iface,
IDataObject* pDataObj,
DWORD grfKeyState,
POINTL pt,
DWORD* pdwEffect)
{
IDropTarget *target;
HRESULT r = get_target_from_wrapper( iface, &target );
if (SUCCEEDED( r ))
{
r = IDropTarget_Drop( target, pDataObj, grfKeyState, pt, pdwEffect );
IDropTarget_Release( target );
}
return r;
}
static const IDropTargetVtbl DropTargetWrapperVTbl =
{
DropTargetWrapper_QueryInterface,
DropTargetWrapper_AddRef,
DropTargetWrapper_Release,
DropTargetWrapper_DragEnter,
DropTargetWrapper_DragOver,
DropTargetWrapper_DragLeave,
DropTargetWrapper_Drop
};
static IDropTarget* WrapDropTarget( HWND hwnd )
{
DropTargetWrapper* This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (This)
{
This->IDropTarget_iface.lpVtbl = &DropTargetWrapperVTbl;
This->hwnd = hwnd;
This->refs = 1;
}
return &This->IDropTarget_iface;
}
/***********************************************************************
* get_droptarget_pointer
*
* Retrieves the marshalled IDropTarget from the window.
*/
static IDropTarget* get_droptarget_pointer(HWND hwnd)
{
IDropTarget *droptarget = NULL;
HANDLE map;
IStream *stream;
map = get_droptarget_local_handle(hwnd);
if(!map) return NULL;
if(SUCCEEDED(create_stream_from_map(map, &stream)))
{
CoUnmarshalInterface(stream, &IID_IDropTarget, (void**)&droptarget);
IStream_Release(stream);
}
CloseHandle(map);
return droptarget;
}
/***********************************************************************
* RegisterDragDrop (OLE32.@)
*/
HRESULT WINAPI RegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget)
{
DWORD pid = 0;
HRESULT hr;
IStream *stream;
HANDLE map;
IDropTarget *wrapper;
TRACE("(%p,%p)\n", hwnd, pDropTarget);
if (!COM_CurrentApt())
{
ERR("COM not initialized\n");
return E_OUTOFMEMORY;
}
if (!pDropTarget)
return E_INVALIDARG;
if (!IsWindow(hwnd))
{
ERR("invalid hwnd %p\n", hwnd);
return DRAGDROP_E_INVALIDHWND;
}
/* block register for other processes windows */
GetWindowThreadProcessId(hwnd, &pid);
if (pid != GetCurrentProcessId())
{
FIXME("register for another process windows is disabled\n");
return DRAGDROP_E_INVALIDHWND;
}
/* check if the window is already registered */
if (is_droptarget(hwnd))
return DRAGDROP_E_ALREADYREGISTERED;
/*
* Marshal the drop target pointer into a shared memory map and
* store the map's handle in a Wine specific window prop. We also
* store the drop target pointer itself in the
* "OleDropTargetInterface" prop for compatibility with Windows.
*/
hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
if(FAILED(hr)) return hr;
/* IDropTarget::QueryInterface() shouldn't be called, some (broken) apps depend on this. */
wrapper = WrapDropTarget( hwnd );
if(!wrapper)
{
IStream_Release(stream);
return E_OUTOFMEMORY;
}
hr = CoMarshalInterface(stream, &IID_IDropTarget, (IUnknown*)wrapper, MSHCTX_LOCAL, NULL, MSHLFLAGS_TABLESTRONG);
IDropTarget_Release(wrapper);
if(SUCCEEDED(hr))
{
hr = create_map_from_stream(stream, &map);
if(SUCCEEDED(hr))
{
IDropTarget_AddRef(pDropTarget);
SetPropW(hwnd, prop_oledroptarget, pDropTarget);
SetPropW(hwnd, prop_marshalleddroptarget, map);
}
else
{
LARGE_INTEGER zero;
zero.QuadPart = 0;
IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL);
CoReleaseMarshalData(stream);
}
}
IStream_Release(stream);
return hr;
}
/***********************************************************************
* RevokeDragDrop (OLE32.@)
*/
HRESULT WINAPI RevokeDragDrop(HWND hwnd)
{
HANDLE map;
IStream *stream;
IDropTarget *drop_target;
HRESULT hr;
TRACE("(%p)\n", hwnd);
if (!IsWindow(hwnd))
{
ERR("invalid hwnd %p\n", hwnd);
return DRAGDROP_E_INVALIDHWND;
}
/* no registration data */
if (!(map = get_droptarget_handle(hwnd)))
return DRAGDROP_E_NOTREGISTERED;
drop_target = GetPropW(hwnd, prop_oledroptarget);
if(drop_target) IDropTarget_Release(drop_target);
RemovePropW(hwnd, prop_oledroptarget);
RemovePropW(hwnd, prop_marshalleddroptarget);
hr = create_stream_from_map(map, &stream);
if(SUCCEEDED(hr))
{
CoReleaseMarshalData(stream);
IStream_Release(stream);
}
CloseHandle(map);
return hr;
}
/***********************************************************************
* OleRegGetUserType (OLE32.@)
*/
HRESULT WINAPI OleRegGetUserType(REFCLSID clsid, DWORD form, LPOLESTR *usertype)
{
DWORD valuetype, valuelen;
WCHAR auxkeynameW[16];
HKEY usertypekey;
HRESULT hres;
LONG ret;
TRACE("%s, %lu, %p.\n", debugstr_guid(clsid), form, usertype);
if (!usertype)
return E_INVALIDARG;
*usertype = NULL;
/* Return immediately if it's not registered. */
hres = COM_OpenKeyForCLSID(clsid, NULL, KEY_READ, &usertypekey);
if (FAILED(hres))
return hres;
valuelen = 0;
/* Try additional types if requested. If they don't exist fall back to USERCLASSTYPE_FULL. */
if (form != USERCLASSTYPE_FULL)
{
HKEY auxkey;
swprintf(auxkeynameW, ARRAY_SIZE(auxkeynameW), L"AuxUserType\\%d", form);
if (COM_OpenKeyForCLSID(clsid, auxkeynameW, KEY_READ, &auxkey) == S_OK)
{
if (!RegQueryValueExW(auxkey, L"", NULL, &valuetype, NULL, &valuelen) && valuelen)
{
RegCloseKey(usertypekey);
usertypekey = auxkey;
}
else
RegCloseKey(auxkey);
}
}
valuelen = 0;
if (RegQueryValueExW(usertypekey, L"", NULL, &valuetype, NULL, &valuelen))
{
RegCloseKey(usertypekey);
return REGDB_E_READREGDB;
}
*usertype = CoTaskMemAlloc(valuelen);
if (!*usertype)
{
RegCloseKey(usertypekey);
return E_OUTOFMEMORY;
}
ret = RegQueryValueExW(usertypekey, L"", NULL, &valuetype, (BYTE *)*usertype, &valuelen);
RegCloseKey(usertypekey);
if (ret != ERROR_SUCCESS)
{
CoTaskMemFree(*usertype);
*usertype = NULL;
return REGDB_E_READREGDB;
}
return S_OK;
}
/***********************************************************************
* DoDragDrop [OLE32.@]
*/
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("%p, %p, %#lx, %p.\n", pDataObject, pDropSource, dwOKEffect, pdwEffect);
if (!pDataObject || !pDropSource || !pdwEffect)
return E_INVALIDARG;
/*
* Setup the drag n drop tracking window.
*/
trackerInfo.dataObject = pDataObject;
trackerInfo.dropSource = pDropSource;
trackerInfo.dwOKEffect = dwOKEffect;
trackerInfo.pdwEffect = pdwEffect;
trackerInfo.trackingDone = FALSE;
trackerInfo.inTrackCall = FALSE;
trackerInfo.escPressed = FALSE;
trackerInfo.curTargetHWND = 0;
trackerInfo.curDragTarget = 0;
hwndTrackWindow = CreateWindowW(OLEDD_DRAGTRACKERCLASS, L"TrackerWindow",
WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, 0,
&trackerInfo);
if (hwndTrackWindow)
{
/*
* Capture the mouse input
*/
SetCapture(hwndTrackWindow);
msg.message = 0;
/*
* Pump messages. All mouse input should go to the capture window.
*/
while (!trackerInfo.trackingDone && GetMessageW(&msg, 0, 0, 0) )
{
trackerInfo.curMousePos.x = msg.pt.x;
trackerInfo.curMousePos.y = msg.pt.y;
trackerInfo.dwKeyState = OLEDD_GetButtonState();
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);
}
else
{
/*
* Dispatch the messages only when it's not a keyboard message.
*/
DispatchMessageW(&msg);
}
}
/* re-post the quit message to outer message loop */
if (msg.message == WM_QUIT)
PostQuitMessage(msg.wParam);
/*
* Destroy the temporary window.
*/
DestroyWindow(hwndTrackWindow);
return trackerInfo.returnValue;
}
return E_FAIL;
}
/***********************************************************************
* OleQueryLinkFromData [OLE32.@]
*/
HRESULT WINAPI OleQueryLinkFromData(
IDataObject* pSrcDataObject)
{
FIXME("(%p),stub!\n", pSrcDataObject);
return S_FALSE;
}
/***********************************************************************
* OleRegGetMiscStatus [OLE32.@]
*/
HRESULT WINAPI OleRegGetMiscStatus(
REFCLSID clsid,
DWORD dwAspect,
DWORD* pdwStatus)
{
WCHAR keyName[16];
HKEY miscStatusKey;
HKEY aspectKey;
LONG result;
HRESULT hr;
TRACE("%s, %ld, %p.\n", debugstr_guid(clsid), dwAspect, pdwStatus);
if (!pdwStatus) return E_INVALIDARG;
*pdwStatus = 0;
if (actctx_get_miscstatus(clsid, dwAspect, pdwStatus)) return S_OK;
hr = COM_OpenKeyForCLSID(clsid, L"MiscStatus", KEY_READ, &miscStatusKey);
if (FAILED(hr))
/* missing key is not a failure */
return hr == REGDB_E_KEYMISSING ? S_OK : hr;
OLEUTL_ReadRegistryDWORDValue(miscStatusKey, pdwStatus);
/*
* Open the key specific to the requested aspect.
*/
swprintf(keyName, ARRAY_SIZE(keyName), L"%d", dwAspect);
result = open_classes_key(miscStatusKey, keyName, KEY_READ, &aspectKey);
if (result == ERROR_SUCCESS)
{
OLEUTL_ReadRegistryDWORDValue(aspectKey, pdwStatus);
RegCloseKey(aspectKey);
}
RegCloseKey(miscStatusKey);
return S_OK;
}
static HRESULT EnumOLEVERB_Construct(HKEY hkeyVerb, ULONG index, IEnumOLEVERB **ppenum);
typedef struct
{
IEnumOLEVERB IEnumOLEVERB_iface;
LONG ref;
HKEY hkeyVerb;
ULONG index;
} EnumOLEVERB;
static inline EnumOLEVERB *impl_from_IEnumOLEVERB(IEnumOLEVERB *iface)
{
return CONTAINING_RECORD(iface, EnumOLEVERB, IEnumOLEVERB_iface);
}
static HRESULT WINAPI EnumOLEVERB_QueryInterface(
IEnumOLEVERB *iface, REFIID riid, void **ppv)
{
TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IEnumOLEVERB))
{
IEnumOLEVERB_AddRef(iface);
*ppv = iface;
return S_OK;
}
return E_NOINTERFACE;
}
static ULONG WINAPI EnumOLEVERB_AddRef(
IEnumOLEVERB *iface)
{
EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
TRACE("()\n");
return InterlockedIncrement(&This->ref);
}
static ULONG WINAPI EnumOLEVERB_Release(
IEnumOLEVERB *iface)
{
EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
LONG refs = InterlockedDecrement(&This->ref);
TRACE("()\n");
if (!refs)
{
RegCloseKey(This->hkeyVerb);
HeapFree(GetProcessHeap(), 0, This);
}
return refs;
}
static HRESULT WINAPI EnumOLEVERB_Next(
IEnumOLEVERB *iface, ULONG celt, LPOLEVERB rgelt,
ULONG *pceltFetched)
{
EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
HRESULT hr = S_OK;
TRACE("%p, %lu, %p, %p.\n", iface, celt, rgelt, pceltFetched);
if (pceltFetched)
*pceltFetched = 0;
for (; celt; celt--, rgelt++)
{
WCHAR wszSubKey[20];
LONG cbData;
LPWSTR pwszOLEVERB;
LPWSTR pwszMenuFlags;
LPWSTR pwszAttribs;
LONG res = RegEnumKeyW(This->hkeyVerb, This->index, wszSubKey, ARRAY_SIZE(wszSubKey));
if (res == ERROR_NO_MORE_ITEMS)
{
hr = S_FALSE;
break;
}
else if (res != ERROR_SUCCESS)
{
ERR("RegEnumKeyW failed with error %ld\n", res);
hr = REGDB_E_READREGDB;
break;
}
res = RegQueryValueW(This->hkeyVerb, wszSubKey, NULL, &cbData);
if (res != ERROR_SUCCESS)
{
ERR("RegQueryValueW failed with error %ld\n", res);
hr = REGDB_E_READREGDB;
break;
}
pwszOLEVERB = CoTaskMemAlloc(cbData);
if (!pwszOLEVERB)
{
hr = E_OUTOFMEMORY;
break;
}
res = RegQueryValueW(This->hkeyVerb, wszSubKey, pwszOLEVERB, &cbData);
if (res != ERROR_SUCCESS)
{
ERR("RegQueryValueW failed with error %ld\n", res);
hr = REGDB_E_READREGDB;
CoTaskMemFree(pwszOLEVERB);
break;
}
TRACE("verb string: %s\n", debugstr_w(pwszOLEVERB));
pwszMenuFlags = wcschr(pwszOLEVERB, ',');
if (!pwszMenuFlags)
{
hr = OLEOBJ_E_INVALIDVERB;
CoTaskMemFree(pwszOLEVERB);
break;
}
/* nul terminate the name string and advance to first character */
*pwszMenuFlags = '\0';
pwszMenuFlags++;
pwszAttribs = wcschr(pwszMenuFlags, ',');
if (!pwszAttribs)
{
hr = OLEOBJ_E_INVALIDVERB;
CoTaskMemFree(pwszOLEVERB);
break;
}
/* nul terminate the menu string and advance to first character */
*pwszAttribs = '\0';
pwszAttribs++;
/* fill out structure for this verb */
rgelt->lVerb = wcstol(wszSubKey, NULL, 10);
rgelt->lpszVerbName = pwszOLEVERB; /* user should free */
rgelt->fuFlags = wcstol(pwszMenuFlags, NULL, 10);
rgelt->grfAttribs = wcstol(pwszAttribs, NULL, 10);
if (pceltFetched)
(*pceltFetched)++;
This->index++;
}
return hr;
}
static HRESULT WINAPI EnumOLEVERB_Skip(
IEnumOLEVERB *iface, ULONG celt)
{
EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
TRACE("%p, %lu.\n", iface, celt);
This->index += celt;
return S_OK;
}
static HRESULT WINAPI EnumOLEVERB_Reset(
IEnumOLEVERB *iface)
{
EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
TRACE("()\n");
This->index = 0;
return S_OK;
}
static HRESULT WINAPI EnumOLEVERB_Clone(
IEnumOLEVERB *iface,
IEnumOLEVERB **ppenum)
{
EnumOLEVERB *This = impl_from_IEnumOLEVERB(iface);
HKEY hkeyVerb;
TRACE("(%p)\n", ppenum);
if (!DuplicateHandle(GetCurrentProcess(), This->hkeyVerb, GetCurrentProcess(), (HANDLE *)&hkeyVerb, 0, FALSE, DUPLICATE_SAME_ACCESS))
return HRESULT_FROM_WIN32(GetLastError());
return EnumOLEVERB_Construct(hkeyVerb, This->index, ppenum);
}
static const IEnumOLEVERBVtbl EnumOLEVERB_VTable =
{
EnumOLEVERB_QueryInterface,
EnumOLEVERB_AddRef,
EnumOLEVERB_Release,
EnumOLEVERB_Next,
EnumOLEVERB_Skip,
EnumOLEVERB_Reset,
EnumOLEVERB_Clone
};
static HRESULT EnumOLEVERB_Construct(HKEY hkeyVerb, ULONG index, IEnumOLEVERB **ppenum)
{
EnumOLEVERB *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This)
{
RegCloseKey(hkeyVerb);
return E_OUTOFMEMORY;
}
This->IEnumOLEVERB_iface.lpVtbl = &EnumOLEVERB_VTable;
This->ref = 1;
This->index = index;
This->hkeyVerb = hkeyVerb;
*ppenum = &This->IEnumOLEVERB_iface;
return S_OK;
}
/***********************************************************************
* OleRegEnumVerbs [OLE32.@]
*
* Enumerates verbs associated with a class stored in the registry.
*
* PARAMS
* clsid [I] Class ID to enumerate the verbs for.
* ppenum [O] Enumerator.
*
* RETURNS
* S_OK: Success.
* REGDB_E_CLASSNOTREG: The specified class does not have a key in the registry.
* REGDB_E_READREGDB: The class key could not be opened for some other reason.
* OLE_E_REGDB_KEY: The Verb subkey for the class is not present.
* OLEOBJ_E_NOVERBS: The Verb subkey for the class is empty.
*/
HRESULT WINAPI OleRegEnumVerbs (REFCLSID clsid, LPENUMOLEVERB* ppenum)
{
LONG res;
HKEY hkeyVerb;
DWORD dwSubKeys;
TRACE("(%s, %p)\n", debugstr_guid(clsid), ppenum);
res = COM_OpenKeyForCLSID(clsid, L"Verb", KEY_READ, &hkeyVerb);
if (FAILED(res))
{
if (res == REGDB_E_CLASSNOTREG)
ERR("CLSID %s not registered\n", debugstr_guid(clsid));
else if (res == REGDB_E_KEYMISSING)
ERR("no Verbs key for class %s\n", debugstr_guid(clsid));
else
ERR("failed to open Verbs key for CLSID %s with error %ld\n",
debugstr_guid(clsid), res);
return res;
}
res = RegQueryInfoKeyW(hkeyVerb, NULL, NULL, NULL, &dwSubKeys, NULL,
NULL, NULL, NULL, NULL, NULL, NULL);
if (res != ERROR_SUCCESS)
{
ERR("failed to get subkey count with error %ld\n", GetLastError());
return REGDB_E_READREGDB;
}
if (!dwSubKeys)
{
WARN("class %s has no verbs\n", debugstr_guid(clsid));
RegCloseKey(hkeyVerb);
return OLEOBJ_E_NOVERBS;
}
return EnumOLEVERB_Construct(hkeyVerb, 0, ppenum);
}
/******************************************************************************
* OleSetContainedObject [OLE32.@]
*/
HRESULT WINAPI OleSetContainedObject(
LPUNKNOWN pUnknown,
BOOL fContained)
{
IRunnableObject* runnable = NULL;
HRESULT hres;
TRACE("(%p,%x)\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;
}
/******************************************************************************
* OleRun [OLE32.@]
*
* Set the OLE object to the running state.
*
* PARAMS
* pUnknown [I] OLE object to run.
*
* RETURNS
* Success: S_OK.
* Failure: Any HRESULT code.
*/
HRESULT WINAPI DECLSPEC_HOTPATCH OleRun(LPUNKNOWN pUnknown)
{
IRunnableObject *runable;
HRESULT hres;
TRACE("(%p)\n", pUnknown);
hres = IUnknown_QueryInterface(pUnknown, &IID_IRunnableObject, (void**)&runable);
if (FAILED(hres))
return S_OK; /* Appears to return no error. */
hres = IRunnableObject_Run(runable, NULL);
IRunnableObject_Release(runable);
return hres;
}
/******************************************************************************
* OleLoad [OLE32.@]
*/
HRESULT WINAPI OleLoad(
LPSTORAGE pStg,
REFIID riid,
LPOLECLIENTSITE pClientSite,
LPVOID* ppvObj)
{
IPersistStorage* persistStorage = NULL;
IUnknown* pUnk;
IOleObject* pOleObject = NULL;
STATSTG storageInfo;
HRESULT hres;
TRACE("(%p, %s, %p, %p)\n", pStg, debugstr_guid(riid), pClientSite, ppvObj);
*ppvObj = NULL;
/*
* TODO, Conversion ... OleDoAutoConvert
*/
/*
* Get the class ID for the object.
*/
hres = IStorage_Stat(pStg, &storageInfo, STATFLAG_NONAME);
if (FAILED(hres))
return hres;
/*
* Now, try and create the handler for the object
*/
hres = CoCreateInstance(&storageInfo.clsid,
NULL,
CLSCTX_INPROC_HANDLER|CLSCTX_INPROC_SERVER,
riid,
(void**)&pUnk);
/*
* If that fails, as it will most times, load the default
* OLE handler.
*/
if (FAILED(hres))
{
hres = OleCreateDefaultHandler(&storageInfo.clsid,
NULL,
riid,
(void**)&pUnk);
}
/*
* If we couldn't find a handler... this is bad. Abort the whole thing.
*/
if (FAILED(hres))
return hres;
if (pClientSite)
{
hres = IUnknown_QueryInterface(pUnk, &IID_IOleObject, (void **)&pOleObject);
if (SUCCEEDED(hres))
{
DWORD dwStatus;
hres = IOleObject_GetMiscStatus(pOleObject, DVASPECT_CONTENT, &dwStatus);
}
}
/*
* Initialize the object with its IPersistStorage interface.
*/
hres = IUnknown_QueryInterface(pUnk, &IID_IPersistStorage, (void**)&persistStorage);
if (SUCCEEDED(hres))
{
hres = IPersistStorage_Load(persistStorage, pStg);
IPersistStorage_Release(persistStorage);
persistStorage = NULL;
}
if (SUCCEEDED(hres) && pClientSite)
/*
* Inform the new object of its client site.
*/
hres = IOleObject_SetClientSite(pOleObject, pClientSite);
/*
* Cleanup interfaces used internally
*/
if (pOleObject)
IOleObject_Release(pOleObject);
if (SUCCEEDED(hres))
{
IOleLink *pOleLink;
HRESULT hres1;
hres1 = IUnknown_QueryInterface(pUnk, &IID_IOleLink, (void **)&pOleLink);
if (SUCCEEDED(hres1))
{
FIXME("handle OLE link\n");
IOleLink_Release(pOleLink);
}
}
if (FAILED(hres))
{
IUnknown_Release(pUnk);
pUnk = NULL;
}
*ppvObj = pUnk;
return hres;
}
/***********************************************************************
* OleSave [OLE32.@]
*/
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.@]
*/
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;
}
return S_OK;
}
/**************************************************************************
* 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(void)
{
}
/***
* OLEMenu_UnInitialize()
*
* Releases the OLEMENU data structures.
*/
static void OLEMenu_UnInitialize(void)
{
}
/*************************************************************************
* OLEMenu_InstallHooks
* Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC
*
* RETURNS: TRUE if message hooks were successfully installed
* FALSE on failure
*/
static BOOL OLEMenu_InstallHooks( DWORD tid )
{
OleMenuHookItem *pHookItem;
/* Create an entry for the hook table */
if ( !(pHookItem = HeapAlloc(GetProcessHeap(), 0,
sizeof(OleMenuHookItem)) ) )
return FALSE;
pHookItem->tid = tid;
pHookItem->hHeap = GetProcessHeap();
pHookItem->CallWndProc_hHook = NULL;
/* Install a thread scope message hook for WH_GETMESSAGE */
pHookItem->GetMsg_hHook = SetWindowsHookExW( WH_GETMESSAGE, OLEMenu_GetMsgProc,
0, GetCurrentThreadId() );
if ( !pHookItem->GetMsg_hHook )
goto CLEANUP;
/* Install a thread scope message hook for WH_CALLWNDPROC */
pHookItem->CallWndProc_hHook = SetWindowsHookExW( 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 successfully installed
* FALSE on failure
*/
static 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 */
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
*/
static OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid )
{
OleMenuHookItem *pHookItem;
/* 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 )
{
INT 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
* FALSE if the popup menu is part of a container owned group
*/
static 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;
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.
*/
static LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam)
{
LPCWPSTRUCT pMsg;
HOLEMENU hOleMenu = 0;
OleMenuDescriptor *pOleMenuDescriptor = NULL;
OleMenuHookItem *pHookItem = NULL;
WORD fuFlags;
TRACE("%i, %#Ix, %#Ix.\n", code, wParam, 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 = GetPropW( pMsg->hwnd, prop_olemenuW );
if ( !hOleMenu )
goto NEXTHOOK;
/* Get the menu descriptor */
pOleMenuDescriptor = 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 */
SendMessageW( 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 )
{
SendMessageW( 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.
*/
static LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam)
{
LPMSG pMsg;
HOLEMENU hOleMenu = 0;
OleMenuDescriptor *pOleMenuDescriptor = NULL;
OleMenuHookItem *pHookItem = NULL;
WORD wCode;
TRACE("%i, %#Ix, %#Ix.\n", code, wParam, 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 = GetPropW( pMsg->hwnd, prop_olemenuW );
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 = 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.@]
* 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 = 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.@]
* Destroy the shared menu descriptor
*/
HRESULT WINAPI OleDestroyMenuDescriptor(
HOLEMENU hmenuDescriptor)
{
if ( hmenuDescriptor )
GlobalFree( hmenuDescriptor );
return S_OK;
}
/***********************************************************************
* OleSetMenuDescriptor [OLE32.@]
* Installs or removes OLE dispatching code for the containers frame window.
*
* 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
*
* 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.
*/
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("(%p, %p, %p, %p, %p), Context sensitive help filtering not implemented!\n",
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 = 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 */
SetPropW( hwndFrame, prop_olemenuW, 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 */
RemovePropW( hwndFrame, prop_olemenuW );
}
return S_OK;
}
/******************************************************************************
* IsAccelerator [OLE32.@]
* Mostly copied from controls/menu.c TranslateAccelerator implementation
*/
BOOL WINAPI IsAccelerator(HACCEL hAccel, int cAccelEntries, LPMSG lpMsg, WORD* lpwCmd)
{
LPACCEL lpAccelTbl;
int i;
if(!lpMsg) return FALSE;
if (!hAccel)
{
WARN_(accel)("NULL accel handle\n");
return FALSE;
}
if((lpMsg->message != WM_KEYDOWN &&
lpMsg->message != WM_SYSKEYDOWN &&
lpMsg->message != WM_SYSCHAR &&
lpMsg->message != WM_CHAR)) return FALSE;
lpAccelTbl = HeapAlloc(GetProcessHeap(), 0, cAccelEntries * sizeof(ACCEL));
if (NULL == lpAccelTbl)
{
return FALSE;
}
if (CopyAcceleratorTableW(hAccel, lpAccelTbl, cAccelEntries) != cAccelEntries)
{
WARN_(accel)("CopyAcceleratorTableW failed\n");
HeapFree(GetProcessHeap(), 0, lpAccelTbl);
return FALSE;
}
TRACE_(accel)("hAccel=%p, cAccelEntries=%d,"
"msg->hwnd=%p, msg->message=%04x, wParam=%#Ix, lParam=%#Ix\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", LOWORD(lpMsg->wParam) & 0xff);
goto found;
}
}
else
{
if(lpAccelTbl[i].fVirt & FVIRTKEY)
{
INT mask = 0;
TRACE_(accel)("found accel for virt_key %Ix (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", LOWORD(lpMsg->wParam) & 0xff);
goto found;
}
}
}
}
}
WARN_(accel)("couldn't translate accelerator key\n");
HeapFree(GetProcessHeap(), 0, lpAccelTbl);
return FALSE;
found:
if(lpwCmd) *lpwCmd = lpAccelTbl[i].cmd;
HeapFree(GetProcessHeap(), 0, lpAccelTbl);
return TRUE;
}
/***********************************************************************
* ReleaseStgMedium [OLE32.@]
*/
void WINAPI ReleaseStgMedium(
STGMEDIUM* pmedium)
{
if (!pmedium) return;
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(void)
{
WNDCLASSW wndClass;
ZeroMemory (&wndClass, sizeof(WNDCLASSW));
wndClass.style = CS_GLOBALCLASS;
wndClass.lpfnWndProc = OLEDD_DragTrackerWindowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = sizeof(TrackerWindowInfo*);
wndClass.hCursor = 0;
wndClass.hbrBackground = 0;
wndClass.lpszClassName = OLEDD_DRAGTRACKERCLASS;
RegisterClassW (&wndClass);
}
/***
* 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.
*/
#define DRAG_TIMER_ID 1
static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
{
LPCREATESTRUCTA createStruct = (LPCREATESTRUCTA)lParam;
SetWindowLongPtrW(hwnd, 0, (LONG_PTR)createStruct->lpCreateParams);
SetTimer(hwnd, DRAG_TIMER_ID, 50, NULL);
break;
}
case WM_TIMER:
case WM_MOUSEMOVE:
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
{
TrackerWindowInfo *trackerInfo = (TrackerWindowInfo*)GetWindowLongPtrA(hwnd, 0);
if (trackerInfo->trackingDone) break;
OLEDD_TrackStateChange(trackerInfo);
break;
}
case WM_DESTROY:
{
KillTimer(hwnd, DRAG_TIMER_ID);
break;
}
}
/*
* This is a window proc after all. Let's call the default.
*/
return DefWindowProcW (hwnd, uMsg, wParam, lParam);
}
static void drag_enter( TrackerWindowInfo *info, HWND new_target )
{
HRESULT hr;
info->curTargetHWND = new_target;
while (new_target && !is_droptarget( new_target ))
new_target = GetParent( new_target );
info->curDragTarget = get_droptarget_pointer( new_target );
if (info->curDragTarget)
{
*info->pdwEffect = info->dwOKEffect;
hr = IDropTarget_DragEnter( info->curDragTarget, info->dataObject,
info->dwKeyState, info->curMousePos,
info->pdwEffect );
*info->pdwEffect &= info->dwOKEffect;
/* failed DragEnter() means invalid target */
if (hr != S_OK)
{
IDropTarget_Release( info->curDragTarget );
info->curDragTarget = NULL;
info->curTargetHWND = NULL;
}
}
}
static void drag_end( TrackerWindowInfo *info )
{
HRESULT hr;
info->trackingDone = TRUE;
ReleaseCapture();
if (info->curDragTarget)
{
if (info->returnValue == DRAGDROP_S_DROP &&
*info->pdwEffect != DROPEFFECT_NONE)
{
*info->pdwEffect = info->dwOKEffect;
hr = IDropTarget_Drop( info->curDragTarget, info->dataObject, info->dwKeyState,
info->curMousePos, info->pdwEffect );
*info->pdwEffect &= info->dwOKEffect;
if (FAILED( hr ))
info->returnValue = hr;
}
else
{
IDropTarget_DragLeave( info->curDragTarget );
*info->pdwEffect = DROPEFFECT_NONE;
}
IDropTarget_Release( info->curDragTarget );
info->curDragTarget = NULL;
}
else
*info->pdwEffect = DROPEFFECT_NONE;
}
static HRESULT give_feedback( TrackerWindowInfo *info )
{
HRESULT hr;
int res;
HCURSOR cur;
if (info->curDragTarget == NULL)
*info->pdwEffect = DROPEFFECT_NONE;
hr = IDropSource_GiveFeedback( info->dropSource, *info->pdwEffect );
if (hr == DRAGDROP_S_USEDEFAULTCURSORS)
{
if (*info->pdwEffect & DROPEFFECT_MOVE)
res = CURSOR_MOVE;
else if (*info->pdwEffect & DROPEFFECT_COPY)
res = CURSOR_COPY;
else if (*info->pdwEffect & DROPEFFECT_LINK)
res = CURSOR_LINK;
else
res = CURSOR_NODROP;
cur = LoadCursorW( hProxyDll, MAKEINTRESOURCEW( res ) );
SetCursor( cur );
}
return hr;
}
/***
* OLEDD_TrackStateChange()
*
* This method is invoked while a drag and drop operation is in effect.
*
* params:
* trackerInfo - Pointer to the structure identifying the
* drag & drop operation that is currently
* active.
*/
static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo)
{
HWND hwndNewTarget = 0;
POINT pt;
/*
* This method may be called from QueryContinueDrag again,
* (i.e. by running message loop) so avoid recursive call chain.
*/
if (trackerInfo->inTrackCall) return;
trackerInfo->inTrackCall = TRUE;
/*
* Get the handle of the window under the mouse
*/
pt.x = trackerInfo->curMousePos.x;
pt.y = trackerInfo->curMousePos.y;
hwndNewTarget = WindowFromPoint(pt);
trackerInfo->returnValue = IDropSource_QueryContinueDrag(trackerInfo->dropSource,
trackerInfo->escPressed,
trackerInfo->dwKeyState);
if (trackerInfo->curTargetHWND != hwndNewTarget &&
(trackerInfo->returnValue == S_OK ||
trackerInfo->returnValue == DRAGDROP_S_DROP))
{
if (trackerInfo->curDragTarget)
{
IDropTarget_DragLeave(trackerInfo->curDragTarget);
IDropTarget_Release(trackerInfo->curDragTarget);
trackerInfo->curDragTarget = NULL;
trackerInfo->curTargetHWND = NULL;
}
if (hwndNewTarget)
drag_enter( trackerInfo, hwndNewTarget );
give_feedback( trackerInfo );
}
if (trackerInfo->returnValue == S_OK)
{
if (trackerInfo->curDragTarget)
{
*trackerInfo->pdwEffect = trackerInfo->dwOKEffect;
IDropTarget_DragOver(trackerInfo->curDragTarget,
trackerInfo->dwKeyState,
trackerInfo->curMousePos,
trackerInfo->pdwEffect);
*trackerInfo->pdwEffect &= trackerInfo->dwOKEffect;
}
give_feedback( trackerInfo );
}
else
drag_end( trackerInfo );
trackerInfo->inTrackCall = FALSE;
}
/***
* 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(void)
{
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_MENU] & 0x80) !=0)
keyMask |= MK_ALT;
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)
{
WCHAR buffer[20];
DWORD cbData = sizeof(buffer);
DWORD dwKeyType;
LONG lres;
lres = RegQueryValueExW(regKey, L"", NULL, &dwKeyType, (BYTE *)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 = wcstoul(buffer, NULL, 10);
break;
}
}
}
/******************************************************************************
* OleDraw (OLE32.@)
*
* 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 rect)
{
HRESULT hres;
IViewObject *viewobject;
if (!pUnk) return E_INVALIDARG;
hres = IUnknown_QueryInterface(pUnk,
&IID_IViewObject,
(void**)&viewobject);
if (SUCCEEDED(hres))
{
hres = IViewObject_Draw(viewobject, dwAspect, -1, 0, 0, 0, hdcDraw, (RECTL*)rect, 0, 0, 0);
IViewObject_Release(viewobject);
return hres;
}
else
return DV_E_NOIVIEWOBJECT;
}
/***********************************************************************
* OleTranslateAccelerator [OLE32.@]
*/
HRESULT WINAPI OleTranslateAccelerator (LPOLEINPLACEFRAME lpFrame,
LPOLEINPLACEFRAMEINFO lpFrameInfo, LPMSG lpmsg)
{
WORD wID;
TRACE("(%p,%p,%p)\n", lpFrame, lpFrameInfo, lpmsg);
if (IsAccelerator(lpFrameInfo->haccel,lpFrameInfo->cAccelEntries,lpmsg,&wID))
return IOleInPlaceFrame_TranslateAccelerator(lpFrame,lpmsg,wID);
return S_FALSE;
}
/******************************************************************************
* OleCreate [OLE32.@]
*
*/
HRESULT WINAPI OleCreate(
REFCLSID rclsid,
REFIID riid,
DWORD renderopt,
LPFORMATETC pFormatEtc,
LPOLECLIENTSITE pClientSite,
LPSTORAGE pStg,
LPVOID* ppvObj)
{
HRESULT hres;
IUnknown * pUnk = NULL;
IOleObject *pOleObject = NULL;
TRACE("%s, %s, %ld, %p, %p, %p, %p.\n", debugstr_guid(rclsid),
debugstr_guid(riid), renderopt, pFormatEtc, pClientSite, pStg, ppvObj);
hres = CoCreateInstance(rclsid, 0, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER, riid, (LPVOID*)&pUnk);
if (SUCCEEDED(hres))
hres = IStorage_SetClass(pStg, rclsid);
if (pClientSite && SUCCEEDED(hres))
{
hres = IUnknown_QueryInterface(pUnk, &IID_IOleObject, (LPVOID*)&pOleObject);
if (SUCCEEDED(hres))
{
DWORD dwStatus;
IOleObject_GetMiscStatus(pOleObject, DVASPECT_CONTENT, &dwStatus);
}
}
if (SUCCEEDED(hres))
{
IPersistStorage * pPS;
if (SUCCEEDED((hres = IUnknown_QueryInterface(pUnk, &IID_IPersistStorage, (LPVOID*)&pPS))))
{
TRACE("trying to set stg %p\n", pStg);
hres = IPersistStorage_InitNew(pPS, pStg);
TRACE("-- result %#lx\n", hres);
IPersistStorage_Release(pPS);
}
}
if (pClientSite && SUCCEEDED(hres))
{
TRACE("trying to set clientsite %p\n", pClientSite);
hres = IOleObject_SetClientSite(pOleObject, pClientSite);
TRACE("-- result %#lx\n", hres);
}
if (pOleObject)
IOleObject_Release(pOleObject);
if (((renderopt == OLERENDER_DRAW) || (renderopt == OLERENDER_FORMAT)) &&
SUCCEEDED(hres))
{
hres = OleRun(pUnk);
if (SUCCEEDED(hres))
{
IOleCache *pOleCache;
if (SUCCEEDED(IUnknown_QueryInterface(pUnk, &IID_IOleCache, (void **)&pOleCache)))
{
DWORD dwConnection;
if (renderopt == OLERENDER_DRAW && !pFormatEtc) {
FORMATETC pfe;
pfe.cfFormat = 0;
pfe.ptd = NULL;
pfe.dwAspect = DVASPECT_CONTENT;
pfe.lindex = -1;
pfe.tymed = TYMED_NULL;
hres = IOleCache_Cache(pOleCache, &pfe, ADVF_PRIMEFIRST, &dwConnection);
}
else
hres = IOleCache_Cache(pOleCache, pFormatEtc, ADVF_PRIMEFIRST, &dwConnection);
IOleCache_Release(pOleCache);
}
}
}
if (FAILED(hres) && pUnk)
{
IUnknown_Release(pUnk);
pUnk = NULL;
}
*ppvObj = pUnk;
TRACE("-- %p\n", pUnk);
return hres;
}
/******************************************************************************
* OleGetAutoConvert [OLE32.@]
*/
HRESULT WINAPI OleGetAutoConvert(REFCLSID clsidOld, LPCLSID pClsidNew)
{
HKEY hkey = NULL;
WCHAR buf[CHARS_IN_GUID];
LONG len;
HRESULT res = S_OK;
res = COM_OpenKeyForCLSID(clsidOld, L"AutoConvertTo", KEY_READ, &hkey);
if (FAILED(res))
goto done;
len = sizeof(buf);
if (RegQueryValueW(hkey, NULL, buf, &len))
{
res = REGDB_E_KEYMISSING;
goto done;
}
res = CLSIDFromString(buf, pClsidNew);
done:
if (hkey) RegCloseKey(hkey);
return res;
}
/******************************************************************************
* OleSetAutoConvert [OLE32.@]
*/
HRESULT WINAPI OleSetAutoConvert(REFCLSID clsidOld, REFCLSID clsidNew)
{
HKEY hkey = NULL;
WCHAR szClsidNew[CHARS_IN_GUID];
HRESULT res = S_OK;
TRACE("(%s,%s)\n", debugstr_guid(clsidOld), debugstr_guid(clsidNew));
res = COM_OpenKeyForCLSID(clsidOld, NULL, KEY_READ | KEY_WRITE, &hkey);
if (FAILED(res))
goto done;
StringFromGUID2(clsidNew, szClsidNew, CHARS_IN_GUID);
if (RegSetValueW(hkey, L"AutoConvertTo", REG_SZ, szClsidNew, (lstrlenW(szClsidNew)+1) * sizeof(WCHAR)))
{
res = REGDB_E_WRITEREGDB;
goto done;
}
done:
if (hkey) RegCloseKey(hkey);
return res;
}
/******************************************************************************
* OleDoAutoConvert [OLE32.@]
*/
HRESULT WINAPI OleDoAutoConvert(LPSTORAGE pStg, LPCLSID pClsidNew)
{
WCHAR *user_type_old, *user_type_new;
CLIPFORMAT cf;
STATSTG stat;
CLSID clsid;
HRESULT hr;
TRACE("(%p, %p)\n", pStg, pClsidNew);
*pClsidNew = CLSID_NULL;
if(!pStg)
return E_INVALIDARG;
hr = IStorage_Stat(pStg, &stat, STATFLAG_NONAME);
if(FAILED(hr))
return hr;
*pClsidNew = stat.clsid;
hr = OleGetAutoConvert(&stat.clsid, &clsid);
if(FAILED(hr))
return hr;
hr = IStorage_SetClass(pStg, &clsid);
if(FAILED(hr))
return hr;
hr = ReadFmtUserTypeStg(pStg, &cf, &user_type_old);
if(FAILED(hr)) {
cf = 0;
user_type_new = NULL;
}
hr = OleRegGetUserType(&clsid, USERCLASSTYPE_FULL, &user_type_new);
if(FAILED(hr))
user_type_new = NULL;
hr = WriteFmtUserTypeStg(pStg, cf, user_type_new);
CoTaskMemFree(user_type_new);
if(FAILED(hr))
{
CoTaskMemFree(user_type_old);
IStorage_SetClass(pStg, &stat.clsid);
return hr;
}
hr = SetConvertStg(pStg, TRUE);
if(FAILED(hr))
{
WriteFmtUserTypeStg(pStg, cf, user_type_old);
IStorage_SetClass(pStg, &stat.clsid);
}
else
*pClsidNew = clsid;
CoTaskMemFree(user_type_old);
return hr;
}
/******************************************************************************
* OleIsRunning [OLE32.@]
*/
BOOL WINAPI OleIsRunning(LPOLEOBJECT object)
{
IRunnableObject *pRunnable;
HRESULT hr;
BOOL running;
TRACE("(%p)\n", object);
if (!object) return FALSE;
hr = IOleObject_QueryInterface(object, &IID_IRunnableObject, (void **)&pRunnable);
if (FAILED(hr))
return TRUE;
running = IRunnableObject_IsRunning(pRunnable);
IRunnableObject_Release(pRunnable);
return running;
}
/***********************************************************************
* OleNoteObjectVisible [OLE32.@]
*/
HRESULT WINAPI OleNoteObjectVisible(LPUNKNOWN pUnknown, BOOL bVisible)
{
TRACE("(%p, %s)\n", pUnknown, bVisible ? "TRUE" : "FALSE");
return CoLockObjectExternal(pUnknown, bVisible, TRUE);
}
/***********************************************************************
* PropSysAllocString [OLE32.@]
* NOTES
* Forward to oleaut32.
*/
BSTR WINAPI PropSysAllocString(LPCOLESTR str)
{
return SysAllocString(str);
}
/***********************************************************************
* PropSysFreeString [OLE32.@]
* NOTES
* Forward to oleaut32.
*/
void WINAPI PropSysFreeString(LPOLESTR str)
{
SysFreeString(str);
}
/******************************************************************************
* OleQueryCreateFromData [OLE32.@]
*
* Checks whether an object can become an embedded object.
* the clipboard or OLE drag and drop.
* Returns : S_OK - Format that supports Embedded object creation are present.
* OLE_E_STATIC - Format that supports static object creation are present.
* S_FALSE - No acceptable format is available.
*/
HRESULT WINAPI OleQueryCreateFromData(IDataObject *data)
{
IEnumFORMATETC *enum_fmt;
FORMATETC fmt;
BOOL found_static = FALSE;
HRESULT hr;
hr = IDataObject_EnumFormatEtc(data, DATADIR_GET, &enum_fmt);
if(FAILED(hr)) return hr;
do
{
hr = IEnumFORMATETC_Next(enum_fmt, 1, &fmt, NULL);
if(hr == S_OK)
{
if(fmt.cfFormat == embedded_object_clipboard_format ||
fmt.cfFormat == embed_source_clipboard_format ||
fmt.cfFormat == filename_clipboard_format)
{
IEnumFORMATETC_Release(enum_fmt);
return S_OK;
}
if(fmt.cfFormat == CF_METAFILEPICT ||
fmt.cfFormat == CF_BITMAP ||
fmt.cfFormat == CF_DIB)
found_static = TRUE;
}
} while (hr == S_OK);
IEnumFORMATETC_Release(enum_fmt);
return found_static ? OLE_S_STATIC : S_FALSE;
}
static inline void init_fmtetc(FORMATETC *fmt, CLIPFORMAT cf, TYMED tymed)
{
fmt->cfFormat = cf;
fmt->ptd = NULL;
fmt->dwAspect = DVASPECT_CONTENT;
fmt->lindex = -1;
fmt->tymed = tymed;
}
/***************************************************************************
* get_storage
*
* Retrieve an object's storage from a variety of sources.
*
* FIXME: CF_FILENAME.
*/
static HRESULT get_storage(IDataObject *data, IStorage *stg, UINT *src_cf, BOOL other_fmts)
{
static const UINT fmt_id[] = { CF_METAFILEPICT, CF_BITMAP, CF_DIB };
UINT i;
HRESULT hr;
FORMATETC fmt;
STGMEDIUM med;
IPersistStorage *persist;
CLSID clsid;
if (src_cf) *src_cf = 0;
/* CF_EMBEDEDOBJECT */
init_fmtetc(&fmt, embedded_object_clipboard_format, TYMED_ISTORAGE);
med.tymed = TYMED_ISTORAGE;
med.u.pstg = stg;
med.pUnkForRelease = NULL;
hr = IDataObject_GetDataHere(data, &fmt, &med);
if(SUCCEEDED(hr))
{
if (src_cf) *src_cf = embedded_object_clipboard_format;
return hr;
}
/* CF_EMBEDSOURCE */
init_fmtetc(&fmt, embed_source_clipboard_format, TYMED_ISTORAGE);
med.tymed = TYMED_ISTORAGE;
med.u.pstg = stg;
med.pUnkForRelease = NULL;
hr = IDataObject_GetDataHere(data, &fmt, &med);
if(SUCCEEDED(hr))
{
if (src_cf) *src_cf = embed_source_clipboard_format;
return hr;
}
if (other_fmts)
{
for (i = 0; i < ARRAY_SIZE(fmt_id); i++)
{
init_fmtetc(&fmt, fmt_id[i], TYMED_ISTORAGE);
hr = IDataObject_QueryGetData(data, &fmt);
if (SUCCEEDED(hr))
{
if (src_cf) *src_cf = fmt_id[i];
return hr;
}
}
}
/* IPersistStorage */
hr = IDataObject_QueryInterface(data, &IID_IPersistStorage, (void**)&persist);
if(FAILED(hr)) return hr;
hr = IPersistStorage_GetClassID(persist, &clsid);
if(FAILED(hr)) goto end;
hr = IStorage_SetClass(stg, &clsid);
if(FAILED(hr)) goto end;
hr = IPersistStorage_Save(persist, stg, FALSE);
if(FAILED(hr)) goto end;
hr = IPersistStorage_SaveCompleted(persist, NULL);
end:
IPersistStorage_Release(persist);
return hr;
}
/******************************************************************************
* OleCreateFromDataEx [OLE32.@]
*
* Creates an embedded object from data transfer object retrieved from
* the clipboard or OLE drag and drop.
*/
HRESULT WINAPI OleCreateFromDataEx(IDataObject *data, REFIID iid, DWORD flags,
DWORD renderopt, ULONG num_cache_fmts, DWORD *adv_flags, FORMATETC *cache_fmts,
IAdviseSink *sink, DWORD *conns,
IOleClientSite *client_site, IStorage *stg, void **obj)
{
HRESULT hr;
UINT src_cf;
FIXME("%p, %s, %#lx, %#lx, %ld, %p, %p, %p, %p, %p, %p, %p: stub\n",
data, debugstr_guid(iid), flags, renderopt, num_cache_fmts, adv_flags, cache_fmts,
sink, conns, client_site, stg, obj);
hr = get_storage(data, stg, &src_cf, TRUE);
if(FAILED(hr)) return hr;
hr = OleLoad(stg, iid, client_site, obj);
if(FAILED(hr)) return hr;
/* FIXME: Init cache */
return hr;
}
/******************************************************************************
* OleCreateFromData [OLE32.@]
*/
HRESULT WINAPI OleCreateFromData(IDataObject *data, REFIID iid, DWORD renderopt, FORMATETC *fmt,
IOleClientSite *client_site, IStorage *stg, void **obj)
{
DWORD advf = ADVF_PRIMEFIRST;
return OleCreateFromDataEx(data, iid, 0, renderopt, fmt ? 1 : 0, fmt ? &advf : NULL,
fmt, NULL, NULL, client_site, stg, obj);
}
/******************************************************************************
* OleCreateLinkFromData [OLE32.@]
*/
HRESULT WINAPI OleCreateLinkFromData(IDataObject *data, REFIID iid, DWORD renderopt, FORMATETC *fmt,
IOleClientSite *client_site, IStorage *stg, void **obj)
{
FIXME("%p, %s, %#lx, %p, %p, %p, %p: semi-stub\n",
data, debugstr_guid(iid), renderopt, fmt, client_site, stg, obj);
return OleCreateFromData(data, iid, renderopt, fmt, client_site, stg, obj);
}
/******************************************************************************
* OleCreateStaticFromData [OLE32.@]
*/
HRESULT WINAPI OleCreateStaticFromData(IDataObject *data, REFIID iid, DWORD renderopt, FORMATETC *fmt,
IOleClientSite *client_site, IStorage *stg, void **obj)
{
HRESULT hr;
CLSID clsid;
IOleObject * ole_object = NULL;
IOleCache2 *ole_cache = NULL;
IPersistStorage *persist = NULL;
DWORD connection;
STGMEDIUM stgmedium;
LPOLESTR ole_typename;
TRACE("%p, %s, %#lx, %p, %p, %p, %p.\n", data, debugstr_guid(iid), renderopt, fmt, client_site, stg, obj);
if (!obj || !stg)
return E_INVALIDARG;
if (renderopt != OLERENDER_FORMAT)
{
FIXME("semi-stub\n");
return OleCreateFromData(data, iid, renderopt, fmt, client_site, stg, obj);
}
if (!fmt)
return E_INVALIDARG;
hr = IDataObject_GetData(data, fmt, &stgmedium);
if (FAILED(hr)) return hr;
switch (fmt->cfFormat)
{
case CF_BITMAP:
case CF_DIB:
clsid = CLSID_Picture_Dib;
break;
case CF_ENHMETAFILE:
clsid = CLSID_Picture_EnhMetafile;
break;
case CF_METAFILEPICT:
clsid = CLSID_Picture_Metafile;
break;
default:
ReleaseStgMedium(&stgmedium);
return DV_E_CLIPFORMAT;
}
hr = OleCreateDefaultHandler(&clsid, NULL, &IID_IOleObject, (void **)&ole_object);
if (FAILED(hr)) goto end;
if (client_site)
{
hr = IOleObject_SetClientSite(ole_object, client_site);
if (FAILED(hr)) goto end;
}
hr = IOleObject_QueryInterface(ole_object, &IID_IOleCache2, (void **)&ole_cache);
if (FAILED(hr)) goto end;
hr = IOleObject_QueryInterface(ole_object, &IID_IPersistStorage, (void **)&persist);
if (FAILED(hr)) goto end;
hr = WriteClassStg(stg, &clsid);
if (FAILED(hr)) goto end;
hr = IPersistStorage_InitNew(persist, stg);
if (FAILED(hr)) goto end;
hr = IOleCache2_Cache(ole_cache, fmt, ADVF_PRIMEFIRST, &connection);
if (FAILED(hr)) goto end;
hr = IOleCache2_SetData(ole_cache, fmt, &stgmedium, TRUE);
if (FAILED(hr)) goto end;
stgmedium.tymed = TYMED_NULL;
hr = IOleObject_GetUserType(ole_object, USERCLASSTYPE_FULL, &ole_typename);
if(FAILED(hr))
ole_typename = NULL;
hr = WriteFmtUserTypeStg(stg, fmt->cfFormat, ole_typename);
CoTaskMemFree(ole_typename);
if (FAILED(hr)) goto end;
hr = IPersistStorage_Save(persist, stg, TRUE);
if (FAILED(hr)) goto end;
hr = IPersistStorage_SaveCompleted(persist, NULL);
if (FAILED(hr)) goto end;
hr = IOleObject_QueryInterface(ole_object, iid, obj);
end:
if (stgmedium.tymed == TYMED_NULL)
ReleaseStgMedium(&stgmedium);
if (persist)
IPersistStorage_Release(persist);
if (ole_cache)
IOleCache2_Release(ole_cache);
if (ole_object)
IOleObject_Release(ole_object);
return hr;
}
/******************************************************************************
* OleCreateFromFileEx [OLE32.@]
*/
HRESULT WINAPI OleCreateFromFileEx(REFCLSID clsid, const OLECHAR *filename, REFIID iid, DWORD flags,
DWORD renderopt, ULONG num_fmts, DWORD *adv_flags, FORMATETC *fmts, IAdviseSink *sink,
DWORD *conns, IOleClientSite *client_site, IStorage *stg, void **obj)
{
HRESULT hr;
IMoniker *mon;
IDataObject *data;
IUnknown *unk = NULL;
IOleCache *cache = NULL;
ULONG i;
TRACE("cls %s, %s, iid %s, flags %ld, render opts %ld, num fmts %ld, adv flags %p, fmts %p\n", debugstr_guid(clsid),
debugstr_w(filename), debugstr_guid(iid), flags, renderopt, num_fmts, adv_flags, fmts);
TRACE("sink %p, conns %p, client site %p, storage %p, obj %p\n", sink, conns, client_site, stg, obj);
for (i = 0; i < num_fmts; i++)
TRACE("\t%ld: fmt %s adv flags %ld\n", i, debugstr_formatetc(fmts + i), adv_flags[i]);
hr = CreateFileMoniker( filename, &mon );
if (FAILED(hr)) return hr;
hr = BindMoniker( mon, 0, &IID_IDataObject, (void**)&data );
IMoniker_Release( mon );
if (FAILED(hr)) return hr;
hr = get_storage( data, stg, NULL, FALSE );
if (FAILED(hr)) goto end;
hr = OleLoad( stg, &IID_IUnknown, client_site, (void**)&unk );
if (FAILED(hr)) goto end;
if (renderopt == OLERENDER_FORMAT)
{
hr = IUnknown_QueryInterface( unk, &IID_IOleCache, (void**)&cache );
if (FAILED(hr)) goto end;
for (i = 0; i < num_fmts; i++)
{
STGMEDIUM med;
DWORD dummy_conn;
memset( &med, 0, sizeof(med) );
hr = IDataObject_GetData( data, fmts + i, &med );
if (FAILED(hr)) goto end;
hr = IOleCache_Cache( cache, fmts + i, adv_flags[i], &dummy_conn );
if (SUCCEEDED(hr))
hr = IOleCache_SetData( cache, fmts + i, &med, TRUE );
if (FAILED(hr))
{
ReleaseStgMedium( &med );
goto end;
}
}
}
hr = IUnknown_QueryInterface( unk, iid, obj );
end:
if (cache) IOleCache_Release( cache );
if (unk) IUnknown_Release( unk );
IDataObject_Release( data );
return hr;
}
/******************************************************************************
* OleCreateFromFile [OLE32.@]
*/
HRESULT WINAPI OleCreateFromFile(REFCLSID clsid, const OLECHAR *filename, REFIID iid, DWORD renderopt,
FORMATETC *fmt, IOleClientSite *client_site, IStorage *storage, void **obj)
{
DWORD advf = ADVF_PRIMEFIRST;
return OleCreateFromFileEx(clsid, filename, iid, 0, renderopt, fmt ? 1 : 0, fmt ? &advf : NULL, fmt,
NULL, NULL, client_site, storage, obj);
}
/******************************************************************************
* OleDuplicateData [OLE32.@]
*/
HANDLE WINAPI OleDuplicateData(HANDLE hSrc, CLIPFORMAT cfFormat, UINT uiFlags)
{
HANDLE hDst = NULL;
TRACE("(%p,%x,%x)\n", hSrc, cfFormat, uiFlags);
if (!uiFlags) uiFlags = GMEM_MOVEABLE;
switch (cfFormat)
{
case CF_ENHMETAFILE:
hDst = CopyEnhMetaFileW(hSrc, NULL);
break;
case CF_METAFILEPICT:
hDst = CopyMetaFileW(hSrc, NULL);
break;
case CF_PALETTE:
{
LOGPALETTE * logpalette;
UINT nEntries = GetPaletteEntries(hSrc, 0, 0, NULL);
if (!nEntries) return NULL;
logpalette = HeapAlloc(GetProcessHeap(), 0,
FIELD_OFFSET(LOGPALETTE, palPalEntry[nEntries]));
if (!logpalette) return NULL;
if (!GetPaletteEntries(hSrc, 0, nEntries, logpalette->palPalEntry))
{
HeapFree(GetProcessHeap(), 0, logpalette);
return NULL;
}
logpalette->palVersion = 0x300;
logpalette->palNumEntries = (WORD)nEntries;
hDst = CreatePalette(logpalette);
HeapFree(GetProcessHeap(), 0, logpalette);
break;
}
case CF_BITMAP:
{
LONG size;
BITMAP bm;
if (!GetObjectW(hSrc, sizeof(bm), &bm))
return NULL;
size = GetBitmapBits(hSrc, 0, NULL);
if (!size) return NULL;
bm.bmBits = HeapAlloc(GetProcessHeap(), 0, size);
if (!bm.bmBits) return NULL;
if (GetBitmapBits(hSrc, size, bm.bmBits))
hDst = CreateBitmapIndirect(&bm);
HeapFree(GetProcessHeap(), 0, bm.bmBits);
break;
}
default:
{
SIZE_T size = GlobalSize(hSrc);
LPVOID pvSrc = NULL;
LPVOID pvDst = NULL;
/* allocate space for object */
if (!size) return NULL;
hDst = GlobalAlloc(uiFlags, size);
if (!hDst) return NULL;
/* lock pointers */
pvSrc = GlobalLock(hSrc);
if (!pvSrc)
{
GlobalFree(hDst);
return NULL;
}
pvDst = GlobalLock(hDst);
if (!pvDst)
{
GlobalUnlock(hSrc);
GlobalFree(hDst);
return NULL;
}
/* copy data */
memcpy(pvDst, pvSrc, size);
/* cleanup */
GlobalUnlock(hDst);
GlobalUnlock(hSrc);
}
}
TRACE("returning %p\n", hDst);
return hDst;
}