mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1566 lines
48 KiB
1566 lines
48 KiB
#include "shellprv.h"
|
|
#ifndef WINNT
|
|
#include <..\..\..\core\inc\krnlcmn.h> // GetProcessDword
|
|
#endif
|
|
#pragma hdrstop
|
|
|
|
BOOL IsWin16Process(HWND hwnd);
|
|
|
|
typedef struct {
|
|
HWND hwndDrop;
|
|
POINT ptDrop;
|
|
} DRAGDATA, *LPDRAGDATA;
|
|
|
|
//
|
|
// I put all the win3.1 droptarget support code within #ifdef blocks
|
|
// so that we can easily tell what kind of code should be migrated into
|
|
// OLE's drag loop.
|
|
//
|
|
#define WIN31_DROPTARGET
|
|
|
|
#define PXM_DRAGDROP WM_USER
|
|
#define PXM_FOUNDOLE (WM_USER+1)
|
|
|
|
// Internal prototypes
|
|
LRESULT CALLBACK TargetProxyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
HWND _CreateProxyWindow();
|
|
|
|
|
|
#ifdef DEBUG
|
|
UINT g_cRefExtra = 0;
|
|
#endif
|
|
//
|
|
// This dynamic structure array contains the list of registered drop target.
|
|
//
|
|
HDSA g_hdsaDropTargets = NULL;
|
|
|
|
#pragma data_seg(DATASEG_PERINSTANCE)
|
|
int g_bAnyDropTarget = 0; // True if we every had a drop target in this process.
|
|
#pragma data_seg()
|
|
|
|
typedef struct _DROPTARGETINFO // dti
|
|
{
|
|
HWND hwndTarget;
|
|
HWND hwndProxyTarget;
|
|
LPDROPTARGET pdtgt;
|
|
UINT idProcess;
|
|
UINT idThread;
|
|
#ifdef OLE_DELAYED_LOADING
|
|
BOOL fRegisteredToOLE;
|
|
#endif // OLE_DELAYED_LOADING
|
|
} DROPTARGETINFO, FAR* LPDROPTARGETINFO;
|
|
|
|
LPDROPTARGETINFO SHDrag_FindDropTarget(HWND hwndTarget, int* pi, BOOL fInContext);
|
|
|
|
#ifdef OLE_DELAYED_LOADING
|
|
BOOL g_fRegisterToOLE = FALSE; // must be in "shared data section"
|
|
#endif // OLE_DELAYED_LOADING
|
|
|
|
//
|
|
// Emulates RegisterDragDrop of OLE 2.0
|
|
//
|
|
HRESULT WINAPI SHRegisterDragDrop(HWND hwnd, LPDROPTARGET pdtgt)
|
|
{
|
|
HRESULT hres;
|
|
HWND hwndProxyTarget = _CreateProxyWindow();
|
|
DROPTARGETINFO dti = { hwnd, hwndProxyTarget,
|
|
pdtgt,
|
|
GetCurrentProcessId(),
|
|
GetCurrentThreadId(),
|
|
FALSE };
|
|
|
|
#ifdef OLE_DELAYED_LOADING
|
|
//
|
|
// If the OLE is already loaded in this process, register this window
|
|
// to the OLE as well. Note that we do it before so that we can store
|
|
// fRegisteredToOLE flag in the g_hdsaDropTargets.
|
|
//
|
|
if (g_fRegisterToOLE)
|
|
{
|
|
SHXRegisterDragDrop(hwnd, pdtgt);
|
|
dti.fRegisteredToOLE = TRUE;
|
|
}
|
|
#endif // OLE_DELAYED_LOADING
|
|
|
|
pdtgt->lpVtbl->AddRef(pdtgt);
|
|
|
|
ENTERCRITICAL;
|
|
{
|
|
if (!g_hdsaDropTargets) {
|
|
g_hdsaDropTargets = DSA_Create(SIZEOF(DROPTARGETINFO), 8);
|
|
}
|
|
|
|
if (g_hdsaDropTargets)
|
|
{
|
|
if (!SHDrag_FindDropTarget(hwnd, NULL, FALSE))
|
|
{
|
|
DSA_InsertItem(g_hdsaDropTargets, 0x7fffffff, &dti);
|
|
g_bAnyDropTarget = TRUE;
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If the window is already registered, we'll hit this assert;
|
|
// we are not supposed to hit this assert. Please let me know
|
|
// if you hit this assert. (SatoNa)
|
|
//
|
|
Assert(0);
|
|
hres = DRAGDROP_E_ALREADYREGISTERED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
#ifdef SN_TRACE
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - A drop target registered (%x, %x, %x) - Total %d"),
|
|
hwnd, hwndProxyTarget, pdtgt,
|
|
DSA_GetItemCount(g_hdsaDropTargets));
|
|
#endif
|
|
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// Emulates RevokeDragDrop of OLE 2.0
|
|
//
|
|
HRESULT WINAPI SHRevokeDragDrop(HWND hwnd)
|
|
{
|
|
HRESULT hres = DRAGDROP_E_NOTREGISTERED;
|
|
DROPTARGETINFO dti; // No initialization needed (CAREFUL!)
|
|
|
|
//
|
|
// Find the registered one, and remove it from the list.
|
|
//
|
|
ENTERCRITICAL;
|
|
{
|
|
int i;
|
|
LPDROPTARGETINFO pdti = SHDrag_FindDropTarget(hwnd, &i, TRUE);
|
|
if (pdti)
|
|
{
|
|
dti = *pdti; // copy it for later (see below)
|
|
DSA_DeleteItem(g_hdsaDropTargets, i);
|
|
hres = NOERROR;
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
//
|
|
// We need to do this clean-up from outside of the critical section.
|
|
//
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
dti.pdtgt->lpVtbl->Release(dti.pdtgt);
|
|
DestroyWindow(dti.hwndProxyTarget);
|
|
#ifdef SN_TRACE
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - A drop target revoked (%x, %x, %x) - Total %d"),
|
|
dti.hwndTarget, dti.hwndProxyTarget, dti.pdtgt,
|
|
DSA_GetItemCount(g_hdsaDropTargets));
|
|
#endif
|
|
|
|
#ifdef OLE_DELAYED_LOADING
|
|
if (dti.fRegisteredToOLE)
|
|
{
|
|
HRESULT hresT;
|
|
hresT = SHXRevokeDragDrop(hwnd);
|
|
Assert(SUCCEEDED(hresT)); // something is wrong with OLE
|
|
}
|
|
#endif // OLE_DELAYED_LOADING
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
//
|
|
// Returns the pointer to the droptarget info for the specified target
|
|
// window. This function must be called within the critical section.
|
|
//
|
|
LPDROPTARGETINFO SHDrag_FindDropTarget(HWND hwndTarget, int* pi, BOOL fInContext)
|
|
{
|
|
int i;
|
|
UINT idProcess = GetCurrentProcessId();
|
|
UINT idThread = GetCurrentThreadId();
|
|
|
|
if (!g_hdsaDropTargets) {
|
|
return NULL; // No drop target registered ever.
|
|
}
|
|
|
|
ASSERTCRITICAL;
|
|
for (i=0 ; i<DSA_GetItemCount(g_hdsaDropTargets) ; i++ )
|
|
{
|
|
LPDROPTARGETINFO pdti = DSA_GetItemPtr(g_hdsaDropTargets, i);
|
|
if (pdti->hwndTarget == hwndTarget)
|
|
{
|
|
if (fInContext && pdti->idProcess!=idProcess && pdti->idThread!=idThread)
|
|
{
|
|
//
|
|
// WANTED: I've been looking for a repro case which will
|
|
// hit this assert. Please let me know if you hit this.
|
|
// (SatoNa)
|
|
//
|
|
DebugMsg(DM_ERROR, TEXT("sh ER - ### Please let me know if you see this (SatoNa) ###"));
|
|
Assert(0);
|
|
continue;
|
|
}
|
|
|
|
if (pi) {
|
|
*pi = i;
|
|
}
|
|
return pdti;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
LPDROPTARGETINFO SHDrag_FindProxyTarget(HWND hwndProxyTarget)
|
|
{
|
|
int i;
|
|
|
|
if (!g_hdsaDropTargets) {
|
|
return NULL; // No drop target registered ever.
|
|
}
|
|
|
|
ASSERTCRITICAL;
|
|
for (i=0 ; i<DSA_GetItemCount(g_hdsaDropTargets) ; i++ )
|
|
{
|
|
LPDROPTARGETINFO pdti = DSA_GetItemPtr(g_hdsaDropTargets, i);
|
|
if (pdti->hwndProxyTarget == hwndProxyTarget) {
|
|
return pdti;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
typedef struct _DRAGCONTEXT { // drgc
|
|
HWND hwndProxyTarget;
|
|
HWND hwndHit;
|
|
HWND hwndProxySource;
|
|
HWND hwndOwner;
|
|
DWORD grfKeyState;
|
|
POINTL ptl;
|
|
DWORD dwEffect;
|
|
DWORD dwSrcEffect;
|
|
LPDATAOBJECT pdtobjOrg; // original data object (source process)
|
|
LPDATAOBJECT pdtobj; // marshalled data object (target process)
|
|
LPVOID pvDataObject;
|
|
DWORD dwProcessID; // of calling process
|
|
BOOL fDropped; // indicates whether it is dropped or not.
|
|
#ifdef WIN31_DROPTARGET
|
|
BOOL fHDrop; // the data object contains an HDROP
|
|
HWND hwnd31Target; // might be different from hwndTarget
|
|
#endif
|
|
} DRAGCONTEXT, FAR* LPDRAGCONTEXT;
|
|
|
|
typedef enum
|
|
{
|
|
SHD_DRAGLEAVE = 0,
|
|
SHD_DRAGENTER = 1,
|
|
SHD_DRAGOVER = 2,
|
|
SHD_DROP = 3
|
|
};
|
|
|
|
#ifdef WIN31_DROPTARGET
|
|
|
|
// are we dropping in the nonclient area of the window or on
|
|
// an iconic window?
|
|
//
|
|
// in:
|
|
// hwnd
|
|
// pt points in screen coords inside the window rect
|
|
//
|
|
// returns:
|
|
// TRUE in non client area
|
|
// FALSE in client area
|
|
|
|
BOOL IsNCDrop(HWND hwnd, POINT pt)
|
|
{
|
|
return (!IsIconic(hwnd) &&
|
|
HTCLIENT!=SendMessage(hwnd, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y)));
|
|
}
|
|
|
|
BOOL SHDrag_IsWin31Target(HWND hwnd, BOOL fNC, DROPSTRUCT* pds)
|
|
{
|
|
if (IsWindowEnabled(hwnd))
|
|
{
|
|
BOOL bRet;
|
|
pds->hwndSink = hwnd;
|
|
bRet = SendMessage(hwnd, WM_QUERYDROPOBJECT, fNC, (LPARAM)pds);
|
|
|
|
//
|
|
// If the window returns FALSE even though the window has the
|
|
// WS_EX_ACCEPTFILES flag, it means that its window proc is
|
|
// processing this undocumented WM_QUERYDROPOBJECT.
|
|
//
|
|
if (!bRet && (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_ACCEPTFILES))
|
|
{
|
|
TCHAR szWindowClass[64];
|
|
int cch = GetClassName(hwnd, szWindowClass, ARRAYSIZE(szWindowClass));
|
|
|
|
DebugMsg(DM_TRACE, TEXT("sh TR SHDrag_IsWin31Target -- Processing WM_QUERYDROPOBJECT! (%x, %d, %s)"), hwnd, cch, szWindowClass);
|
|
|
|
//
|
|
// Hack for Novell GroupWise 4.1
|
|
//
|
|
if (cch==7 && lstrcmp(szWindowClass, TEXT("OF41win"))==0) {
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
HWND _GetParent(HWND hwnd)
|
|
{
|
|
DWORD style = GetWindowLong(hwnd, GWL_STYLE);
|
|
if (style & WS_CHILD)
|
|
{
|
|
return GetParent(hwnd);
|
|
}
|
|
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - _GetParent returning NULL since this is not a child (parent=%x)"), GetParent(hwnd));
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// This function finds the Win3.1 drop target from the parent chain of
|
|
// pdrgc->hwndHit, and returns TRUE if it found it. It also map the
|
|
// pds->ptDrop into the client coordinate of the drop target window.
|
|
// It will update pdrgc->hwnd31Target as well.
|
|
//
|
|
BOOL SHDrag_Win31QueryDropObject(LPDRAGCONTEXT pdrgc, DROPSTRUCT *pds)
|
|
{
|
|
//
|
|
// hwndTarget is not a registered drop target, try Win31 style.
|
|
//
|
|
HWND hwnd31Target = pdrgc->hwndHit;
|
|
BOOL fNC;
|
|
BOOL fWin31Target;
|
|
|
|
if (!hwnd31Target || !pdrgc->fHDrop)
|
|
return FALSE;
|
|
|
|
fNC = IsNCDrop(hwnd31Target, pds->ptDrop);
|
|
ScreenToClient(hwnd31Target, &pds->ptDrop);
|
|
|
|
// Check if this is a Win31 droptarget.
|
|
fWin31Target = SHDrag_IsWin31Target(hwnd31Target, fNC, pds);
|
|
|
|
//
|
|
// If we are in the client area, walk up the parent chain until we find
|
|
// a valid drop target.
|
|
//
|
|
if (!fNC)
|
|
{
|
|
while (!fWin31Target)
|
|
{
|
|
HWND hwndParent = _GetParent(hwnd31Target);
|
|
if (!hwndParent) {
|
|
break; // Can't find any drop target.
|
|
}
|
|
|
|
// Lets map the point into the parents coordinate space
|
|
MapWindowPoints(hwnd31Target, hwndParent, &pds->ptDrop, 1);
|
|
hwnd31Target = hwndParent;
|
|
fWin31Target = SHDrag_IsWin31Target(hwnd31Target, fNC, pds);
|
|
}
|
|
}
|
|
|
|
if (!fWin31Target) {
|
|
hwnd31Target = NULL;
|
|
}
|
|
|
|
if (pdrgc->hwnd31Target != hwnd31Target)
|
|
{
|
|
if (pdrgc->hwnd31Target)
|
|
{
|
|
#ifdef SN_TRACE
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Leaving Win31 drop target"));
|
|
#endif
|
|
SendMessage(pdrgc->hwnd31Target, WM_DRAGSELECT, FALSE, (LPARAM)pds);
|
|
pdrgc->dwEffect = 0;
|
|
}
|
|
|
|
pdrgc->hwnd31Target = hwnd31Target;
|
|
|
|
if (pdrgc->hwnd31Target)
|
|
{
|
|
#ifdef SN_TRACE
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Entering Win31 drop target (hit=%x, target=%x)"), pdrgc->hwndHit, pdrgc->hwnd31Target);
|
|
#endif
|
|
SendMessage(pdrgc->hwnd31Target, WM_DRAGSELECT, TRUE, (LPARAM)pds);
|
|
//
|
|
// We always says "this Win 3.1 target supports only Copy
|
|
// operation. This prevents our link-only sources (such as
|
|
// \\pyrex\user) to be a Win 3.1 drag source.
|
|
//
|
|
pdrgc->dwEffect = DROPEFFECT_COPY;
|
|
}
|
|
}
|
|
|
|
return (BOOL)pdrgc->hwnd31Target;
|
|
}
|
|
|
|
#endif // WIN31_DROPTARGET
|
|
|
|
|
|
void SHDrag_DragLeave(LPDRAGCONTEXT pdrgc)
|
|
{
|
|
if (pdrgc->hwndProxyTarget)
|
|
{
|
|
SendMessage(pdrgc->hwndProxyTarget, PXM_DRAGDROP, SHD_DRAGLEAVE, (LPARAM)pdrgc);
|
|
pdrgc->hwndProxyTarget = NULL;
|
|
}
|
|
#ifdef WIN31_DROPTARGET
|
|
else if (pdrgc->hwnd31Target)
|
|
{
|
|
// If this is a Win31 drop target, let it know that we are leaving.
|
|
DROPSTRUCT ds = {
|
|
pdrgc->hwndProxySource, // hwndSource
|
|
pdrgc->hwnd31Target, // hwndSink
|
|
DOF_SHELLDATA, // so winfile does not get upset
|
|
0, // dwData
|
|
{ pdrgc->ptl.x, pdrgc->ptl.y },
|
|
0
|
|
};
|
|
|
|
ScreenToClient(pdrgc->hwnd31Target, &ds.ptDrop);
|
|
#ifdef SN_TRACE
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Leaving Win31 drop target"));
|
|
#endif
|
|
SendMessage(pdrgc->hwnd31Target, WM_DRAGSELECT, FALSE, (LPARAM)&ds);
|
|
pdrgc->hwnd31Target = NULL;
|
|
}
|
|
#endif
|
|
pdrgc->dwEffect = 0;
|
|
}
|
|
|
|
|
|
|
|
void SHDrag_DragEnter(LPDRAGCONTEXT pdrgc, HWND hwndHit)
|
|
{
|
|
pdrgc->hwndHit = hwndHit;
|
|
|
|
Assert(pdrgc->hwndProxyTarget==NULL);
|
|
Assert(pdrgc->dwEffect==0);
|
|
Assert(pdrgc->hwnd31Target == NULL);
|
|
|
|
if (hwndHit==NULL || !IsWindowEnabled(hwndHit)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check if this is a registered drop target.
|
|
//
|
|
ENTERCRITICAL;
|
|
{
|
|
//
|
|
// Search a shell drop target in the parent chain.
|
|
//
|
|
HWND hwndT;
|
|
for(hwndT=hwndHit; hwndT; hwndT=_GetParent(hwndT))
|
|
{
|
|
LPDROPTARGETINFO pdti = SHDrag_FindDropTarget(hwndT, NULL, FALSE);
|
|
if (pdti) {
|
|
// Yes, we found it.
|
|
Assert(hwndT == pdti->hwndTarget);
|
|
pdrgc->hwndProxyTarget = pdti->hwndProxyTarget;
|
|
#ifdef SN_TRACE
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Found (%x) as a registered target"), hwndT);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
if (pdrgc->hwndProxyTarget) {
|
|
SendMessage(pdrgc->hwndProxyTarget, PXM_DRAGDROP, SHD_DRAGENTER, (LPARAM)pdrgc);
|
|
}
|
|
#ifdef WIN31_DROPTARGET
|
|
else
|
|
{
|
|
DROPSTRUCT ds = {
|
|
pdrgc->hwndProxySource, // hwndSource
|
|
hwndHit, // hwndSink
|
|
DOF_SHELLDATA, // so winfile does not get upset
|
|
0, // dwData
|
|
{ pdrgc->ptl.x, pdrgc->ptl.y },
|
|
0
|
|
};
|
|
SHDrag_Win31QueryDropObject(pdrgc, &ds);
|
|
}
|
|
#endif // WIN31_DROPTARGET
|
|
}
|
|
|
|
void SHDrag_DragOver(LPDRAGCONTEXT pdrgc)
|
|
{
|
|
if (pdrgc->hwndHit==NULL || !IsWindowEnabled(pdrgc->hwndHit)) {
|
|
pdrgc->dwEffect = 0;
|
|
return;
|
|
}
|
|
|
|
if (pdrgc->hwndProxyTarget) {
|
|
SendMessage(pdrgc->hwndProxyTarget, PXM_DRAGDROP, SHD_DRAGOVER, (LPARAM)pdrgc);
|
|
// DebugMsg(DM_TRACE, "sh TR - SHDrag_DragOver dwEffect = %d", pdrgc->dwEffect);
|
|
}
|
|
#ifdef WIN31_DROPTARGET
|
|
else
|
|
{
|
|
// If this is a Win31 drop target, let it know that we are dragging over.
|
|
DROPSTRUCT ds = {
|
|
pdrgc->hwndProxySource, // hwndSource
|
|
pdrgc->hwndHit,
|
|
DOF_SHELLDATA, // so winfile does not get upset
|
|
0, // dwData
|
|
{ pdrgc->ptl.x, pdrgc->ptl.y },
|
|
0,
|
|
};
|
|
|
|
if (SHDrag_Win31QueryDropObject(pdrgc, &ds))
|
|
{
|
|
SendMessage(pdrgc->hwnd31Target, WM_DRAGMOVE, 0, (LPARAM)&ds);
|
|
//
|
|
// We always says "this Win 3.1 target supports only Copy
|
|
// operation. This prevents our link-only sources (such as
|
|
// \\pyrex\user) to be a Win 3.1 drag source.
|
|
//
|
|
pdrgc->dwEffect = DROPEFFECT_COPY;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
VOID CALLBACK SHDrag_DropCallBack(HWND hwnd, UINT uMsg, DWORD dwData, LRESULT lResult)
|
|
{
|
|
LPDRAGCONTEXT pdrgc = (LPDRAGCONTEXT)dwData;
|
|
pdrgc->fDropped = TRUE;
|
|
}
|
|
|
|
// 16 bit hwnds have a selector for the hinst
|
|
|
|
#define IsWindow32(w) HIWORD(GetWindowInstance(w))
|
|
|
|
// we give 32bit hwnds long names, 16 bit guys get short names. it would
|
|
// be nice to give 4.0 16 bit apps long names too... but we can bag that for now
|
|
|
|
UINT GetHDropAspect(HWND hwnd)
|
|
{
|
|
return IsWindow32(hwnd) ? DVASPECT_CONTENT : DVASPECT_SHORTNAME;
|
|
}
|
|
|
|
|
|
HRESULT WINAPI SHDrag_Drop(LPDRAGCONTEXT pdrgc)
|
|
{
|
|
HRESULT hres = NOERROR;
|
|
BOOL fOwnerDisabled = FALSE;
|
|
|
|
//
|
|
// Disable the owner window unless the target window is one of its
|
|
// child windows.
|
|
//
|
|
if (pdrgc->hwndOwner && !IsChild(pdrgc->hwndOwner, pdrgc->hwndHit))
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - SHDrag_DropThread Disabling pdrgc->hwndOwner"));
|
|
fOwnerDisabled = TRUE;
|
|
EnableWindow(pdrgc->hwndOwner, FALSE);
|
|
}
|
|
|
|
Assert(pdrgc);
|
|
|
|
if (pdrgc->hwndProxyTarget)
|
|
{
|
|
//
|
|
// We use SendMessageCallback and a message loop so that we can
|
|
// process all the paint messages to the windows owned by this
|
|
// GUI thread.
|
|
//
|
|
Assert(!pdrgc->fDropped);
|
|
if (SendMessageCallback(pdrgc->hwndProxyTarget, PXM_DRAGDROP, SHD_DROP, (LPARAM)pdrgc,
|
|
SHDrag_DropCallBack, (DWORD)pdrgc))
|
|
{
|
|
while (!pdrgc->fDropped)
|
|
{
|
|
MSG msg;
|
|
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
|
|
pdrgc->hwndProxyTarget = NULL;
|
|
}
|
|
#ifdef WIN31_DROPTARGET
|
|
else
|
|
{
|
|
// If this is a Win31 drop target, let it know that we are dragging over.
|
|
DRAGDATA dd;
|
|
DROPSTRUCT ds = {
|
|
pdrgc->hwndProxySource, // hwndSource
|
|
pdrgc->hwndHit, // hwndSink
|
|
DOF_SHELLDATA, // so winfile does not get upset
|
|
(LPARAM)&dd, // dwData
|
|
{ pdrgc->ptl.x, pdrgc->ptl.y },
|
|
0 // dwControlData
|
|
};
|
|
if (SHDrag_Win31QueryDropObject(pdrgc, &ds))
|
|
{
|
|
DWORD dwStatus;
|
|
|
|
dwStatus = SendMessage(pdrgc->hwnd31Target, WM_DROPOBJECT, (WPARAM)pdrgc->hwndProxySource, (LPARAM)&ds);
|
|
|
|
//
|
|
// WinFile.exe does not send WM_DRAGSELECT (wParam=FALSE) when an object is
|
|
// dropped. Therefore, I'm going to remove this. (SatoNa)
|
|
//
|
|
#if 0
|
|
// Chris, are you sure that you want to send this before WM_DROPFILES?
|
|
SendMessage(pdrgc->hwnd31Target, WM_DRAGSELECT, FALSE, (LPARAM)&ds);
|
|
#endif
|
|
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - Dropping onto Win31 drop target (%x)"), dwStatus);
|
|
|
|
if (dwStatus == DO_DROPFILE)
|
|
{
|
|
STGMEDIUM medium;
|
|
// ask for short name version of hdrop
|
|
|
|
FORMATETC fmte = {CF_HDROP, NULL, GetHDropAspect(pdrgc->hwnd31Target), -1, TYMED_HGLOBAL};
|
|
|
|
if (SUCCEEDED(pdrgc->pdtobjOrg->lpVtbl->GetData(pdrgc->pdtobjOrg, &fmte, &medium)))
|
|
{
|
|
//
|
|
// We need to make a copy of HDROP and post.
|
|
//
|
|
UINT cb = GlobalSize(medium.hGlobal);
|
|
HDROP hDrop = GlobalAlloc(GPTR, cb);
|
|
if (hDrop)
|
|
{
|
|
POINT pt;
|
|
LPDROPFILES lpdfs = (LPDROPFILES)hDrop;
|
|
const DROPFILES * pdfsOrg = GlobalLock(medium.hGlobal);
|
|
|
|
//
|
|
// We are not supposed to hit this Assert as far as
|
|
// the data object is created by the shell itself!
|
|
//
|
|
Assert(pdfsOrg->pFiles == SIZEOF(DROPFILES));
|
|
|
|
// First copy the memory down.
|
|
hmemcpy((LPTSTR)hDrop, pdfsOrg, cb);
|
|
|
|
GlobalUnlock(medium.hGlobal);
|
|
|
|
//
|
|
// Now fill in the position information about
|
|
// this drop point.
|
|
//
|
|
pt.x = pdrgc->ptl.x;
|
|
pt.y = pdrgc->ptl.y;
|
|
|
|
lpdfs->fNC = IsNCDrop(pdrgc->hwnd31Target, pt);
|
|
// We always pass pt in client coordinate, even if
|
|
// fNC is TRUE.
|
|
ScreenToClient(pdrgc->hwnd31Target, &pt);
|
|
|
|
|
|
lpdfs->pt.x = (SHORT)pt.x;
|
|
lpdfs->pt.y = (SHORT)pt.y;
|
|
|
|
//
|
|
// HACK: 16-bit apps just call SetActiveWindow
|
|
// which won't bring them to the top. Therfore,
|
|
// we should call SetForegroundWindow for them.
|
|
// Note that 32-bit apps should call it explicitly.
|
|
//
|
|
if (IsWin16Process(pdrgc->hwnd31Target)) {
|
|
SetForegroundWindow(pdrgc->hwnd31Target);
|
|
}
|
|
|
|
if (PostMessage(pdrgc->hwnd31Target, WM_DROPFILES, (WPARAM)hDrop, 0L))
|
|
{
|
|
pdrgc->dwEffect = DROPEFFECT_COPY;
|
|
}
|
|
else
|
|
{
|
|
DragFinish(hDrop);
|
|
}
|
|
SHReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pdrgc->hwnd31Target = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (fOwnerDisabled) {
|
|
EnableWindow(pdrgc->hwndOwner, TRUE);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// Emulates DoDragDrop of OLE 2.0
|
|
//
|
|
// Parameters:
|
|
// hwndOwner -- The owner window to be disabled when Drop happens.
|
|
//
|
|
HRESULT ShellDoDragDrop(HWND hwndOwner, IDataObject *pdata, IDropSource *pdsrc, DWORD dwEffect, DWORD *pdwEffect)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
LPDRAGCONTEXT pdrgc;
|
|
HCURSOR hcurSCopy = LoadCursor(HINST_THISDLL, MAKEINTRESOURCE(IDC_SCOPY));
|
|
HCURSOR hcurInvalid = LoadCursor(NULL, IDC_NO);
|
|
|
|
#ifdef DEBUG
|
|
ULONG cRefData = pdata->lpVtbl->AddRef(pdata) - g_cRefExtra;
|
|
pdata->lpVtbl->Release(pdata);
|
|
#endif
|
|
|
|
pdrgc = Alloc(SIZEOF(DRAGCONTEXT)); // shared & zeroinit!
|
|
if (pdrgc)
|
|
{
|
|
DWORD grfMouseState;
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_SHORTNAME, -1, TYMED_HGLOBAL};
|
|
HCURSOR hcurOld = GetCursor(); // to be restored
|
|
|
|
// see if there is an HDROP in this data object so we can
|
|
// drop on win3.1 apps
|
|
pdrgc->fHDrop = (pdata->lpVtbl->QueryGetData(pdata, &fmte) == NOERROR);
|
|
pdrgc->hwndOwner = hwndOwner;
|
|
pdrgc->dwSrcEffect = dwEffect;
|
|
|
|
pdrgc->pvDataObject = DataObj_SaveShellData(pdata, TRUE);
|
|
pdrgc->dwProcessID = GetCurrentProcessId();
|
|
pdrgc->pdtobjOrg = pdata;
|
|
|
|
pdrgc->grfKeyState = grfMouseState =
|
|
((GetKeyState(VK_LBUTTON)<0) ? MK_LBUTTON : 0) |
|
|
((GetKeyState(VK_MBUTTON)<0) ? MK_MBUTTON : 0) |
|
|
((GetKeyState(VK_RBUTTON)<0) ? MK_RBUTTON : 0);
|
|
|
|
// we can still do the drag loop if we don't have a marshled version of the
|
|
// data object, we just restrict the drag to be within the current process
|
|
//
|
|
// if (pdrgc->pvDataObject)
|
|
{
|
|
HWND hwndProxySource = _CreateProxyWindow();
|
|
if (hwndProxySource)
|
|
{
|
|
extern void DAD_ShowCursor(BOOL fShow);
|
|
BOOL bContinueDragging;
|
|
BOOL fCanceled = FALSE;
|
|
|
|
#define SCROLL_SAMPLE_RATE 100 // get notfied so we can poll our position
|
|
UINT idTimer = SetTimer(NULL, 0, SCROLL_SAMPLE_RATE, NULL);
|
|
|
|
pdrgc->hwndProxySource = hwndProxySource;
|
|
SetCapture(hwndProxySource);
|
|
|
|
for (bContinueDragging = TRUE ; bContinueDragging; )
|
|
{
|
|
MSG msg, msgT;
|
|
HWND hwndHit;
|
|
DWORD grfNewKeyState;
|
|
|
|
//
|
|
// Notes:
|
|
// We can't use WM_KEYDOWN message, because we need to set
|
|
// the focus to the drag source window to get that message.
|
|
// However, we don't want to change the focus when the user
|
|
// starts dragging.
|
|
//
|
|
if ((GetCapture() != hwndProxySource) || (GetKeyState(VK_ESCAPE) < 0))
|
|
{
|
|
fCanceled = TRUE;
|
|
break; // for loop
|
|
}
|
|
|
|
grfNewKeyState =
|
|
((GetKeyState(VK_SHIFT )<0) ? MK_SHIFT : 0) |
|
|
((GetKeyState(VK_CONTROL)<0) ? MK_CONTROL : 0) |
|
|
((GetKeyState(VK_MENU )<0) ? MK_ALT : 0);
|
|
if (grfNewKeyState != (pdrgc->grfKeyState & (MK_SHIFT|MK_CONTROL|MK_ALT)))
|
|
{
|
|
pdrgc->grfKeyState = grfMouseState | grfNewKeyState;
|
|
|
|
// I don't trust just jumping to the mousemove code, since
|
|
// we are checking the key state asynchronously
|
|
PostMessage(hwndProxySource, WM_MOUSEMOVE,
|
|
(WPARAM)grfNewKeyState, GetMessagePos());
|
|
}
|
|
|
|
if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
continue;
|
|
}
|
|
|
|
switch (msg.message)
|
|
{
|
|
//
|
|
// Read "Notes" above to see why we can't do this.
|
|
//
|
|
#if 0
|
|
case WM_KEYDOWN:
|
|
if (msg.wParam != VK_ESCAPE)
|
|
break;
|
|
|
|
// fall through... to cancel
|
|
#endif
|
|
case WM_LBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
// We should cancel the dragging if the other button is clicked.
|
|
bContinueDragging = FALSE;
|
|
fCanceled = TRUE;
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
// This will force us to drop out of the main loop
|
|
bContinueDragging = FALSE;
|
|
|
|
// We need to do the MouseMove processing here in case we are
|
|
// getting the mouse up at a point without a corresponding mouse
|
|
// move to that point; this will set all of the state variables
|
|
// (hwndTarget and dwEffect) correctly
|
|
goto ForceMouseMove;
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
if (msg.hwnd || (msg.wParam!=idTimer)) {
|
|
goto DispatchMsg;
|
|
}
|
|
// fall through
|
|
|
|
case WM_MOUSEMOVE:
|
|
//
|
|
// If we have another WM_MOUSEMOVE message in the queue
|
|
// (before any other mouse message), don't process this
|
|
// mouse message.
|
|
//
|
|
if (PeekMessage(&msgT, NULL, WM_MOUSEFIRST, WM_MOUSELAST, 0)
|
|
&& msgT.message==WM_MOUSEMOVE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ForceMouseMove:
|
|
hwndHit = WindowFromPoint(msg.pt);
|
|
|
|
// Send an NCHITTEST message (REVIEW: why?)
|
|
SendMessage(hwndHit, WM_NCHITTEST, 0, MAKELPARAM(msg.pt.x, msg.pt.y));
|
|
|
|
pdrgc->ptl.x = msg.pt.x;
|
|
pdrgc->ptl.y = msg.pt.y;
|
|
|
|
if (hwndHit != pdrgc->hwndHit)
|
|
{
|
|
SHDrag_DragLeave(pdrgc);
|
|
SHDrag_DragEnter(pdrgc, hwndHit);
|
|
}
|
|
|
|
SHDrag_DragOver(pdrgc);
|
|
hres = pdsrc->lpVtbl->GiveFeedback(pdsrc, pdrgc->dwEffect);
|
|
|
|
if (hres == DRAGDROP_S_USEDEFAULTCURSORS) {
|
|
if (pdrgc->dwEffect & (DROPEFFECT_COPY|DROPEFFECT_MOVE|DROPEFFECT_LINK)) {
|
|
SetCursor(hcurSCopy);
|
|
} else {
|
|
SetCursor(hcurInvalid);
|
|
}
|
|
}
|
|
break;
|
|
|
|
DispatchMsg:
|
|
default:
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
#ifdef DEBUG
|
|
if (GetCapture() != hwndProxySource) {
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - SHDoDragDrop: We lost capture! Resetting"));
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
// BUGBUG: Should be called by DropSource::QyeryContinueDrag
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
DAD_ShowCursor(TRUE);
|
|
DAD_SetDragCursor(DCID_NULL);
|
|
|
|
KillTimer(NULL, idTimer);
|
|
ReleaseCapture();
|
|
|
|
if (!fCanceled)
|
|
{
|
|
if (pdrgc->dwEffect)
|
|
{
|
|
hres = SHDrag_Drop(pdrgc);
|
|
}
|
|
else
|
|
{
|
|
// tried to drop on someone who did not accept
|
|
MessageBeep(MB_ICONHAND);
|
|
SHDrag_DragLeave(pdrgc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SHDrag_DragLeave(pdrgc);
|
|
}
|
|
|
|
Assert(pdrgc->hwndProxyTarget == NULL);
|
|
Assert(pdrgc->hwnd31Target == NULL);
|
|
|
|
DestroyWindow(hwndProxySource);
|
|
pdrgc->hwndProxySource = NULL;
|
|
}
|
|
|
|
if (pdrgc->pvDataObject)
|
|
Free(pdrgc->pvDataObject);
|
|
}
|
|
|
|
*pdwEffect = pdrgc->dwEffect;
|
|
|
|
SetCursor(hcurOld); // We must SetCusor before Release_DropSource.
|
|
|
|
Free(pdrgc);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
//
|
|
// If we kick off two threaded dragdrop operation, we might hit this
|
|
// assert. Otherwise, we are not supposed to hit this assert.
|
|
//
|
|
AssertMsg(cRefData+g_cRefExtra == pdata->lpVtbl->AddRef(pdata), TEXT("Data Object ref count not ballanced %d"), cRefData);
|
|
pdata->lpVtbl->Release(pdata);
|
|
#endif
|
|
return hres;
|
|
}
|
|
|
|
|
|
//
|
|
// This function creates a proxy window for marshalling.
|
|
//
|
|
|
|
const TCHAR c_szProxyWindowClass[] = TEXT("ProxyTarget");
|
|
|
|
HWND _CreateProxyWindow()
|
|
{
|
|
WNDCLASS cls;
|
|
|
|
if (!GetClassInfo(HINST_THISDLL, c_szProxyWindowClass, &cls))
|
|
{
|
|
cls.hCursor = NULL;
|
|
cls.hIcon = NULL;
|
|
cls.lpszMenuName = NULL;
|
|
cls.hInstance = HINST_THISDLL;
|
|
cls.lpszClassName = c_szProxyWindowClass;
|
|
cls.hbrBackground = NULL;
|
|
cls.lpfnWndProc = TargetProxyWndProc;
|
|
cls.style = 0;
|
|
cls.cbWndExtra = 0;
|
|
cls.cbClsExtra = 0;
|
|
|
|
RegisterClass(&cls);
|
|
}
|
|
|
|
return CreateWindowEx(0, c_szProxyWindowClass, NULL,
|
|
WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, HINST_THISDLL, NULL);
|
|
}
|
|
|
|
// Creates a copy of data object from within the target process
|
|
void _CreateProxyDataObject(LPDRAGCONTEXT pdrgc)
|
|
{
|
|
Assert(pdrgc->pdtobj==NULL);
|
|
|
|
if (pdrgc->dwProcessID == GetCurrentProcessId())
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("Using dataobject in process"));
|
|
pdrgc->pdtobj = pdrgc->pdtobjOrg;
|
|
pdrgc->pdtobj->lpVtbl->AddRef(pdrgc->pdtobj);
|
|
}
|
|
else if (pdrgc->pvDataObject)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("cloning dataobject for inter process stuff"));
|
|
DataObj_CreateFromMemory(pdrgc->pvDataObject, &pdrgc->pdtobj);
|
|
}
|
|
else
|
|
{
|
|
Assert(pdrgc->pdtobj == NULL);
|
|
}
|
|
}
|
|
|
|
void _ReleaseProxyDataObject(LPDRAGCONTEXT pdrgc)
|
|
{
|
|
Assert(pdrgc->pdtobj);
|
|
pdrgc->pdtobj->lpVtbl->Release(pdrgc->pdtobj);
|
|
pdrgc->pdtobj=NULL;
|
|
}
|
|
|
|
LRESULT TargetProxy_OnDragDrop(LPDRAGCONTEXT pdrgc, WPARAM wParam)
|
|
{
|
|
LPDROPTARGET pdtgt = NULL;
|
|
ENTERCRITICAL;
|
|
{
|
|
LPDROPTARGETINFO pdti = SHDrag_FindProxyTarget(pdrgc->hwndProxyTarget);
|
|
if (pdti) {
|
|
pdtgt = pdti->pdtgt;
|
|
pdtgt->lpVtbl->AddRef(pdtgt);
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
if (pdtgt)
|
|
{
|
|
switch(wParam)
|
|
{
|
|
case SHD_DRAGENTER:
|
|
_CreateProxyDataObject(pdrgc);
|
|
if (pdrgc->pdtobj) {
|
|
pdrgc->dwEffect = pdrgc->dwSrcEffect;
|
|
pdtgt->lpVtbl->DragEnter(pdtgt, pdrgc->pdtobj, pdrgc->grfKeyState, pdrgc->ptl, &pdrgc->dwEffect);
|
|
pdrgc->dwEffect &= (DROPEFFECT_COPY|DROPEFFECT_MOVE|DROPEFFECT_LINK);
|
|
}
|
|
break;
|
|
|
|
case SHD_DRAGOVER:
|
|
if (pdrgc->pdtobj) {
|
|
pdrgc->dwEffect = pdrgc->dwSrcEffect;
|
|
pdtgt->lpVtbl->DragOver(pdtgt, pdrgc->grfKeyState, pdrgc->ptl, &pdrgc->dwEffect);
|
|
pdrgc->dwEffect &= (DROPEFFECT_COPY|DROPEFFECT_MOVE|DROPEFFECT_LINK);
|
|
}
|
|
break;
|
|
|
|
case SHD_DRAGLEAVE:
|
|
if (pdrgc->pdtobj) {
|
|
pdtgt->lpVtbl->DragLeave(pdtgt);
|
|
_ReleaseProxyDataObject(pdrgc);
|
|
}
|
|
break;
|
|
|
|
case SHD_DROP:
|
|
if (pdrgc->pdtobj) {
|
|
pdrgc->dwEffect = pdrgc->dwSrcEffect;
|
|
pdtgt->lpVtbl->Drop(pdtgt, pdrgc->pdtobj, pdrgc->grfKeyState, pdrgc->ptl, &pdrgc->dwEffect);
|
|
_ReleaseProxyDataObject(pdrgc);
|
|
}
|
|
break;
|
|
}
|
|
pdtgt->lpVtbl->Release(pdtgt);
|
|
}
|
|
return pdrgc->dwEffect;
|
|
}
|
|
|
|
#ifdef OLE_DELAYED_LOADING
|
|
LRESULT TargetProxy_OnFoundOLE(HWND hwndProxyTarget, HWND hwndTarget)
|
|
{
|
|
DROPTARGETINFO dti = { NULL, NULL, NULL };
|
|
|
|
//
|
|
// Find the drop target within a critical section
|
|
//
|
|
ENTERCRITICAL;
|
|
{
|
|
LPDROPTARGETINFO pdti = SHDrag_FindDropTarget(hwndTarget, NULL, TRUE);
|
|
if (pdti && !pdti->fRegisteredToOLE)
|
|
{
|
|
dti = *pdti;
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
if (dti.hwndTarget)
|
|
{
|
|
HRESULT hres;
|
|
Assert(dti.hwndTarget == hwndTarget);
|
|
|
|
//
|
|
// Call OLE from outside of any critical section.
|
|
//
|
|
hres = SHXRegisterDragDrop(dti.hwndTarget, dti.pdtgt);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
//
|
|
// Update pdti->fRegisteredToOLE within a critical section
|
|
//
|
|
ENTERCRITICAL;
|
|
{
|
|
LPDROPTARGETINFO pdti = SHDrag_FindDropTarget(hwndTarget, NULL, TRUE);
|
|
if (pdti)
|
|
{
|
|
pdti->fRegisteredToOLE = TRUE;
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif // OLE_DELAYED_LOADING
|
|
|
|
//
|
|
// WndProc for the proxy window
|
|
//
|
|
LRESULT CALLBACK TargetProxyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
case PXM_DRAGDROP:
|
|
return TargetProxy_OnDragDrop((LPDRAGCONTEXT)lParam, wParam);
|
|
|
|
case WM_CANCELMODE:
|
|
//
|
|
// This is one of methods that MikeSch suggested
|
|
// me to prevent loosing capture during this loop.
|
|
//
|
|
// Swallow it to prevent loosing the capture.
|
|
return 0L;
|
|
|
|
#ifdef OLE_DELAYED_LOADING
|
|
case PXM_FOUNDOLE:
|
|
return TargetProxy_OnFoundOLE(hwnd, (HWND)wParam);
|
|
#endif OLE_DELAYED_LOADING
|
|
}
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
//===========================================================================
|
|
// Clipboard functions
|
|
//
|
|
// Notes: Although our SHSet/GetClipboard API emulates OLESet/GetClipboard
|
|
// functions, they are not fully compatible. SHSet/GetClipboard API works
|
|
// only if the data object is our CIDLData instance.
|
|
//===========================================================================
|
|
|
|
extern BOOL CIDLData_IsSimple(LPDATAOBJECT pdata);
|
|
extern void WINAPI IDLData_InitializeClipboardFormats(void);
|
|
extern CLIPFORMAT g_cfPrivateShellData;
|
|
|
|
//
|
|
// This function emulates OleSetClipboard().
|
|
//
|
|
HRESULT WINAPI ShellSetClipboard(LPDATAOBJECT pdtobj)
|
|
{
|
|
HRESULT hres = NOERROR;
|
|
|
|
if (OpenClipboard(NULL)) // associate it with current task.
|
|
{
|
|
EmptyClipboard();
|
|
IDLData_InitializeClipboardFormats();
|
|
|
|
if (pdtobj)
|
|
{
|
|
HGLOBAL hmem;
|
|
//
|
|
// Support WIN3.1 style clipboard : Just put the file name of
|
|
// the first item as "FileName".
|
|
//
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
hres = pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
if (DragQueryFile(medium.hGlobal, 0, szPath, ARRAYSIZE(szPath)))
|
|
{
|
|
UINT uLen = lstrlen(szPath)+1;
|
|
|
|
hmem = GlobalAlloc(GPTR, uLen * SIZEOF(TCHAR));
|
|
if (hmem)
|
|
{
|
|
#ifdef UNICODE
|
|
HGLOBAL hmemAnsi;
|
|
|
|
hmemAnsi = GlobalAlloc(GPTR, uLen);
|
|
if (hmemAnsi)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0,
|
|
szPath, uLen,
|
|
(LPSTR)hmemAnsi, uLen,
|
|
NULL, NULL);
|
|
SetClipboardData(g_cfFileName, hmemAnsi);
|
|
lstrcpy((LPTSTR)hmem, szPath);
|
|
SetClipboardData(g_cfFileNameW, hmem);
|
|
}
|
|
#else
|
|
lstrcpy((LPTSTR)hmem, szPath);
|
|
SetClipboardData(g_cfFileName, hmem);
|
|
#endif
|
|
}
|
|
#ifdef MSMAIL32_SUCKS
|
|
// MS Mail 3.2 won't paste "FileName" if text is present
|
|
// in the clipboard.
|
|
hmem = GlobalAlloc(GPTR, (lstrlen(szPath)+1) * SIZEOF(TCHAR));
|
|
if (hmem)
|
|
{
|
|
lstrcpy((LPTSTR)hmem, szPath);
|
|
SetClipboardData(CF_TEXT, hmem);
|
|
}
|
|
#endif
|
|
}
|
|
SHReleaseStgMedium(&medium);
|
|
}
|
|
|
|
//
|
|
// Then, put the body of data object to the clipboard.
|
|
//
|
|
hmem = (HGLOBAL)DataObj_SaveShellData(pdtobj, FALSE);
|
|
if (hmem)
|
|
{
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
hres = pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium);
|
|
if (SUCCEEDED(hres)) {
|
|
SetClipboardData(CF_HDROP, medium.hGlobal);
|
|
}
|
|
SetClipboardData(g_cfPrivateShellData, hmem);
|
|
hres = NOERROR;
|
|
}
|
|
else
|
|
{
|
|
hres = E_INVALIDARG;
|
|
}
|
|
}
|
|
CloseClipboard();
|
|
}
|
|
else
|
|
{
|
|
hres = CLIPBRD_E_CANT_OPEN;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
#ifdef TEST_FILECONTENTS
|
|
|
|
UINT g_cfRichText = 0;
|
|
|
|
HRESULT AddFileContents(IDataObject **ppdtobj)
|
|
{
|
|
HGLOBAL hclip;
|
|
HRESULT hres;
|
|
|
|
*ppdtobj = NULL;
|
|
|
|
if (!g_cfRichText)
|
|
g_cfRichText = RegisterClipboardFormat(c_szRichTextFormat);
|
|
|
|
// REVIEW: we really should do this on demand
|
|
hclip = GetClipboardData(g_cfRichText);
|
|
if (hclip)
|
|
{
|
|
IDataObject *pdtobj;
|
|
hres = CIDLData_CreateInstance(NULL, &pdtobj, NULL);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
HGLOBAL hdesc = GlobalAlloc(GPTR, SIZEOF(FILEGROUPDESCRIPTOR) + SIZEOF(FILEDESCRIPTOR));
|
|
if (hdesc)
|
|
{
|
|
STGMEDIUM medium;
|
|
FORMATETC fmte = {g_cfFileGroupDescriptor, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
// BUGBUG - BobDay - We need to store an ANSI FileGroupDescriptor as one
|
|
// of the formats. I'm not quite sure whether we have to set the data, or
|
|
// whether we just provide some sort of tranlation from the unicode version
|
|
// and its data, but for compatibility it needs to be done. Old apps might
|
|
// look for this specific ansi clipboard format.
|
|
#define pdesc ((FILEGROUPDESCRIPTOR *)hdesc)
|
|
|
|
Assert(pdesc->cItems == 0);
|
|
pdesc->fgd[0].dwFlags = 0; // just the filename is valid
|
|
lstrcpy(pdesc->fgd[0].cFileName, TEXT("Clip Rich Text File.rtf"));
|
|
|
|
medium.tymed = TYMED_HGLOBAL;
|
|
medium.hGlobal = hdesc;
|
|
medium.pUnkForRelease = NULL;
|
|
|
|
// give the data object ownership of ths
|
|
hres = pdtobj->lpVtbl->SetData(pdtobj, &fmte, &medium, TRUE);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
#if 0
|
|
HGLOBAL hdata = GlobalAlloc(GPTR, GlobalSize(hclip));
|
|
if (hdata)
|
|
{
|
|
FORMATETC fmte = {g_cfFileContents, NULL, DVASPECT_CONTENT, pdesc->cItems, TYMED_HGLOBAL};
|
|
CopyMemory(hdata, hclip, GlobalSize(hclip));
|
|
|
|
fmte.lindex = pdesc->cItems;
|
|
|
|
medium.tymed = TYMED_HGLOBAL;
|
|
medium.hGlobal = hdata;
|
|
medium.pUnkForRelease = NULL;
|
|
|
|
// give the data object ownership of ths
|
|
hres = pdtobj->lpVtbl->SetData(pdtobj, &fmte, &medium, TRUE);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
pdesc->cItems++;
|
|
#endif
|
|
medium.pstm = CreateMemStream(hclip, GlobalSize(hclip));
|
|
if (medium.pstm)
|
|
{
|
|
FORMATETC fmte = {g_cfFileContents, NULL, DVASPECT_CONTENT, pdesc->cItems, TYMED_ISTREAM};
|
|
|
|
medium.tymed = TYMED_ISTREAM;
|
|
medium.pUnkForRelease = NULL;
|
|
|
|
hres = pdtobj->lpVtbl->SetData(pdtobj, &fmte, &medium, TRUE);
|
|
if (SUCCEEDED(hres))
|
|
pdesc->cItems++;
|
|
else
|
|
medium.pstm->lpVtbl->Release(medium.pstm);
|
|
}
|
|
|
|
*ppdtobj = pdtobj;
|
|
return NOERROR;
|
|
#if 0
|
|
}
|
|
else
|
|
GlobalFree(hdata);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
GlobalFree(hdesc);
|
|
|
|
#undef pdesc
|
|
}
|
|
pdtobj->lpVtbl->Release(pdtobj);
|
|
}
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
hres = E_INVALIDARG;
|
|
|
|
return hres;
|
|
}
|
|
|
|
#endif // TEST_FILECONTENTS
|
|
|
|
|
|
//
|
|
// This funciton emulates OleGetClipboard()
|
|
//
|
|
STDAPI SHGetClipboard(LPDATAOBJECT *ppdtobj)
|
|
{
|
|
HRESULT hres = NOERROR;
|
|
if (OpenClipboard(NULL)) // associate it with current task
|
|
{
|
|
HGLOBAL hmem;
|
|
LPVOID pmem;
|
|
IDLData_InitializeClipboardFormats();
|
|
hmem = GetClipboardData(g_cfPrivateShellData); // private format
|
|
if (hmem)
|
|
{
|
|
//
|
|
// The source is the shell. Deserialize our CIDLData.
|
|
//
|
|
hres = DataObj_CreateFromMemory(GlobalLock(hmem), ppdtobj);
|
|
GlobalUnlock(hmem);
|
|
}
|
|
else if (GetClipboardData(g_cfHIDA)==NULL) // avoid using HDROP in case of OLE
|
|
{
|
|
//
|
|
// The source is not the shell, try CF_HDROP.
|
|
//
|
|
hmem = GetClipboardData(CF_HDROP);
|
|
if (hmem)
|
|
{
|
|
//
|
|
// There is a CF_HDROP. Create CIDLData from it.
|
|
//
|
|
UINT cb = GlobalSize(hmem);
|
|
HDROP hdrop = GlobalAlloc(GPTR, cb);
|
|
if (hdrop)
|
|
{
|
|
if ((pmem = GlobalLock(hmem)) != NULL)
|
|
{
|
|
hmemcpy((LPVOID)hdrop, pmem, cb);
|
|
GlobalUnlock(hmem);
|
|
}
|
|
|
|
hres = CIDLData_CreateInstance(NULL, ppdtobj, NULL);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = DataObj_SetGlobal(*ppdtobj, CF_HDROP, hdrop);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
DWORD dwPreferred = DROPEFFECT_COPY;
|
|
|
|
hmem = GetClipboardData(g_cfPreferredDropEffect);
|
|
if (hmem &&
|
|
((pmem = GlobalLock(hmem)) != NULL))
|
|
{
|
|
dwPreferred = *(DWORD *)pmem;
|
|
GlobalUnlock(hmem);
|
|
}
|
|
|
|
DataObj_SetPreferredEffect(*ppdtobj, dwPreferred);
|
|
}
|
|
else
|
|
{
|
|
GlobalFree(hdrop);
|
|
(*ppdtobj)->lpVtbl->Release(*ppdtobj);
|
|
*ppdtobj = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = CLIPBRD_E_BAD_DATA;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hres = CLIPBRD_E_BAD_DATA;
|
|
}
|
|
|
|
CloseClipboard();
|
|
}
|
|
else
|
|
{
|
|
hres = S_FALSE;
|
|
}
|
|
|
|
#ifdef OLE_DAD_TARGET
|
|
if (hres == CLIPBRD_E_BAD_DATA)
|
|
{
|
|
if (g_hmodOLE)
|
|
{
|
|
hres = SHXOleGetClipboard(ppdtobj);
|
|
}
|
|
#ifdef TEST_FILECONTENTS
|
|
else
|
|
{
|
|
hres = AddFileContents(ppdtobj);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("created file contents data object"));
|
|
}
|
|
}
|
|
#endif // TEST_FILECONTENTS
|
|
}
|
|
#endif // OLE_DAD_TARGET
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
#ifdef OLE_DELAYED_LOADING
|
|
|
|
void RegisterShellDropTargetsToOLE(void)
|
|
{
|
|
g_fRegisterToOLE = TRUE;
|
|
if (g_hdsaDropTargets)
|
|
{
|
|
//
|
|
// Find not-yet-registered one and post a message.
|
|
//
|
|
ENTERCRITICAL;
|
|
{
|
|
int i;
|
|
for (i=0; i<DSA_GetItemCount(g_hdsaDropTargets) ; i++)
|
|
{
|
|
LPDROPTARGETINFO pdti = DSA_GetItemPtr(g_hdsaDropTargets, i);
|
|
if (!pdti->fRegisteredToOLE)
|
|
{
|
|
PostMessage(pdti->hwndProxyTarget, PXM_FOUNDOLE, (WPARAM)pdti->hwndTarget, 0);
|
|
}
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
}
|
|
#endif // OLE_DELAYED_LOADING
|
|
|
|
//
|
|
// Returns TRUE, if the window is owned by 16-bit process.
|
|
//
|
|
BOOL IsWin16Process(HWND hwnd)
|
|
{
|
|
DWORD idProcess;
|
|
BOOL fRet = FALSE;
|
|
if (GetWindowThreadProcessId(hwnd, &idProcess))
|
|
{
|
|
if (GetProcessDword(idProcess, GPD_FLAGS) & GPF_WIN16_PROCESS)
|
|
{
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
void DragDrop_Term(BOOL fProcessDetach)
|
|
{
|
|
int i;
|
|
if (!g_hdsaDropTargets)
|
|
return;
|
|
|
|
ENTERCRITICAL;
|
|
if (fProcessDetach)
|
|
{
|
|
UINT idProcess = GetCurrentProcessId();
|
|
for (i = DSA_GetItemCount(g_hdsaDropTargets)-1 ; i>=0 ;i--)
|
|
{
|
|
LPDROPTARGETINFO pdti = DSA_GetItemPtr(g_hdsaDropTargets, i);
|
|
if (pdti->idProcess == idProcess)
|
|
{
|
|
//
|
|
// WARNING: Note that we MUST not release them.
|
|
//
|
|
DebugMsg(DM_TRACE, TEXT("sh WA DragDrop_Term(1) is removing registered target for this process"));
|
|
#ifdef SN_TRACE
|
|
Assert(0);
|
|
#endif // SN_TRACE
|
|
DSA_DeleteItem(g_hdsaDropTargets, i);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UINT idThread = GetCurrentThreadId();
|
|
for (i = DSA_GetItemCount(g_hdsaDropTargets)-1 ; i>=0 ;i--)
|
|
{
|
|
LPDROPTARGETINFO pdti = DSA_GetItemPtr(g_hdsaDropTargets, i);
|
|
if (pdti->idThread == idThread)
|
|
{
|
|
//
|
|
// WARNING: Note that we don't release them because
|
|
// the memory might have been gone already. We simply leak.
|
|
//
|
|
DebugMsg(DM_TRACE, TEXT("sh WA DragDrop_Term(0) is removing registered target of this thread"));
|
|
DebugMsg(DM_TRACE, TEXT("sh WA ### Pls let me know if you see this message NOT after GPF (SatoNa)###"));
|
|
Assert(0);
|
|
DSA_DeleteItem(g_hdsaDropTargets, i);
|
|
}
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|