windows-nt/Source/XPSP1/NT/com/ole32/ole232/drag/drag.cpp
2020-09-26 16:20:57 +08:00

2542 lines
69 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: drag.cpp
//
// Contents: Api's for doing drag'n'drop
//
// Classes: CPoint
// CDragOperation
// CDropTarget
//
// History: dd-mmm-yy Author Comment
// 08-Nov-94 alexgo converted to PrivDragDrop rpc
// for Drag Drop protocol
// 20-Oct-94 alexgo added Win3.1 style drag drop
// for Chicago/NT shell
// 30-Sep-94 ricksa Drag/Drop optimization.
// 18-Jul-94 ricksa made cursors work in shared WOW
// 21-Apr-94 ricksa made drag/drop handle WM_CANCELMODE
// 04-Apr-94 ricksa rewrote DoDragDrop loop
// 11-Jan-94 alexgo added VDATEHEAP to every function
// 29-Dec-93 alexgo converted to RPC alogirithm for
// getting IDropTarget, etc.
// 06-Dec-93 alexgo commented, formatted
// 93/94 Johann Posch (JohannP) created Drag/Drop for Ole 16 bit
//
// Notes:
//
// RPC Drag Drop algorithm:
//
// During a drag drop operation, the user is moving the mouse around
// the screen, passing over many windows. For each window the mouse
// is over, we need to determine if the window is a drop target.
// If it is, then we remote the IDropTarget interface to the DropSource
// so that the correct visual feedbacks can be given.
//
// To accomplish this, RegisterDragDrop adds two properties to the
// drop target window: a public property, EndPoint ID (provided to
// us by compobj), and a private property (available only to the calling
// process), the IDropTarget pointer.
//
// During the DoDragDrop loop, we ask compobj to test each window for
// the EndpointID property. If it is there, compobj (via
// GetInterfaceFromWindowProp), then we will rpc to the drop target
// process, get the IDropTarget pointer and marshal it back to the
// drop source process. We also install a custom message filter to
// ensure that messages (particularly mouse move messages) are handled
// correctly.
//
// RevokeDragDrop simply removes the above mentioned properties from
// the window handle.
//
// Because in Win32, you can always switch windows and mouse capture
// depends on having the mouse button down, drag/drop processing
// is changed slightly. Whenever, the user does an operation that
// would switch windows, the clipboard window that we use for capture
// will get a WM_CANCELMODE. It will notify the drag operation and
// the drag operation will proceed as if the user aborted the operation.
//
//
// Win 3.1 DragDrop algorithm:
//
// Win3.1 apps can register a window as a drop target via DragAcceptFiles.
// This API sets the WS_EX_ACCEPTFILES bit in the window style.
//
// In Win3.1, these apps would get a WM_DROPFILES message when
// files where dropped on them. An hglobal with the filenames is
// sent in the wparam of WM_DROPFILES.
//
// In Chicago and NT3.5, CF_HDROP is a new clipboard format that is
// identical to the data sent in WM_DROPFILES. If we see this format
// available in a data object passed to DoDragDrop, then we enter
// into our Win31 compatibility mode (which affects finding a drop
// target).
//
// When finding a drop target for a given window, we check to
// see if a window in the hierarchy is registered as a Win31 drop
// target. If so, then we create a wrapper drop target. This wrapper
// drop target will forward calls to the real drop target (if available).
//
// With Win3.1 drag drop, we can do a COPY. If the OLE target indicates
// that no OLE drop can be performed (by returning DROPEFFECT_NONE),
// then we substitute in DROPEFFECT_COPY.
//
// On Drop, if the OLE target chooses not to accept the drop, then
// we will post the window a WM_DROPFILES message with the hglobal
// obtained from IDataObject::GetData(CF_HDROP).
//
//--------------------------------------------------------------------------
#include <le2int.h>
// Note: Enable including native user APIs
// for stack switching
#include <userapis.h>
#pragma SEG(drag)
#include <getif.hxx>
#include <dragopt.h>
#include <resource.h>
#include "enumgen.h"
#include "clipbrd.h"
#include "drag.h"
NAME_SEG(Drag)
ASSERTDATA
ATOM g_aEndPointAtom;
// DROPFILES is the structure of data contained in the CF_HDROP format.
// However, this is private to the shell, so it is not declared in any
// header files.
typedef struct _DROPFILES {
DWORD pFiles; // offset of file list
POINTL pt; // drop point (client coords)
DWORD fNC; // is it on NonClient area
// and pt is in screen coords
DWORD fWide; // WIDE character switch
} DROPFILES, FAR * LPDROPFILES;
#define WM_NCMOUSEFIRST 0x00A0
#define WM_NCMOUSELAST 0x00A9
// From ido.cpp to create shared memory formats
HANDLE CreateSharedDragFormats(IDataObject *pIDataObject);
#define VK_ALT VK_MENU
static const struct {
int keyCode;
WPARAM keyFlag;
} vKeyMap [] = {
{ VK_LBUTTON, MK_LBUTTON },
{ VK_RBUTTON, MK_RBUTTON },
{ VK_MBUTTON, MK_MBUTTON },
{ VK_ALT , MK_ALT },
{ VK_SHIFT , MK_SHIFT },
{ VK_CONTROL, MK_CONTROL }
};
// This is the default cursor object for 32 bit apps. Only one such object
// is needed for 32 bit apps. 16 bit apps need one per shared WOW application
// that is running.
CDragDefaultCursors *cddcDefault32 = NULL;
extern ATOM g_aDropTarget;
extern ATOM g_aDropTargetMarshalHwnd;
//+-------------------------------------------------------------------------
//
// Member: DragDropProcessUninitialize
//
// Synopsis: Does any Unitialization necessary at OleUninitialize time.
// for the last Unitialize for the Process
//
// Returns: none
//
// Algorithm:
// History: dd-mmm-yy Author Comment
// 18-Jul-94 rogerg Created
//
// Note: We need a per thread default cursor object in WOW because
// of the clean up that WOW does. For 32 bit apps, we just use
// one for the entire process.
//
//--------------------------------------------------------------------------
void DragDropProcessUninitialize(void)
{
if (NULL != cddcDefault32)
{
delete cddcDefault32;
cddcDefault32 = NULL;
}
}
//+-------------------------------------------------------------------------
//
// Member: CDragDefaultCursors::GetDefaultCursorObject, static
//
// Synopsis: Get appropriate pointer to default cursor object
//
// Returns: NULL - error occurred
// ~NULL - pointer to appropriate default cursor table
//
// Algorithm: If we are in a 32 bit app, just get a pointer to the
// single cursor table. In 16 bit, get the per thread cursor
// table. If there is none, then allocate and initialize it.
//
// History: dd-mmm-yy Author Comment
// 18-Jul-94 Ricksa Created
//
// Note: We need a per thread default cursor object in WOW because
// of the clean up that WOW does. For 32 bit apps, we just use
// one for the entire process.
//
//--------------------------------------------------------------------------
CDragDefaultCursors *CDragDefaultCursors::GetDefaultCursorObject(void)
{
if (!IsWOWThread())
{
// If we aren't in WOW, we can use the single common default cursor
// object. We make sure that it is initialized before we use it.
if (NULL == cddcDefault32)
{
cddcDefault32 = new CDragDefaultCursors;
if (cddcDefault32)
{
if (!cddcDefault32->Init())
{
delete cddcDefault32;
cddcDefault32 = NULL;
}
}
}
return cddcDefault32;
}
COleTls tls;
// We are in WOW. Get the cursor object if it has already been allocated
CDragDefaultCursors *pccdc16 = (CDragDefaultCursors *) tls->pDragCursors;
if (pccdc16 == NULL)
{
// No cursor table so allocate it -- Please note that we take advantage
// of the fact that this object has only the default constructor by
// simply allocating it rather than "newing" it. The point is that
// we need to free the memory at thread release time and this happens
// in code that doesn't know about the the object.
pccdc16 = (CDragDefaultCursors *)
PrivMemAlloc(sizeof(CDragDefaultCursors));
if (pccdc16 != NULL)
{
// Successfully allocated so initialize it
if (!pccdc16->Init())
{
PrivMemFree(pccdc16);
return NULL;
}
tls->pDragCursors = pccdc16;
}
}
return pccdc16;
}
//+-------------------------------------------------------------------------
//
// Function: CDragDefaultCursors::Init
//
// Synopsis: Initialize object by loading all the default cursors.
//
// History: dd-mmm-yy Author Comment
// 19-Apr-94 Ricksa Created
//
// Note: We continue the Win16 practice of ignoring possible failure
// cases when loading the cursors although we do put in a
// debug verification that they all loaded.
//
//--------------------------------------------------------------------------
BOOL CDragDefaultCursors::Init(void)
{
// Make sure table is set to NULLs.
memset(&ahcursorDefaults[0][0], 0, sizeof(ahcursorDefaults));
// Load cursors for operation
if ( !(ahcursorDefaults[NO_SCROLL] [NO_DROP]
= LoadCursor (g_hmodOLE2, MAKEINTRESOURCE(CURNONE))) )
return FALSE;
if (!(ahcursorDefaults[NO_SCROLL] [MOVE_DROP] =
LoadCursor (g_hmodOLE2, MAKEINTRESOURCE(CURMOVE))) )
return FALSE;
if (!(ahcursorDefaults[NO_SCROLL] [COPY_DROP] =
LoadCursor (g_hmodOLE2, MAKEINTRESOURCE(CURCOPY))) )
return FALSE;
if (!(ahcursorDefaults[NO_SCROLL] [LINK_DROP] =
LoadCursor(g_hmodOLE2, MAKEINTRESOURCE(CURLINK))) )
return FALSE;
// Load cursors for operation
ahcursorDefaults[SCROLL] [NO_DROP] =
ahcursorDefaults[NO_SCROLL] [NO_DROP];
ahcursorDefaults[SCROLL] [MOVE_DROP] =
ahcursorDefaults[NO_SCROLL] [MOVE_DROP];
ahcursorDefaults[SCROLL] [COPY_DROP] =
ahcursorDefaults[NO_SCROLL] [COPY_DROP];
ahcursorDefaults[SCROLL] [LINK_DROP] =
ahcursorDefaults[NO_SCROLL] [LINK_DROP];
#if DBG == 1
// For debug, verify that cursors were loaded correctly
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 4; j++)
{
AssertSz((ahcursorDefaults[i] [j] != NULL),
"Drag/Drop cursor initialization failed!");
}
}
#endif // DBG == 1
return TRUE;
}
//+-------------------------------------------------------------------------
//
// Function: CDragDefaultCursors::SetCursor
//
// Synopsis: Set cursor to appropriate value
//
// Algorithm: We use the input effect to calculate the appropriate offset
// into the table for the cursor to use.
//
// History: dd-mmm-yy Author Comment
// 19-Apr-94 Ricksa Created
//
// Note: We use the table approach so we to make consistent behavior
// between scroll and non-scroll cursors.
//
//--------------------------------------------------------------------------
void CDragDefaultCursors::SetCursor(DWORD dwEffect)
{
// Get Scroll index
int iScroll = (dwEffect & DROPEFFECT_SCROLL) ? SCROLL : NO_SCROLL;
int iCursorType = NO_DROP;
if (dwEffect & DROPEFFECT_LINK)
{
iCursorType = LINK_DROP;
}
else if (dwEffect & DROPEFFECT_COPY)
{
iCursorType = COPY_DROP;
}
else if (dwEffect & DROPEFFECT_MOVE)
{
iCursorType = MOVE_DROP;
}
::SetCursor(ahcursorDefaults[iScroll] [iCursorType]);
}
//
// Drag/Drop Operation Statics
//
LONG CDragOperation::s_wScrollInt = -1;
//+-------------------------------------------------------------------------
//
// Function: GetControlKeysState
//
// Synopsis: queries the current status of the control keys
//
// Arguments: [fAll] -- if true, the just query the keys, not mouse
// buttons too
//
// Returns: the MK flags for each key pressed
//
// Algorithm: Get key state either for all keys and mouse buttons in
// the vKeyMap table or simply for the key portion of the table
// and translate it to the WPARAM form as returned in mouse
// messages.
//
// History: dd-mmm-yy Author Comment
// 06-Dec-93 alexgo 32bit port
//
//--------------------------------------------------------------------------
WORD GetControlKeysState(BOOL fAll)
{
WORD grfKeyState = 0;
int i = (fAll) ? 0 : 3;
for (; i < sizeof(vKeyMap) / sizeof(vKeyMap[0]); i++)
{
if (GetKeyState(vKeyMap[i].keyCode) < 0) // Key down
{
grfKeyState |= vKeyMap[i].keyFlag;
}
}
return grfKeyState;
}
//+-------------------------------------------------------------------------
//
// Function: GetControlKeysStateOfParam
//
// Synopsis: gets the key/button state of wparam (used with mouse messages)
//
// Arguments: [wParam] -- the wParam to parse apart
//
// Returns: the key's set in wParam
//
// Algorithm: First determine if keys we are interested in are set
// in the wParam message. Then go check the state of the
// ALT key and record that in the key state. We then return
// that to the caller.
//
// History: dd-mmm-yy Author Comment
// 06-Dec-93 alexgo 32bit port
//
//--------------------------------------------------------------------------
WORD GetControlKeysStateOfParam(WPARAM wParam)
{
// Check all the buttons we are interested in at once.
WORD grfKeyState = (WORD) wParam
& (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_SHIFT | MK_CONTROL);
// get the alt key
if (GetKeyState(VK_ALT) < 0) // Key down
{
grfKeyState |= MK_ALT;
}
return grfKeyState;
}
//+-------------------------------------------------------------------------
//
// Function: IsWin31DropTarget
//
// Synopsis: determines whether the given hwnd is a valid drop target
// for Win31 style drag drop
//
// Effects:
//
// Arguments: [hwnd] -- the window to check
//
// Requires:
//
// Returns: TRUE/
// FALSE
//
// Signals:
//
// Modifies:
//
// Algorithm: checks the WS_EX_ACCEPTFILES style bit. If this bit is
// set and the window is not disabled, then it is a valid
// Win3.1 drop target.
//
// History: dd-mmm-yy Author Comment
// 25-Jan-95 alexgo added check for WS_DISABLED
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL IsWin31DropTarget( HWND hwnd )
{
LONG exstyle;
exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
if( (exstyle & WS_EX_ACCEPTFILES) )
{
LONG style;
style = GetWindowLong(hwnd, GWL_STYLE);
if( !(style & WS_DISABLED) )
{
return TRUE;
}
}
return FALSE;
}
//+-------------------------------------------------------------------------
//
// Function: UseWin31DragDrop
//
// Synopsis: tests the given data object to see if enough data is offered
// to perform Win3.1 style drag drop
//
// Effects:
//
// Arguments: [pDataObject] -- pointer to the data object
//
// Requires: pdataobj must not be NULL
//
// Returns: TRUE/FALSE
//
// Signals:
//
// Modifies:
//
// Algorithm: does an IDataObject::QueryGetData for CF_HDROP
//
// History: dd-mmm-yy Author Comment
// 30-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL UseWin31DragDrop(IDataObject *pDataObject)
{
FORMATETC formatetc;
INIT_FORETC(formatetc);
formatetc.cfFormat = CF_HDROP;
formatetc.tymed = TYMED_HGLOBAL;
if( pDataObject->QueryGetData(&formatetc) == NOERROR )
{
return TRUE;
}
else
{
return FALSE;
}
}
//+-------------------------------------------------------------------------
//
// Function: IsNCDrop
//
// Synopsis: are we dropping into the non-client area of the window or
// on an iconic window?
//
// Effects: *DOES A SEND MESSAGE*!!!
//
// Arguments: [hwnd] -- the window to ask
// [pt] -- the point in screen coords
//
// Requires:
//
// Returns: TRUE/FALSE (TRUE if in non-client area)
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 25-Jan-95 alexgo borrowed from Win95 shell sources
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL IsNCDrop(HWND hwnd, POINT pt)
{
return (!IsIconic(hwnd) &&
HTCLIENT!=SendMessage(hwnd, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y)));
}
//+-------------------------------------------------------------------------
//
// Member: GetDropTarget
//
// Synopsis: Gets the IDropTarget * from the closest window in the
// hierachy up from the given window (if available, of
// course ;-)
//
// Arguments: [hwndCur] -- the window to the cursor is currently over
// [hwndDropTarget] -- the window that contains a valid DropTarget
//
// Returns: Result of drag enter operation at Target
//
// Algorithm: Loop calling PrivDragDrop until we get a drop target or
// we run out of windows that are parent to the window that
// the mouse is currently on.
//
// If a window in the hierarchy has registered itself for
// Win3.1 drag drop, then we create a drop target wrapper
// (CDropTarget) to handle the Win3.1 protocol. Note
// that a window hierarchy may be both OLE *and* Win3.1
// targets.
//
// History: dd-mmm-yy Author Comment
// 08-Nov-94 alexgo converted to use PrivDragDrop
// 20-Oct-94 alexgo added Win31 drop target support
// 30-Sep-94 ricksa Drag/Drop optimization.
// 21-Jul-94 alexgo removed GetDropTargetFromWindow
// optimization and put that functionality
// in GetInterfaceFromWindowProp (to
// help make clipboard faster).
// 06-Apr-94 Ricksa Modified to call GetDropTargetFromWindow
// to optimize local calls
// 11-Jan-94 alexgo changed name from GetTopStm to
// GetDropTarget, converted to the RPC-style
// drag drop, added a VDATEHEAP macro
// 06-Dec-93 alexgo commented
//
//--------------------------------------------------------------------------
HRESULT CDragOperation::GetDropTarget(HWND hwnd31,HWND hwndDropTarget)
{
IDropTarget *ptarget = NULL;
DDInfo hDDInfo = NULL;
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN GetDropTarget ( %x,%x)\n", this, hwnd31,hwndDropTarget));
_pDropTarget = NULL;
HRESULT hr = E_FAIL;
if (hwndDropTarget)
{
HWND hwndClipWindow;
Assert(GetProp(hwndDropTarget, (LPCWSTR)g_aDropTarget));
// If the DropTarget hasn't been marshaled, Marshal it now.
if (hwndClipWindow = (HWND) GetProp(hwndDropTarget,(LPCWSTR) g_aDropTargetMarshalHwnd))
{
SSSendMessage(hwndClipWindow,WM_OLE_CLIPBRD_MARSHALDROPTARGET,0,(LPARAM) hwndDropTarget);
}
hr = PrivDragDrop(hwndDropTarget,
DRAGOP_ENTER,
_DOBuffer,
_pDataObject,
_grfKeyState,
_cpt.GetPOINTL(),
_pdwEffect,
NULL,
&hDDInfo);
if (hr != NOERROR)
{
hwndDropTarget = NULL;
}
}
Assert( (NULL == hwnd31) || IsWin31DropTarget(hwnd31));
if( hwndDropTarget || hwnd31 )
{
ptarget = new CDropTarget(hwnd31, hwndDropTarget, *_pdwEffect, this, hDDInfo);
if( ptarget == NULL )
{
hr = E_OUTOFMEMORY;
}
else
{
hr = NOERROR;
}
// if we have a Win31 drop target AND the OLE drop target returned
// DROPEFFECT_NONE, then we should return DROPEFFECT_COPY
if( hr == NOERROR && *_pdwEffect == DROPEFFECT_NONE && hwnd31 )
{
*_pdwEffect = DROPEFFECT_COPY;
}
_pDropTarget = ptarget;
}
DDDebugOut((DEB_ITRACE, "%p OUT GetDropTarget ( %lx ) [ %p ]\n",
this, hr, _pDropTarget));
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::CDragOperation
//
// Synopsis: Initialize the object to start the operation
//
// Arguments: [pDataObject] - pointer to data object to drop
// [pDropSource] - pointer to source for drop operation
// [dwOKEffects] - effects allowed in drag operation
// [pdwEffect] - how operation affected source data
// [hr] - whether constructor succeeded
//
// Algorithm: Initialize data in object. Make sure that static data
// is initialized. Wait for first mouse message to begin.
//
// History: dd-mmm-yy Author Comment
// 20-Oct-94 alexgo added support for Win31 drag drop
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
CDragOperation::CDragOperation(
LPDATAOBJECT pDataObject,
LPDROPSOURCE pDropSource,
DWORD dwOKEffects,
DWORD FAR *pdwEffect,
HRESULT& hr)
:
_pDataObject(pDataObject),
_DOBuffer(NULL),
_pDropSource(pDropSource),
_pDropTarget(NULL),
_pRealDropTarget(NULL),
_hFormats(NULL),
_dwOKEffects(dwOKEffects),
_pdwEffect(pdwEffect),
_fEscapePressed(FALSE),
_curOld(GetCursor()),
_hwndLast((HWND) -1),
_grfKeyState(0),
_hrDragResult(S_OK),
_fReleasedCapture(FALSE),
_pcddcDefault(NULL),
_fUseWin31(FALSE)
{
VDATEHEAP();
// Set the default scroll interval
if (s_wScrollInt < 0)
{
InitScrollInt();
}
hr = GetMarshalledInterfaceBuffer(IID_IDataObject, pDataObject,
&_DOBuffer);
if( hr != NOERROR )
{
Assert(NULL == _DOBuffer);
return;
}
// Get appropriate default cursor table object
if ((_pcddcDefault = CDragDefaultCursors::GetDefaultCursorObject()) == NULL)
{
// Some error occurred while we were trying to initialize the
// so return an error. This should be highly unusual.
DDDebugOut((DEB_ERROR,
"CDragDefaultCursors::GetDefaultCursorObject Failed!\n"));
hr = E_FAIL;
return;
}
// We will use the clipboard window to capture the mouse but we
// must have a clipboard window so we make sure it is created
// if it is not already there.
hr = ClipSetCaptureForDrag(this);
if (FAILED(hr))
{
return;
}
_hFormats = CreateSharedDragFormats(pDataObject);
// it's OK for _hFormats to be NULL (indicates an empty or non-existant
// formatetc enumertor
// For following peek
MSG msg;
// Busy wait until a mouse or escape message is in the queue
while (!PeekMessage(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
{
// Note: all keyboard messages except escape are tossed. This is
// fairly reasonable since the user has to be holding the left
// mouse button down at this point. They can't really be doing
// too much data input one handed.
if ((PeekMessage(&msg, 0, WM_KEYDOWN, WM_KEYDOWN, PM_REMOVE)
|| PeekMessage(&msg, 0, WM_SYSKEYDOWN, WM_SYSKEYDOWN, PM_REMOVE))
&& msg.wParam == VK_ESCAPE)
{
_fEscapePressed = TRUE;
break;
}
}
// get mouse pos and key state
if (!_fEscapePressed)
{
_cpt.Set(msg.pt.x, msg.pt.y);
_grfKeyState = GetControlKeysStateOfParam(msg.wParam);
}
else
{
// We ask the cursor for its position since we didn't get a
// position from the mouse.
GetCursorPos(_cpt.GetAddressOfPOINT());
_grfKeyState = GetControlKeysState(TRUE);
}
// Check to see if we need to do Win3.1 style drag drop.
// If we do, then set a flag so we can construct a fake drop target as
// needed
if( UseWin31DragDrop(pDataObject) )
{
_fUseWin31 = TRUE;
}
}
//+-------------------------------------------------------------------------
//
// Function: ~CDragOperation
//
// Synopsis: Clean up object
//
// Algorithm: Release mouse capture. Restore ole cursor. Remove enum
// formats.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
CDragOperation::~CDragOperation(void)
{
VDATEHEAP();
AssertSz((_pDropTarget == NULL), "CDragOperation::~CDragOperation");
// Stop the mouse capture
ReleaseCapture();
// Restore the cursor if it got changed
SetCursor(_curOld);
// Close the handle to the shared memory
if (_hFormats)
{
CloseHandle(_hFormats);
_hFormats = NULL;
}
if( _DOBuffer )
{
ReleaseMarshalledInterfaceBuffer(_DOBuffer);
}
}
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::InitScrollInt
//
// Synopsis: Initialize the scroll interval
//
// Algorithm: Look in profile for defined interval. If none set, then
// default to zero.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
void CDragOperation::InitScrollInt(void)
{
DWORD dw;
OLECHAR szBuffer[20];
s_wScrollInt = DD_DEFSCROLLDELAY;
dw = sizeof(szBuffer);
if (ERROR_SUCCESS == RegQueryValueEx(HKEY_CURRENT_USER,
OLESTR("Control Panel\\Mouse\\DragScrollDelay"),
NULL,
NULL,
(LPBYTE)szBuffer,
&dw))
{
s_wScrollInt = wcstol(szBuffer, NULL, 0);
}
}
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::UpdateTarget
//
// Synopsis: Update the target window based on mouse location
//
// Returns: TRUE - continue drag operation
// FALSE - error or time to drop
//
// Algorithm: First, we query the source to see if it wants to continue
// with the drop. If so, we get current window for mouse. If
// it is different than the previous window check to see whether
// the targets are different. If they are different, then notify
// the current target that we are leaving and then notify the
// new target that we have arrived.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
// 10-Jul-94 AlexT Allow same IDropTarget on different HWNDs
//
//--------------------------------------------------------------------------
BOOL CDragOperation::UpdateTarget(void)
{
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::UpdateTarget ( )\n", this));
// Assume this operation will continue the drag drop
BOOL fResult = TRUE;
HRESULT hr;
LPDROPTARGET lpCurDropTarget = NULL,
lpOldDropTarget = NULL;
HWND hWndTemp = NULL;
HWND hwndCur = WindowFromPoint(_cpt.GetPOINT());
// Query continue can return telling us one of four things:
// (1) Keep going (S_OK), (2) Drop operation should occur
// (DRAGDROP_S_DROP), (3) Drop operation is canceled
// (DRAGDROP_S_CANCEL) or (4) An unexpected error has occurred.
HRESULT hrQuery = _pDropSource->QueryContinueDrag(_fEscapePressed,
_grfKeyState);
if (FAILED(hrQuery) || (hrQuery == ResultFromScode(DRAGDROP_S_CANCEL)))
{
// Unexpected error or the operation has been cancelled so give up.
_hrDragResult = hrQuery;
fResult = FALSE;
goto UpdateTarget_exit;
}
// walk up the window list to find the actual pointer values for the current
// and old IDropTarget interfaces
if (hwndCur != _hwndLast)
{
hWndTemp = _hwndLast;
BOOL fChangedWin31 = FALSE;
HWND hWndOldDrop = NULL;
HWND hWndNewDrop = NULL;
HWND hWndWin31Drop = NULL;
LPDROPTARGET lpRealDropTarget = NULL;
HANDLE hTemp = NULL;
DWORD dwCurrentProcessId = 0;
if (hWndTemp != (HWND)-1)
GetWindowThreadProcessId(hWndTemp, &dwCurrentProcessId);
DWORD dwTempProcessID = dwCurrentProcessId;
while (hWndTemp && !lpRealDropTarget && hWndTemp != (HWND)-1 && dwTempProcessID == dwCurrentProcessId)
{
if (lpRealDropTarget = (IDropTarget *)GetProp(hWndTemp, (LPCWSTR)g_aDropTarget))
{
hWndOldDrop = hWndTemp;
}
hWndTemp = GetParent(hWndTemp);
if (hWndTemp)
{
GetWindowThreadProcessId(hWndTemp, &dwTempProcessID);
}
}
hWndTemp = hwndCur;
if (hWndTemp != (HWND)-1)
GetWindowThreadProcessId(hWndTemp, &dwCurrentProcessId);
dwTempProcessID = dwCurrentProcessId;
while (hWndTemp && dwTempProcessID == dwCurrentProcessId)
{
// If we haven't found the DropTarget yet, check this window.
if (!lpCurDropTarget)
{
if (lpCurDropTarget = (IDropTarget *)GetProp(hWndTemp, (LPCWSTR)g_aDropTarget))
{
hWndNewDrop = hWndTemp;
}
}
// if the current window is a win31 drop target, update the win31 window
// handle in our DropTarget Class. NOTE: Beware, this code relies on the
// fact that we can party on the CDropTarget Class directly, knowing that
// the class is reconstructed below as a result of the GetDropTarget()
// when the real IDropTarget ptrs change.
if (!fChangedWin31 &&
IsWin31DropTarget(hWndTemp) &&
_fUseWin31)
{
fChangedWin31 = TRUE;
hWndWin31Drop = hWndTemp;
if (_pDropTarget)
{
((CDropTarget*)_pDropTarget)->_hwnd31 = hWndTemp;
}
}
// if have a droptarget, and handle Win31 break.
if (lpCurDropTarget && (!_fUseWin31 || fChangedWin31))
{
break;
}
hWndTemp = GetParent(hWndTemp);
if (hWndTemp)
{
GetWindowThreadProcessId(hWndTemp, &dwTempProcessID);
}
}
// only update the drop target if the target has actually changed.
// HACK ALERT: We must explicitly check _hwndLast for -1 because Excel does not
// use OLE drag drop internally. When the cursor is moved outside the Overlapped
// Excel window, DoDragDrop is called. At this point _pRealDropTarget == NULL
// and lpCurDropTarget == NULL, and the no-smoking cursor does not appear.
// the _pRealDropTarget==NULL relies on the fact that lpCurDropTarget==NULL. This
// is true because the first case would short-circuit the rest of the condition
// otherwise
if ( (lpCurDropTarget != _pRealDropTarget) ||
(_hwndLast == (HWND)-1) ||
(hWndNewDrop != hWndOldDrop) ||
(_pRealDropTarget == NULL))
{
DDDebugOut((DEB_ITRACE, "%p lpCurDropTarget != lpOldDropTarget\n", this));
// The window that we are working on has changed
_hwndLast = hwndCur;
_pRealDropTarget = lpCurDropTarget;
//Allow the owner of the window to take foreground if it tries to.
if (dwCurrentProcessId)
AllowSetForegroundWindow(dwCurrentProcessId);
// Assume that neither current or previous window are drop aware
BOOL fCurAndLastNotDropAware = TRUE;
if (_pDropTarget != NULL)
{
// There was a previous drop target
// Last window was drag/drop aware
fCurAndLastNotDropAware = FALSE;
// Tell the drop target we are leaving & release it
_pDropTarget->DragLeave();
_pDropTarget->Release();
_pDropTarget = NULL;
}
// Set up effects for query of target
*_pdwEffect = _dwOKEffects;
hr = GetDropTarget(hWndWin31Drop,hWndNewDrop);
if (_pDropTarget != NULL)
{
// This window is drop awarre
fCurAndLastNotDropAware = FALSE;
// Errors from this call are ignored. We interpret them
// as the drop being disallowed. Since we don't really
// use this information here but in the DragOver call
// we make shortly, we just use this call to notify
// the application that we are beginning a drag operation.
if (!HandleFeedBack(hr))
{
goto UpdateTarget_exit;
}
}
else
{
// Tell the source that nothing happened
// only use DROPEFFECT_NONE if there is no new drop target.
hr = _pDropSource->GiveFeedback(*_pdwEffect = DROPEFFECT_NONE);
if (hr != NOERROR)
{
if (DRAGDROP_S_USEDEFAULTCURSORS == GetScode(hr))
{
_pcddcDefault->SetCursorNone();
}
else
{
// Unexpected error -- we will give up drag/drop.
DDDebugOut((DEB_ERROR,
"CDragOperation::UpdateTarget 1st GiveFeedback FAILED %x\n",
hr));
_hrDragResult = hr;
fResult = FALSE;
goto UpdateTarget_exit;
}
}
}
if (fCurAndLastNotDropAware)
{
// Neither new or old window know about drag/drop so set
// cursor accordingly.
_pcddcDefault->SetCursorNone();
}
}
else
{
// The window that we are working on has changed
_hwndLast = hwndCur;
}
}
if (hrQuery != NOERROR)
{
// Query asked for a drop
fResult = FALSE;
_hrDragResult = hrQuery;
}
UpdateTarget_exit:
DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::UpdateTarget ( %lx )\n",
this, fResult));
return fResult;
}
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::HandleFeedBack
//
// Synopsis: Handle feedback and update of cursor
//
// Arguments: [hr] - hresult from previous operation on drop target.
//
// Returns: TRUE - continue drag operation
// FALSE - error
//
// Algorithm: If previous operation on the target failed, map this to a
// disallowed drop. Then ask the source for feedback. If it
// so requests, then update the cursor. If an unexpected
// error occurs, let caller know that loop should break.
//
// History: dd-mmm-yy Author Comment
// 19-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
BOOL CDragOperation::HandleFeedBack(HRESULT hr)
{
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::HandleFeedBack ( %x )\n",
this, hr));
BOOL fResult = TRUE;
if (hr != NOERROR)
{
// target not responding for some reason; treat
// as if drop not possible, but don't preserve
// the reason why.
*_pdwEffect = DROPEFFECT_NONE;
}
// If bogus return from drag over, then make sure results are appropriate.
// However, if we are in a WOW we need to do things a little differently
// to maintain complete compatability with Win 3.1. In 16-bit OLE 2.0,
// the *_pdwEffect value is not changed when displaying feedback (i.e.,
// the result of the & is not stored back into *_pdwEffect in Win 3.1...
// in straight NT we do). Not storing the results back into *_pdwEffect
// when InWow() is a hack specifically for Visio, and even more
// specifically, for dragging from Visio's palette of "items" to an
// Excel spreadsheet.
if (IsWOWThread())
{
hr = _pDropSource->GiveFeedback( *_pdwEffect & (_dwOKEffects | DROPEFFECT_SCROLL));
}
else
{
*_pdwEffect &= (_dwOKEffects | DROPEFFECT_SCROLL);
hr = _pDropSource->GiveFeedback(*_pdwEffect);
}
if(hr != NOERROR)
{
// Either we want to change the cursor or some unexpected
// error has occurred.
if (DRAGDROP_S_USEDEFAULTCURSORS == GetScode(hr))
{
_pcddcDefault->SetCursor(*_pdwEffect);
}
else
{
DDDebugOut((DEB_ERROR,
"CDragOperation::HandleFeedBack Feedback FAILED %x\n", hr));
fResult = FALSE;
_hrDragResult = hr;
}
}
DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::HandleFeedBack ( %lx )\n",
this, fResult));
return fResult;
}
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::DragOver
//
// Synopsis: Tell the target we are dragging over and process the result
//
// Returns: TRUE - continue drag operation
// FALSE - error or time to drop
//
// Algorithm: Call the target's drag over if there is one and then
// get the sources feedback to update the cursor accordingly.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
BOOL CDragOperation::DragOver(void)
{
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::DragOver ( )\n", this));
// Default the result of the function to continue the loop for
// drag and drop.
BOOL fResult = TRUE;
// Local holder for errors.
HRESULT hr;
if (_pDropTarget != NULL)
{
// Keep effect in a local variable to save indirections
// in this routine.
*_pdwEffect = _dwOKEffects;
hr = _pDropTarget->DragOver(_grfKeyState, _cpt.GetPOINTL(), _pdwEffect);
// Get feedback from source & update cursor if necessary
fResult = HandleFeedBack(hr);
}
DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::DragOver ( %lx )\n",
this, fResult));
return fResult;
}
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::HandleMessages
//
// Synopsis: Handle windows messages
//
// Returns: TRUE - continue drag operation
// FALSE - error or time to drop
//
// Algorithm: Check for any windows message. If the message is a mouse
// message then record the new position of the mouse. If it
// is a key message, the record whether escape has been pushed.
// If this is any other message, then dispatch it. Repeat this
// process until the scroll interval has been exceeded.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
BOOL CDragOperation::HandleMessages(void)
{
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::HandleMessages ( )\n",
this));
// Message buffer
MSG msg;
// Default result of function to continue
BOOL fResult = TRUE;
// Capture all messages (i.e. modal loop).
// Process all input messages, dispatch other messages
//
// Note:we must NOT loop here until a hardware message comes in
// scrolling will not work.
// * yielding is important since other apps need to run
// * look for mouse messages first since these are the most
// impotant
// Flag for whether we peeked a message
BOOL fMsg;
//
// Sundown - The SetTimer return value can be truncated.
// We are passing NULL as HWND and Win32 will returned
// a value not greater than 4GB...
// If a check is required we could consider a temporary
// UINT_PTR value and do an ASSERT on its value...
//
UINT uTimer = (UINT)SetTimer(NULL, 0, s_wScrollInt, NULL);
do
{
fMsg = FALSE;
// Note: the order of peek is important - further messages can show up
// in the last peek
// If we looked for mouse messages first, we might never pick up
// WM_QUIT or keyboard messages (because by the time we finished
// processing the mouse message another might be on the queue).
// So, we check for WM_QUIT and keyboard messages first.
if (PeekMessage(&msg, 0, WM_QUIT, WM_QUIT, PM_REMOVE | PM_NOYIELD) ||
PeekMessage(&msg, 0, WM_KEYFIRST, WM_KEYLAST,
PM_REMOVE | PM_NOYIELD) ||
PeekMessage(&msg, 0, WM_SYSKEYDOWN, WM_SYSKEYUP,
PM_REMOVE | PM_NOYIELD) ||
PeekMessage(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) ||
PeekMessage(&msg, 0, WM_NCMOUSEFIRST, WM_NCMOUSELAST,
PM_REMOVE | PM_NOYIELD) ||
PeekMessage(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
{
fMsg = TRUE;
if (msg.message == WM_QUIT)
{
// Quit message so we are done.
PostQuitMessage((int) msg.wParam);
// We are going exiting so the error doesn't matter too much
_hrDragResult = ResultFromScode(E_UNSPEC);
// Make sure we break out of the loop
fResult = FALSE;
}
else if ((msg.message >= WM_KEYFIRST &&
msg.message <= WM_KEYLAST) ||
(msg.message >= WM_SYSKEYDOWN &&
msg.message <= WM_SYSKEYUP))
{
// Pull all keyboard messages from the queue - this keeps
// the keyboard state in sync with the user's actions
// We use a do/while so that we process the message we've
// already peeked.
do
{
// We only really pay attention to the escape key and dump
// any other key board messages.
if ((msg.message == WM_KEYDOWN
|| msg.message == WM_SYSKEYDOWN)
&& msg.wParam == VK_ESCAPE)
{
// Esc pressed: Cancel
_fEscapePressed = TRUE;
}
}
while (PeekMessage(&msg, 0, WM_KEYFIRST, WM_KEYLAST,
PM_REMOVE | PM_NOYIELD) ||
PeekMessage(&msg, 0, WM_SYSKEYDOWN, WM_SYSKEYUP,
PM_REMOVE | PM_NOYIELD));
DWORD grfKeyState; // temp variable for key state
// get the key state don't change the button states!!
grfKeyState = GetControlKeysState(FALSE) |
(_grfKeyState &
(MK_LBUTTON | MK_RBUTTON | MK_MBUTTON));
// if the keyboard state is unchanged, then don't exit
// this loop (as that will result in DragOver being called).
// If we call DragOver for each keyboard message, then
// performance is unacceptably slow.
if ((grfKeyState == _grfKeyState) && !_fEscapePressed)
{
fMsg = FALSE;
}
else
{
DDDebugOut((DEB_ITRACE, "Updating key state\n"));
_grfKeyState = grfKeyState;
}
}
else if (msg.message >= WM_MOUSEFIRST &&
msg.message <= WM_MOUSELAST)
{
// we may not have the focus (e.g. if we are the Chicago
// shell). Therefore, we won't ever get any WM_KEYDOWN
// messages. Double check the esc key status here
if( GetKeyState(VK_ESCAPE) < 0 )
{
_fEscapePressed = TRUE;
}
// We got a mouse move message - we skip all the mouse messages
// till we get to the last one. The point here is that
// because of the length of DragOver calls, we can get behind
// in processing messages which causes odd things to happen
// on the screen.
if (WM_MOUSEMOVE == msg.message)
{
MSG msg2;
// Keep processing mouse move messages till there
// aren't any more.
// if PeekMessage returns true update the original msg.
while(PeekMessage(&msg2, 0, WM_MOUSEMOVE, WM_MOUSEMOVE,
PM_REMOVE))
{
msg = msg2;
}
}
// Record position of the mouse
_cpt.Set(msg.pt.x, msg.pt.y);
// set mouse button state here
_grfKeyState = GetControlKeysStateOfParam(msg.wParam);
}
else if (msg.message >= WM_NCMOUSEFIRST &&
msg.message <= WM_NCMOUSELAST)
{
// Nothing we need to do for these NC mouse actions
NULL;
}
else if ( (msg.message == WM_TIMER) && (msg.wParam == uTimer) )
{
// Our timer was triggered. We need to recheck the keyboard
// state just in case it has changed. This is important for
// the Chicago shell--if it doesn't have focus, then we won't
// get any WM_KEYDOWN message (just mouse moves).
_grfKeyState = GetControlKeysState(FALSE) | (_grfKeyState &
(MK_LBUTTON | MK_RBUTTON | MK_MBUTTON));
if( GetKeyState(VK_ESCAPE) < 0 )
{
_fEscapePressed = TRUE;
}
// go ahead and fall out of the loop so we call DragOver
// (our timeout expired).
}
else
{
// Dispatch all other messages
DispatchMessage(&msg);
fMsg = FALSE;
}
}
else
{
WaitMessage();
}
// we have to leave the loop periodicially since apps
// might rely on on it the DragOver is called freqeuntly.
} while (!fMsg);
// Get rid of the timer we created for the loop
KillTimer(NULL, uTimer);
DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::HandleMessages ( %lx )\n",
this, fResult));
return fResult;
}
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::CompleteDrop
//
// Synopsis: Complete the drag/drop operation
//
// Returns: Result of operation
//
// Algorithm: If there is a target and we have decided to drop, then
// drop. Otherwise, release the target and return whatever
// the other result of the operation was.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
HRESULT CDragOperation::CompleteDrop(void)
{
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::CompleteDrop ( )\n",
this));
// Stop the mouse capture in case a dialog box is thrown up.
ReleaseCapture();
if (_pDropTarget != NULL)
{
// Caller is Drag/Drop aware
// and indicated it might accept drop
// The drop source replies DRAG_S_DROP if the user has
// released the left mouse button. However, we may be over
// a drop target which has refused a drop (via the feedback
// DROPEFFECT_NONE). Thus, both the drop source and drop
// target need to agree before we commit the drop.
if ((DRAGDROP_S_DROP == GetScode(_hrDragResult))
&& (*_pdwEffect != DROPEFFECT_NONE))
{
// We are going to try to drop
*_pdwEffect = _dwOKEffects;
HRESULT hr = _pDropTarget->Drop(_pDataObject, _grfKeyState,
_cpt.GetPOINTL(), _pdwEffect);
if (FAILED(hr))
{
// If drop actually failed in the last stage, let the
// caller know that this happened.
_hrDragResult = hr;
}
}
else
{
*_pdwEffect = DROPEFFECT_NONE;
_pDropTarget->DragLeave();
}
_pDropTarget->Release();
_pDropTarget = NULL;
}
else
{
*_pdwEffect = DROPEFFECT_NONE;
}
DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::CompleteDrop ( %lx )\n",
this, _hrDragResult));
return _hrDragResult;
}
//+-------------------------------------------------------------------------
//
// Function: RegisterDragDrop
//
// Synopsis: Registers a drop target
//
// Arguments: [hwnd] -- a handle to the drop target window
// [pDropTarget] -- the IDropTarget interface for the window
//
// Returns: HRESULT
//
// Algorithm: We ask compobj (via AssignEndpoinProperty) to put an
// endpoint ID publicly available on the window handle. Then
// we put the IDropTarget pointer on the window as a private
// property (see the notes at the beginning of this file).
//
// History: dd-mmm-yy Author Comment
// 06-Apr-94 ricksa Added tracing
// 16-Jan-94 alexgo pDropTarget is now AddRef'ed
// 11-Jan-94 alexgo added VDATEHEAP, converted to RPC-style
// drag drop.
// 06-Dec-93 alexgo commented
//
// Notes: By AddRef'ing the pDropTarget pointer, we are changing
// the semantics of the 16bit code (which did not do an
// AddRef).
//
//--------------------------------------------------------------------------
#pragma SEG(RegisterDragDrop)
STDAPI RegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget)
{
HRESULT hresult = NOERROR;
BOOL fDelayDrop = FALSE;
VDATEHEAP();
OLETRACEIN((API_RegisterDragDrop, PARAMFMT("hwnd= %h, pDropTarget= %p"),
hwnd, pDropTarget));
DDDebugOut((DEB_ITRACE, "%p _IN RegisterDragDrop ( %lx %p )\n",
NULL, hwnd, pDropTarget));
if (!IsValidInterface(pDropTarget))
{
hresult = E_INVALIDARG;
}
else if (!IsWindow(hwnd))
{
hresult = DRAGDROP_E_INVALIDHWND;
}
else
{
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IDropTarget,(IUnknown **)&pDropTarget);
if (GetProp(hwnd, (LPCWSTR)g_aDropTarget))
{
hresult = DRAGDROP_E_ALREADYREGISTERED;
}
else if (!SetProp(hwnd, (LPCWSTR)g_aDropTarget, (HANDLE)pDropTarget))
{
hresult = E_OUTOFMEMORY;
}
else
{
DWORD dwAssignAptID;
Win4Assert(NOERROR == hresult);
// HACK: We need to add this atom every time RegisterDragDrop
// is called because 16-bit Word does not call RevokeDragDrop
// and user will automatically clean-up this atom if Word is the
// first app run, and then exited before another app calls
// RegisterDragDrop.
g_aEndPointAtom = GlobalAddAtom(ENDPOINT_PROP_NAME);
// See if Delayed Drop can be set up.
fDelayDrop = FALSE;
if (g_aDropTargetMarshalHwnd && IsApartmentInitialized())
{
HWND hwndClipboard = GetPrivateClipboardWindow(CLIP_CREATEIFNOTTHERE);
if (hwndClipboard)
{
fDelayDrop = SetProp(hwnd,(LPCWSTR) g_aDropTargetMarshalHwnd,hwndClipboard);
}
}
// if can't delay marshal then marshal immediately.
if (!fDelayDrop)
{
hresult = AssignEndpointProperty(hwnd);
}
if (NOERROR == hresult)
{
pDropTarget->AddRef();
}
else
{
// We don't free h. It's not a handle at all.
HANDLE h = RemoveProp(hwnd, (LPCWSTR)g_aDropTarget);
}
}
}
DDDebugOut((DEB_ITRACE, "%p OUT RegisterDragDrop ( %lx )\n",
NULL, hresult));
OLETRACEOUT((API_RegisterDragDrop, hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Function: RevokeDragDrop
//
// Synopsis: Unregisters a window as a drop target
//
// Arguments: [hwnd] -- the window to unregister
//
// Returns: HRESULT
//
// Algorithm: Removes the two window properties set by
// RegisterDragDrop
//
// History: dd-mmm-yy Author Comment
// 06-Apr-94 ricksa added tracing
// 16-Jan-94 alexgo added a Release to the drag drop
// pointer to match the AddRef in
// RegisterDragDrop.
// 11-Jan-94 alexgo converted to RPC-style drag drop,
// added VDATEHEAP macro
// 06-Dec-93 alexgo commented
//
// Notes: the DropTarget->Release call changes the semantics of
// this function from the 16bit version (see Notes: for
// RegisterDragDrop).
//
//--------------------------------------------------------------------------
#pragma SEG(RevokeDragDrop)
STDAPI RevokeDragDrop(HWND hwnd)
{
HRESULT hr = NOERROR;
LPDROPTARGET pDropTarget;
BOOL fReleaseDropTarget = TRUE;
VDATEHEAP();
OLETRACEIN((API_RevokeDragDrop, PARAMFMT("hwnd= %h"), hwnd));
DDDebugOut((DEB_ITRACE, "%p _IN RevokeDragDrop ( %lx )\n", NULL, hwnd));
if (!IsWindow(hwnd))
{
hr = DRAGDROP_E_INVALIDHWND;
}
else if ((pDropTarget = (LPDROPTARGET)RemoveProp(hwnd, (LPCWSTR)g_aDropTarget)) == NULL)
{
hr = DRAGDROP_E_NOTREGISTERED;
}
else
{
fReleaseDropTarget = TRUE;
if (GetProp(hwnd, (LPCWSTR) g_aEndPointAtom)) // see if there is an endpoint.
{
DWORD dwAssignAptID;
// Ask compobj to remove the endpoint ID it placed on the window.
if(SUCCEEDED(UnAssignEndpointProperty(hwnd,&dwAssignAptID)))
{
// Note: AptID == ThreadID in Apartment model.
if( (dwAssignAptID != GetCurrentThreadId()) && (IsApartmentInitialized()) )
{
fReleaseDropTarget = FALSE;
}
}
Win4Assert(NULL == GetProp(hwnd,(LPCWSTR) g_aDropTargetMarshalHwnd));
}
else
{
HWND hwndClipbrd;
hwndClipbrd = (HWND) RemoveProp(hwnd,(LPCWSTR) g_aDropTargetMarshalHwnd);
Win4Assert(hwndClipbrd);
fReleaseDropTarget = (IsApartmentInitialized() && (hwndClipbrd != GetPrivateClipboardWindow(CLIP_QUERY )) )
? FALSE : TRUE;
}
// Release our reference to the object since we are no longer using it.
// NOTE: AddRef came from RegisterDragDrop
// Warning: Only call Release if we are in the same thread that Registered the DropTarget
// Or we are FreeThreading.
// This mirrors the atom added in RegisterDragDrop
GlobalDeleteAtom(g_aEndPointAtom);
if (fReleaseDropTarget)
{
pDropTarget->Release();
hr = NOERROR; // Always return NOERROR even if UnAssignEndPoint Failed
}
else
{
LEDebugOut((DEB_WARN, "WARNING:Revoke Called on Different Thread than Register!!\n"));
hr = RPC_E_WRONG_THREAD;
}
}
DDDebugOut((DEB_ITRACE, "%p OUT RegisterDragDrop ( %lx )\n", NULL, hr));
OLETRACEOUT((API_RevokeDragDrop, hr));
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: DoDragDrop
//
// Synopsis: The main drag'n'drop loop
//
// Effects:
//
// Arguments: [pDataObject] -- the object to drag
// [pDropSource] -- the drop source
// [dwOKEffects] -- effects flags (stuff to draw)
// [pdwEffect] -- what actually happened in
// the drag drop attempt
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm: See the notes at the beginning of the file
//
// History: dd-mmm-yy Author Comment
// 25-Nov-96 gopalk Fail the call if OleInitialize has not
// been called
// 05-Dec-94 JohannP added stack switching for WIN95
// 11-Jan-94 alexgo added VDATEHEAP macro, converted to
// the RPC-style drag drop.
// 31-Dec-93 erikgav chicago port
// 06-Dec-93 alexgo formatted
//
// Notes: Under Win95 SSAPI(DoDragDrop) gets expanded to SSDoDragDrop.
// This function is called by DoDragDrop (in stkswtch.cxx)
// which switches to the 16 bit stack first.
// IMPORTANT: this function has to be executed on the 16 bit
// since call back via USER might occur.
//--------------------------------------------------------------------------
#pragma SEG(DoDragDrop)
STDAPI SSAPI(DoDragDrop)(LPDATAOBJECT pDataObject, LPDROPSOURCE pDropSource,
DWORD dwOKEffects, DWORD *pdwEffect)
{
OLETRACEIN((API_DoDragDrop,
PARAMFMT("pDataObject=%p, pDropSource=%p, dwOKEffects=%x, pdwEffect=%p"),
pDataObject, pDropSource, dwOKEffects, pdwEffect));
DDDebugOut((DEB_ITRACE, "%p _IN DoDragDrop (%p %p %lx %p )\n",
NULL, pDataObject, pDropSource, dwOKEffects, pdwEffect));
HRESULT hr = NOERROR;
#ifndef _MAC
// Validation checks
VDATEHEAP();
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IDataObject,(IUnknown **)&pDataObject);
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IDropSource,(IUnknown **)&pDropSource);
if(!IsValidPtrOut(pdwEffect, sizeof(DWORD)) ||
!IsValidInterface(pDropSource) ||
!IsValidInterface(pDataObject))
hr = E_INVALIDARG;
// Check if the thread has called oleinitialize
if(!IsOleInitialized())
hr = CO_E_NOTINITIALIZED;
if(hr == NOERROR) {
// Create the object that does all the work.
CDragOperation drgop(pDataObject, pDropSource, dwOKEffects, pdwEffect, hr);
// Did the constructor succeeded?
if(SUCCEEDED(hr)) {
// Loop till worker object tells us to stop
for(;;) {
// Update target based on new window position
if(!drgop.UpdateTarget()) {
// Error so we are done
break;
}
// Notify
if(!drgop.DragOver()) {
break;
}
// Handle any messages we get in the mean time
if(!drgop.HandleMessages()) {
break;
}
} // end for loop
hr = drgop.CompleteDrop();
}
}
#endif // !_MAC
DDDebugOut((DEB_ITRACE, "%p OUT DoDragDrop ( %lx )\n", NULL, hr));
OLETRACEOUT((API_DoDragDrop, hr));
return hr;
}
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::CDropTarget
//
// Synopsis: constructor for the CDropTarget class
//
// Effects:
//
// Arguments: [hwnd31] -- the hwnd of the Win3.1 drop target
// may be NULL
// [hwndOLE] -- the hwnd of the OLE drop target
// [dwEffectLast] -- the last effect given the the current
// drop target that we are to emulate
// [pdo] -- a pointer to the main drag drop class
// [hDDInfo] -- handle to cached drag drag info
//
//
// Requires: hwnd31 *must* be a handle to a valid Win3.1 drop source
//
// Returns: void
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm: initializes variables
//
// History: dd-mmm-yy Author Comment
// 20-Oct-94 alexgo author
// 08-Jan-95
//
// Notes: there are two ways of determining if a given hwnd is
// a valid Win3.1 drop target:
// 1. send a WM_QUERYDROPOBJECT message for a TRUE/FALSE
// reply
// 2. check the extended style bits for WS_EX_ACCEPTFILES
//
// if ptarget is non-NULL, then the specific window to which
// it belongs is *not* guaranteed to be the same window as
// hwndtarget. hwndtarget is the window that is registered as
// a Win3.1 target. All that is guaranteed is that the ole
// target and hwndtarget are in the same window hierarchy.
//
//--------------------------------------------------------------------------
CDropTarget::CDropTarget( HWND hwnd31, HWND hwndOLE, DWORD dwEffectLast,
CDragOperation *pdo, DDInfo hDDInfo )
{
_crefs = 1;
_hwndOLE = hwndOLE;
_hwnd31 = hwnd31;
_hDDInfo = hDDInfo;
_dwEffectLast = dwEffectLast;
_pdo = pdo; // pointer to the current drag operation class
#if DBG ==1
// now do some checking (see Notes above)
if( hwnd31 )
{
LONG exstyle;
exstyle = GetWindowLong(hwnd31, GWL_EXSTYLE);
// strictly speaking, an app could process the WM_QUERYDROPOBJECT
// message itself (and thus, not set the extended style bits).
// However, this should be considered an application bug; the
// documentation states that apps should call DragAcceptFiles,
// which will set the WS_EX_ACCEPTFILES bit
Assert( (exstyle & WS_EX_ACCEPTFILES) );
}
#endif // DBG ==1
}
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::~CDropTarget
//
// Synopsis: frees the cached drag drop info handle
//
// Effects:
//
// Arguments: void
//
// Requires:
//
// Returns: void
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 08-Jan-95 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
CDropTarget::~CDropTarget()
{
if( _hDDInfo )
{
FreeDragDropInfo(_hDDInfo);
}
}
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::QueryInterface
//
// Synopsis: returns available interfaces on this object
//
// Effects:
//
// Arguments: [riid] -- the requested interface
// [ppv] -- where to put the interface
//
// Requires:
//
// Returns: E_UNEXPECTED
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm: CDropTarget is only used internally by OLE's drag drop code.
// It should never do a QI.
//
// History: dd-mmm-yy Author Comment
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CDropTarget::QueryInterface( REFIID riid, LPVOID * ppv )
{
(void)riid; // unused;
(void)ppv; // unused;
AssertSz(0, "Unexpected QI to CDropTarget");
return E_UNEXPECTED;
}
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::AddRef
//
// Synopsis: increments the reference count
//
// Effects:
//
// Arguments: void
//
// Requires:
//
// Returns: ULONG, the new reference count
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CDropTarget::AddRef( void )
{
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::AddRef ( )\n", this));
_crefs++;
DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::AddRef ( %ld )\n", this,
_crefs));
return _crefs;
}
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::Release
//
// Synopsis: decrements the reference count
//
// Effects: may delete 'this' object
//
// Arguments: void
//
// Requires:
//
// Returns: ULONG, the new reference count
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CDropTarget::Release( void )
{
ULONG crefs;
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::Release ( )\n", this));
crefs = --_crefs;
if( crefs == 0)
{
DDDebugOut((DEB_ITRACE, "DELETING CDropTarget %p\n", this));
delete this;
}
DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::Release ( %ld )\n",
this, crefs));
return crefs;
}
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::DragEnter
//
// Synopsis: sets the window up for drag drop
//
// Effects:
//
// Arguments: [pDataObject] -- the data object to drop
// [grfKeyState] -- the current keyboard state
// [pt] -- the cursor point
// [pdwEffect] -- where to return the drag drop effect
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm: should never be called. DragEnter is always called
// via GetDropTarget
//
// History: dd-mmm-yy Author Comment
// 08-Nov-93 alexgo eliminated
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CDropTarget::DragEnter( IDataObject * pDataObject,
DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect )
{
AssertSz(0, "DragEnter unexpectedly called!");
return E_UNEXPECTED;
}
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::DragOver
//
// Synopsis: called while the mouse is over a given window
//
// Effects:
//
// Arguments: [grfKeyState] -- the state of the keyboard
// [ptl] -- the position of the cursor
// [pdwEffect] -- the drag drop effect
//
// Requires:
//
// Returns: NOERROR
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm: If an OLE target is available, then we forward the call.
// If the target says DROPEFFECT_NONE, then we go ahead
// and return DROPEFFECT_COPY if a Win31 target window is
// available.
//
// If there is no OLE target and we have a Win3.1 target,
// then we go ahead and return DROPEFFECT_COPY.
//
// History: dd-mmm-yy Author Comment
// 08-Nov-94 alexgo converted to PrivDragDrop protocol
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CDropTarget::DragOver( DWORD grfKeyState, POINTL ptl,
DWORD *pdwEffect)
{
HRESULT hresult = NOERROR;
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::DragOver ( %lx , %lx "
", %lx )\n", this, grfKeyState, &ptl, *pdwEffect));
if( _hwndOLE )
{
hresult = PrivDragDrop(_hwndOLE, DRAGOP_OVER, NULL, NULL, grfKeyState,
ptl, pdwEffect, NULL, &_hDDInfo);
_dwEffectLast = *pdwEffect;
if( _hwnd31 )
{
// we only want to stomp on the effect if the DragOver call
// succeeded. If the call failed, then just assume that a
// Win3.1 drop would fail as well.
if( hresult == NOERROR && *pdwEffect == DROPEFFECT_NONE )
{
*pdwEffect = DROPEFFECT_COPY;
}
}
}
else if ( _hwnd31 )
{
*pdwEffect = DROPEFFECT_COPY;
}
DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::DragOver ( %lx ) [ "
"%lx ]\n", this, hresult, *pdwEffect));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::DragLeave
//
// Synopsis: called when the cursor leaves the current target window
//
// Effects:
//
// Arguments: void
//
// Requires:
//
// Returns: NOERROR
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm: Forwards the DragLeave call to the OLE-drop target
// (if it exists).
//
// History: dd-mmm-yy Author Comment
// 08-Nov-94 alexgo converted to PrivDragDrop protocol
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CDropTarget::DragLeave()
{
HRESULT hresult = NOERROR;
static POINTL ptl = {0, 0};
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::DragLeave ( )\n",
this));
if( _hwndOLE )
{
hresult = PrivDragDrop(_hwndOLE, DRAGOP_LEAVE, NULL, NULL, NULL,
ptl, NULL, NULL, &_hDDInfo);
}
DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::DragLeave ( %lx )\n",
this, hresult));
return hresult;
}
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::Drop
//
// Synopsis: called if the user lets go of the mouse button while
// over a drop target
//
// Effects:
//
// Arguments: [pDataObject] -- the data object to use
// [grfKeyState] -- the keyboard state
// [ptl] -- the current mouse position
// [pdwEffect] -- where to return cursor effect feedback
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm: If there is an OLE-target available, then we first forward
// the drop request to it. If the call fails
// (or DROPEFFECT_NONE is returned), then we try the Win31
// drop by posting a WM_DROPFILES message to the Win31 target
// window (ifit exists).
//
// History: dd-mmm-yy Author Comment
// 08-Nov-94 alexgo converted to PrivDragDrop protocol
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CDropTarget::Drop( IDataObject *pDataObject,
DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect )
{
STGMEDIUM medium;
FORMATETC formatetc;
HRESULT hresult = E_FAIL;
IFBuffer DOBuffer = _pdo->GetDOBuffer();
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::Drop ( %p , %lx , ",
"%p , %lx )\n", this, pDataObject, grfKeyState, &ptl, *pdwEffect));
// we don't forward Drop calls to the target if the last effect
// is DROPEFFECT_NONE. It is important that we check for this because
// to DoDragDrop 'normally' would not call Drop if the last effect
// was DROPEFFECT_NONE. However, this target wrapper will stomp
// pdwEffect and return DROPEFFECT_COPY instead of DROPEFFECT_NONE.
if( _hwndOLE && _dwEffectLast != DROPEFFECT_NONE )
{
hresult = PrivDragDrop(_hwndOLE, DRAGOP_DROP, DOBuffer, pDataObject,
grfKeyState, ptl, pdwEffect,
GetPrivateClipboardWindow(CLIP_QUERY), &_hDDInfo);
}
else if( _hwndOLE )
{
// if the 'real' drop effect is NONE, then we need to call
// DragLeave here before going on to post the WM_DROPFILES
// message. Otherwise, the app that is both an OLE and Win31
// and has been returning DROPEFFECT_NONE will never get a
// Drop or DragLeave call (which is necessary to terminate
// the OLE2 drag protocol). Capone in particular is sensitive
// to this.
*pdwEffect = DROPEFFECT_NONE;
hresult = DragLeave();
}
if( (hresult != NOERROR || *pdwEffect == DROPEFFECT_NONE) &&
(hresult != S_FALSE) &&
(_hwnd31) )
{
medium.tymed = TYMED_NULL;
INIT_FORETC(formatetc);
formatetc.cfFormat = CF_HDROP;
formatetc.tymed = TYMED_HGLOBAL;
hresult = pDataObject->GetData(&formatetc, &medium);
if( hresult == NOERROR )
{
// we need to fixup the mouse point coordinates in the CF_HDROP
// data. The point point should be in client coordinates
// (whereas IDropTarget::Drop takes screen coordinates)
DROPFILES *pdf = (DROPFILES *)GlobalLock(medium.hGlobal);
POINT pt;
pt.x = ptl.x;
pt.y = ptl.y;
if( pdf )
{
// we also need to set the non-client (NC) flag of the
// dropfile data. This lets the app do different behaviour
// depending on whether the drop point is in the client or
// non-client area (Word6, for example, opens the file if on
// non-client area, otherwise makes a package object).
pdf->fNC = IsNCDrop(_hwnd31, pt);
if( ScreenToClient(_hwnd31, &pt) )
{
pdf->pt.x = pt.x;
pdf->pt.y = pt.y;
}
else
{
LEDebugOut((DEB_WARN, "WARNING: CF_HDROP pt coords"
"not updated!!\n"));
; // don't do anything
}
GlobalUnlock(medium.hGlobal);
}
else
{
LEDebugOut((DEB_WARN, "WARNING: OUT OF MEMORY!\n"));
; // don't do anything
}
if( PostMessage(_hwnd31, WM_DROPFILES,
(WPARAM)medium.hGlobal, 0) )
{
*pdwEffect = DROPEFFECT_COPY;
}
else
{
// PostMessage failed, so free the data
ReleaseStgMedium(&medium);
*pdwEffect = DROPEFFECT_NONE;
}
}
}
DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::Drop ( %lx ) [ %lx ]\n",
this, hresult, *pdwEffect));
return hresult;
}