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.
5425 lines
196 KiB
5425 lines
196 KiB
/**MOD+**********************************************************************/
|
|
/* Module: cclip.cpp */
|
|
/* */
|
|
/* Purpose: Shared Clipboard Client Addin */
|
|
/* */
|
|
/* Copyright(C) Microsoft Corporation 1998-1999 */
|
|
/* */
|
|
/**MOD-**********************************************************************/
|
|
|
|
/****************************************************************************/
|
|
/* Precompiled header */
|
|
/****************************************************************************/
|
|
#include <precom.h>
|
|
|
|
/****************************************************************************/
|
|
/* Trace definitions */
|
|
/****************************************************************************/
|
|
|
|
#define TRC_GROUP TRC_GROUP_NETWORK
|
|
#define TRC_FILE "cclip"
|
|
#include <atrcapi.h>
|
|
|
|
#include "vcint.h"
|
|
#include "drapi.h"
|
|
|
|
/****************************************************************************/
|
|
// Headers
|
|
/****************************************************************************/
|
|
#include <cclip.h>
|
|
#ifndef OS_WINCE
|
|
#include <shlobj.h>
|
|
#endif
|
|
|
|
#ifdef OS_WINCE
|
|
#include "ceclip.h"
|
|
#endif
|
|
|
|
#ifdef CLIP_TRANSITION_RECORDING
|
|
|
|
UINT g_rguiDbgLastClipState[DBG_RECORD_SIZE];
|
|
UINT g_rguiDbgLastClipEvent[DBG_RECORD_SIZE];
|
|
LONG g_uiDbgPosition = -1;
|
|
|
|
#endif // CLIP_TRANSITION_RECORDING
|
|
|
|
/****************************************************************************/
|
|
/* CTor */
|
|
/****************************************************************************/
|
|
CClip::CClip(VCManager *virtualChannelMgr)
|
|
{
|
|
PRDPDR_DATA prdpdrData;
|
|
|
|
DC_BEGIN_FN("CClip::CClip");
|
|
|
|
/********************************************************************/
|
|
/* Initialize the data */
|
|
/********************************************************************/
|
|
_GetDataSync[TS_RECEIVE_COMPLETED] = NULL;
|
|
_GetDataSync[TS_RESET_EVENT] = NULL;
|
|
|
|
DC_MEMSET(&_CB, 0, sizeof(_CB));
|
|
|
|
_pVCMgr = virtualChannelMgr;
|
|
prdpdrData = _pVCMgr->GetInitData();
|
|
_CB.fDrivesRedirected = prdpdrData->fEnableRedirectDrives;
|
|
_CB.fFileCutCopyOn = _CB.fDrivesRedirected;
|
|
_pClipData = new CClipData(this);
|
|
if (_pClipData)
|
|
{
|
|
_pClipData->AddRef();
|
|
}
|
|
_pUtObject = (CUT*) LocalAlloc(LPTR, sizeof(CUT));
|
|
if (_pUtObject) {
|
|
_pUtObject->UT_Init();
|
|
}
|
|
|
|
if (prdpdrData->szClipPasteInfoString[0] != 0) {
|
|
if (!WideCharToMultiByte(CP_ACP, 0, prdpdrData->szClipPasteInfoString,
|
|
-1, _CB.pasteInfoA, sizeof(_CB.pasteInfoA), NULL, NULL)) {
|
|
StringCbCopyA(_CB.pasteInfoA,
|
|
sizeof(_CB.pasteInfoA),
|
|
"Preparing paste information...");
|
|
}
|
|
}
|
|
else {
|
|
StringCbCopyA(_CB.pasteInfoA,
|
|
sizeof(_CB.pasteInfoA),
|
|
"Preparing paste information...");
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Store the hInstance */
|
|
/********************************************************************/
|
|
_CB.hInst = GetModuleHandle(NULL);
|
|
TRC_NRM((TB, _T("Store hInst %p"), _CB.hInst));
|
|
DC_END_FN();
|
|
}
|
|
/****************************************************************************/
|
|
/* Wrappers for Malloc, Free & Memcpy */
|
|
/****************************************************************************/
|
|
|
|
#ifdef OS_WIN32
|
|
#define ClipAlloc(size) LocalAlloc(LMEM_FIXED, size)
|
|
#define ClipFree(pData) LocalFree(pData)
|
|
#define ClipMemcpy(pTrg, pSrc, len) DC_MEMCPY(pTrg, pSrc, len)
|
|
#endif
|
|
|
|
DCUINT CClip::GetOsMinorType()
|
|
{
|
|
DCUINT minorType = 0;
|
|
if (_pUtObject) {
|
|
minorType = _pUtObject->UT_GetOsMinorType();
|
|
}
|
|
return minorType;
|
|
}
|
|
/****************************************************************************/
|
|
// ClipCheckState
|
|
/****************************************************************************/
|
|
DCUINT DCINTERNAL CClip::ClipCheckState(DCUINT event)
|
|
{
|
|
DCUINT tableVal = cbStateTable[event][_CB.state];
|
|
|
|
DC_BEGIN_FN("CClip::ClipCheckState");
|
|
|
|
TRC_DBG((TB, _T("Test event %s in state %s"),
|
|
cbEvent[event], cbState[_CB.state]));
|
|
|
|
if (tableVal != CB_TABLE_OK)
|
|
{
|
|
if (tableVal == CB_TABLE_WARN)
|
|
{
|
|
TRC_ALT((TB, _T("Unusual event %s in state %s"),
|
|
cbEvent[event], cbState[_CB.state]));
|
|
}
|
|
else
|
|
{
|
|
TRC_ABORT((TB, _T("Invalid event %s in state %s"),
|
|
cbEvent[event], cbState[_CB.state]));
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(tableVal);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
// ClipGetPermBuf - get a permanently allocated buffer
|
|
/****************************************************************************/
|
|
PTS_CLIP_PDU DCINTERNAL CClip::ClipGetPermBuf(DCVOID)
|
|
{
|
|
PTS_CLIP_PDU pClipPDU;
|
|
|
|
DC_BEGIN_FN("CClip::ClipGetPermBuf");
|
|
|
|
#ifdef USE_SEMAPHORE
|
|
/************************************************************************/
|
|
// On Win32, access to the permanent buffer is synchronised via a
|
|
// semaphore
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Wait for perm TX buffer")));
|
|
WaitForSingleObject(_CB.txPermBufSem, INFINITE);
|
|
pClipPDU = (PTS_CLIP_PDU)(_CB.txPermBuffer);
|
|
#endif
|
|
|
|
TRC_DBG((TB, _T("Return buffer at %#p"), pClipPDU));
|
|
|
|
DC_END_FN();
|
|
return(pClipPDU);
|
|
} /* ClipGetPermBuf */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ClipFreeBuf */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CClip::ClipFreeBuf(PDCUINT8 pBuf)
|
|
{
|
|
#ifndef OS_WINCE
|
|
INT i;
|
|
#endif
|
|
DC_BEGIN_FN("CClip::ClipFreeBuf");
|
|
|
|
TRC_DBG((TB, _T("Release buffer at %p"), pBuf));
|
|
#ifdef USE_SEMAPHORE
|
|
if (pBuf == _CB.txPermBuffer)
|
|
{
|
|
TRC_DBG((TB, _T("Free Permanent buffer at %p"), pBuf));
|
|
if (!ReleaseSemaphore(_CB.txPermBufSem, 1, NULL))
|
|
{
|
|
TRC_SYSTEM_ERROR("ReleaseSemaphore");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_DBG((TB, _T("Free Temporary buffer at %p"), pBuf));
|
|
ClipFree(pBuf);
|
|
}
|
|
#else
|
|
#ifdef OS_WINCE
|
|
INT i;
|
|
#endif
|
|
for (i = 0; i < CB_PERM_BUF_COUNT; i++)
|
|
{
|
|
TRC_DBG((TB, _T("Test buf %d, %p vs %p"), i, pBuf, _CB.txPermBuffer[i]));
|
|
if (pBuf == _CB.txPermBuffer[i])
|
|
{
|
|
TRC_NRM((TB, _T("Free perm buffer %d"), i));
|
|
_CB.txPermBufInUse[i] = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == CB_PERM_BUF_COUNT)
|
|
{
|
|
TRC_DBG((TB, _T("Temporary buffer")));
|
|
ClipFree(pBuf);
|
|
}
|
|
#endif
|
|
|
|
DC_END_FN();
|
|
return;
|
|
} /* ClipFreePermBuf */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ClipDrawClipboard - send the local formats to the remote */
|
|
/****************************************************************************/
|
|
DCBOOL DCINTERNAL CClip::ClipDrawClipboard(DCBOOL mustSend)
|
|
{
|
|
DCUINT32 numFormats;
|
|
DCUINT formatCount;
|
|
DCUINT formatID;
|
|
//
|
|
// formatlist is extracted from a PDU at a non-word boundary
|
|
// so it causes an alignment fault on WIN64. Marked UNALIGNED.
|
|
//
|
|
PTS_CLIP_FORMAT formatList;
|
|
DCUINT nameLen;
|
|
|
|
PTS_CLIP_PDU pClipPDU = NULL;
|
|
DCUINT32 pduLen;
|
|
DCUINT32 dataLen;
|
|
DCBOOL rc = TRUE;
|
|
DCBOOL fHdrop = FALSE ;
|
|
|
|
DC_BEGIN_FN("CClip::ClipDrawClipboard");
|
|
|
|
_CB.dropEffect = FO_COPY ;
|
|
_CB.fAlreadyCopied = FALSE ;
|
|
#ifndef OS_WINCE
|
|
_CB.dwVersion = GetVersion() ;
|
|
#else
|
|
OSVERSIONINFO osv;
|
|
memset(&osv, 0, sizeof(osv));
|
|
osv.dwOSVersionInfoSize = sizeof(osv);
|
|
if (!GetVersionEx(&osv))
|
|
{
|
|
TRC_ERR((TB, _T("GetVersionEx failed!")));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
_CB.dwVersion = MAKELPARAM(MAKEWORD(osv.dwMajorVersion, osv.dwMinorVersion), osv.dwBuildNumber);
|
|
#endif
|
|
_CB.fAlreadyCopied = FALSE ;
|
|
/************************************************************************/
|
|
/* First we open the clipboard */
|
|
/************************************************************************/
|
|
|
|
if (!OpenClipboard(_CB.viewerWindow))
|
|
{
|
|
TRC_ERR((TB, _T("Failed to open CB")));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* It was/is open */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("CB opened")));
|
|
_CB.clipOpen = TRUE;
|
|
/************************************************************************/
|
|
/* Count the formats available, checking we don't blow our limit */
|
|
/************************************************************************/
|
|
numFormats = CountClipboardFormats();
|
|
if (numFormats > CB_MAX_FORMATS)
|
|
{
|
|
TRC_ALT((TB, _T("Num formats %ld too large - limit to %d"),
|
|
numFormats, CB_MAX_FORMATS));
|
|
numFormats = CB_MAX_FORMATS;
|
|
}
|
|
TRC_DBG((TB, _T("found %ld formats"), numFormats));
|
|
|
|
/************************************************************************/
|
|
/* if there are no formats available, and we don't have to send the */
|
|
/* info, then don't! */
|
|
/************************************************************************/
|
|
if ((numFormats == 0) && (mustSend == FALSE))
|
|
{
|
|
TRC_NRM((TB, _T("No formats: skipping send")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Get a send buffer. First work out how big it needs to be */
|
|
/************************************************************************/
|
|
dataLen = numFormats * sizeof(TS_CLIP_FORMAT);
|
|
pduLen = dataLen + sizeof(TS_CLIP_PDU);
|
|
|
|
/************************************************************************/
|
|
/* and make sure that's not too big! */
|
|
/************************************************************************/
|
|
if (pduLen > CHANNEL_CHUNK_LENGTH)
|
|
{
|
|
/********************************************************************/
|
|
/* we'll have to limit the number of formats. How many will fit in */
|
|
/* the max buffer size? */
|
|
/********************************************************************/
|
|
pduLen = CHANNEL_CHUNK_LENGTH;
|
|
dataLen = pduLen - sizeof(TS_CLIP_PDU);
|
|
numFormats = dataLen / sizeof(TS_CLIP_FORMAT);
|
|
|
|
/********************************************************************/
|
|
/* no point in having empty space for the last fractional format! */
|
|
/********************************************************************/
|
|
dataLen = numFormats * sizeof(TS_CLIP_FORMAT);
|
|
pduLen = dataLen + sizeof(TS_CLIP_PDU);
|
|
|
|
TRC_ALT((TB, _T("Too many formats! Limited to %ld"), numFormats));
|
|
}
|
|
|
|
pClipPDU = ClipGetPermBuf();
|
|
|
|
/************************************************************************/
|
|
/* Fill in the common parts of the PDU */
|
|
/************************************************************************/
|
|
DC_MEMSET(pClipPDU, 0, sizeof(*pClipPDU));
|
|
|
|
/************************************************************************/
|
|
/* and now the clip bits */
|
|
/************************************************************************/
|
|
pClipPDU->msgType = TS_CB_FORMAT_LIST;
|
|
pClipPDU->dataLen = dataLen;
|
|
#ifndef UNICODE
|
|
pClipPDU->msgFlags = TS_CB_ASCII_NAMES;
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* if there were any formats, list them */
|
|
/************************************************************************/
|
|
if (numFormats)
|
|
{
|
|
/********************************************************************/
|
|
/* set up the format list */
|
|
/********************************************************************/
|
|
formatList = (PTS_CLIP_FORMAT)(pClipPDU->data);
|
|
|
|
/********************************************************************/
|
|
/* and enumerate the formats */
|
|
/********************************************************************/
|
|
_CB.DIBFormatExists = FALSE;
|
|
formatCount = 0;
|
|
formatID = EnumClipboardFormats(0); /* 0 starts the enumeration */
|
|
|
|
while ((formatID != 0) && (formatCount < numFormats))
|
|
{
|
|
#ifdef OS_WINCE
|
|
DCUINT dwTempID = formatID;
|
|
if (formatID == gfmtShellPidlArray)
|
|
{
|
|
formatID = CF_HDROP;
|
|
}
|
|
#endif
|
|
/****************************************************************/
|
|
/* store the ID */
|
|
/****************************************************************/
|
|
formatList[formatCount].formatID = formatID;
|
|
|
|
/****************************************************************/
|
|
/* find the name for the format */
|
|
/****************************************************************/
|
|
nameLen = GetClipboardFormatName(formatID,
|
|
(PDCTCHAR)formatList[formatCount].formatName,
|
|
TS_FORMAT_NAME_LEN);
|
|
|
|
/****************************************************************/
|
|
/* check for predefined formats - they have no name */
|
|
/****************************************************************/
|
|
if (nameLen == 0)
|
|
{
|
|
TRC_NRM((TB, _T("no name for format %d - predefined"), formatID));
|
|
*(formatList[formatCount].formatName) = '\0';
|
|
}
|
|
|
|
TRC_DBG((TB, _T("found format id %ld, name '%s'"),
|
|
formatList[formatCount].formatID,
|
|
formatList[formatCount].formatName));
|
|
|
|
/****************************************************************/
|
|
/* look for formats we don't send */
|
|
/****************************************************************/
|
|
|
|
if ((formatID == CF_DSPBITMAP) ||
|
|
(formatID == CF_ENHMETAFILE) ||
|
|
((!_CB.fFileCutCopyOn || !_CB.fDrivesRedirected) && (formatID == CF_HDROP)) ||
|
|
(formatID == CF_OWNERDISPLAY))
|
|
{
|
|
// We drop enhanced metafile formats, since the local CB
|
|
// will provide conversion where supported
|
|
//
|
|
// Ownerdisplay just isn't going to work since the two
|
|
// windows are on different machines!
|
|
//
|
|
// File cut/copy isn't going to work if there is no drive
|
|
// redirection!
|
|
TRC_ALT((TB, _T("Dropping format ID %d"), formatID));
|
|
formatList[formatCount].formatID = 0;
|
|
*(formatList[formatCount].formatName) = '\0';
|
|
}
|
|
else if (ClipIsExcludedFormat((PDCTCHAR)formatList[formatCount].formatName))
|
|
{
|
|
//
|
|
// We don't support file cut/paste, so we drop
|
|
// file related formats.
|
|
//
|
|
TRC_ALT((TB, _T("Dropping format name '%s'"), (PDCTCHAR)formatList[formatCount].formatName));
|
|
formatList[formatCount].formatID = 0;
|
|
*(formatList[formatCount].formatName) = '\0';
|
|
}
|
|
else
|
|
{
|
|
/************************************************************/
|
|
/* We support the CF_BITMAP format by converting it to */
|
|
/* CF_DIB. If there is already a CF_DIB format, we don't */
|
|
/* need to do this. */
|
|
/************************************************************/
|
|
if ((formatID == CF_BITMAP) && (_CB.DIBFormatExists))
|
|
{
|
|
TRC_NRM((TB, _T("Dropping CF_BITMAP - CF_DIB is supported")));
|
|
}
|
|
else
|
|
{
|
|
/********************************************************/
|
|
/* It's a supported format */
|
|
/********************************************************/
|
|
if (formatID == CF_BITMAP)
|
|
{
|
|
TRC_NRM((TB, _T("Convert CF_BITMAP to CF_DIB")));
|
|
formatList[formatCount].formatID = CF_DIB;
|
|
}
|
|
else if (formatID == CF_DIB)
|
|
{
|
|
TRC_NRM((TB, _T("Really found DIB format")));
|
|
_CB.DIBFormatExists = TRUE;
|
|
}
|
|
if (CF_HDROP == formatID)
|
|
{
|
|
fHdrop = TRUE ;
|
|
}
|
|
/********************************************************/
|
|
/* update the count and move on */
|
|
/********************************************************/
|
|
formatCount++;
|
|
}
|
|
}
|
|
|
|
#ifdef OS_WINCE
|
|
if (formatID == CF_HDROP)
|
|
formatID = dwTempID; //reset the enumeration index, in case we changed it to accommodate CF_HDROP
|
|
#endif
|
|
/****************************************************************/
|
|
/* get the next format */
|
|
/****************************************************************/
|
|
formatID = EnumClipboardFormats(formatID);
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Update the PDU len - we may have dropped some formats along the */
|
|
/* way */
|
|
/********************************************************************/
|
|
dataLen = formatCount * sizeof(TS_CLIP_FORMAT);
|
|
pduLen = dataLen + sizeof(TS_CLIP_PDU);
|
|
TRC_NRM((TB, _T("Final count: %d formats in data len %ld"),
|
|
formatCount, dataLen));
|
|
|
|
pClipPDU->dataLen = dataLen;
|
|
}
|
|
|
|
// if we're NT/2000 and we're going to send an HDROP
|
|
if (fHdrop)
|
|
{
|
|
TRC_NRM((TB, _T("Creating new temp directory for file data"))) ;
|
|
|
|
// How about handling errors from these fs calls?
|
|
#ifndef OS_WINCE
|
|
if (GetOsMinorType() == TS_OSMINORTYPE_WINDOWS_NT)
|
|
#endif
|
|
{
|
|
#ifndef OS_WINCE
|
|
if (0 == GetTempFileNameW(_CB.baseTempDirW, L"_TS", 0, _CB.tempDirW)) {
|
|
#else
|
|
if (0 == GetTempFileNameW(_CB.baseTempDirW, L"_TS", 0, _CB.tempDirW, MAX_PATH)) {
|
|
#endif
|
|
TRC_ERR((TB, _T("Getting temp file name failed; GetLastError=%u"),
|
|
GetLastError()));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// GetACP always returns a valid value
|
|
if (0 == WideCharToMultiByte(GetACP(), NULL, _CB.tempDirW, -1,
|
|
_CB.tempDirA, (MAX_PATH + 1), NULL, NULL)) {
|
|
TRC_ERR((TB, _T("Getting temp file name failed; GetLastError=%u"),
|
|
GetLastError()));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
DeleteFileW(_CB.tempDirW) ;
|
|
|
|
if (0 == CreateDirectoryW(_CB.tempDirW, NULL)) {
|
|
TRC_ERR((TB, _T("Creating temp directory failed; GetLastError=%u"),
|
|
GetLastError()));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
#ifndef OS_WINCE
|
|
else
|
|
{
|
|
if (0 == GetTempFileNameA(_CB.baseTempDirA, "_TS", 0, _CB.tempDirA)) {
|
|
TRC_ERR((TB, _T("Getting temp file name failed; GetLastError=%u"),
|
|
GetLastError()));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// GetACP always returns a valid value
|
|
if (0 == MultiByteToWideChar(GetACP(), MB_ERR_INVALID_CHARS,
|
|
_CB.tempDirA, -1, _CB.tempDirW,
|
|
sizeof(_CB.tempDirW)/(sizeof(_CB.tempDirW[0])) - 1)) {
|
|
TRC_ERR((TB, _T("Failed conversion to wide char; error %d"),
|
|
GetLastError())) ;
|
|
rc = FALSE ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
// Do not check return value
|
|
DeleteFileA(_CB.tempDirA) ;
|
|
if (0 == CreateDirectoryA(_CB.tempDirA, NULL)) {
|
|
TRC_ERR((TB, _T("Creating temp directory failed; GetLastError=%u"),
|
|
GetLastError()));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Update the state */
|
|
/************************************************************************/
|
|
CB_SET_STATE(CB_STATE_PENDING_FORMAT_LIST_RSP, CB_EVENT_WM_DRAWCLIPBOARD);
|
|
|
|
/************************************************************************/
|
|
/* Send the PDU */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Sending format list")));
|
|
if (_CB.channelEP.pVirtualChannelWriteEx
|
|
(_CB.initHandle, _CB.channelHandle, pClipPDU, pduLen, (LPVOID)pClipPDU)
|
|
!= CHANNEL_RC_OK) {
|
|
ClipFreeBuf((PDCUINT8)pClipPDU);
|
|
}
|
|
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* tidy up */
|
|
/************************************************************************/
|
|
if (_CB.clipOpen)
|
|
{
|
|
TRC_DBG((TB, _T("closing CB")));
|
|
_CB.clipOpen = FALSE;
|
|
if (!CloseClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseClipboard");
|
|
}
|
|
}
|
|
|
|
|
|
DC_END_FN();
|
|
|
|
return(rc);
|
|
|
|
} /* ClipDrawClipboard */
|
|
|
|
#ifndef OS_WINCE
|
|
|
|
/****************************************************************************/
|
|
/* ClipGetMFData */
|
|
/****************************************************************************/
|
|
HANDLE DCINTERNAL CClip::ClipGetMFData(HANDLE hData,
|
|
PDCUINT32 pDataLen)
|
|
{
|
|
DCUINT32 lenMFBits = 0;
|
|
DCBOOL rc = FALSE;
|
|
LPMETAFILEPICT pMFP = NULL;
|
|
HDC hMFDC = NULL;
|
|
HMETAFILE hMF = NULL;
|
|
HGLOBAL hMFBits = NULL;
|
|
HANDLE hNewData = NULL;
|
|
PDCUINT8 pNewData = NULL;
|
|
PDCVOID pBits = NULL;
|
|
|
|
DC_BEGIN_FN("CClip::ClipGetMFData");
|
|
|
|
TRC_NRM((TB, _T("Getting MF data")));
|
|
/************************************************************************/
|
|
/* Lock the memory to get a pointer to a METAFILEPICT header structure */
|
|
/* and create a METAFILEPICT DC. */
|
|
/************************************************************************/
|
|
if (GlobalSize(hData) < sizeof(METAFILEPICT)) {
|
|
TRC_ERR((TB, _T("Unexpected global memory size!")));
|
|
_CB.channelEP.pVirtualChannelCloseEx(_CB.initHandle, _CB.channelHandle);
|
|
DC_QUIT;
|
|
}
|
|
|
|
pMFP = (LPMETAFILEPICT)GlobalLock(hData);
|
|
if (pMFP == NULL)
|
|
{
|
|
TRC_SYSTEM_ERROR("GlobalLock");
|
|
DC_QUIT;
|
|
}
|
|
|
|
hMFDC = CreateMetaFile(NULL);
|
|
if (hMFDC == NULL)
|
|
{
|
|
TRC_SYSTEM_ERROR("CreateMetaFile");
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Copy the MFP by playing it into the DC and closing it. */
|
|
/************************************************************************/
|
|
if (!PlayMetaFile(hMFDC, pMFP->hMF))
|
|
{
|
|
TRC_SYSTEM_ERROR("PlayMetaFile");
|
|
CloseMetaFile(hMFDC);
|
|
DC_QUIT;
|
|
}
|
|
hMF = CloseMetaFile(hMFDC);
|
|
if (hMF == NULL)
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseMetaFile");
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Get the MF bits and determine how long they are. */
|
|
/************************************************************************/
|
|
lenMFBits = GetMetaFileBitsEx(hMF, 0, NULL);
|
|
if (lenMFBits == 0)
|
|
{
|
|
TRC_SYSTEM_ERROR("GetMetaFileBitsEx");
|
|
DC_QUIT;
|
|
}
|
|
TRC_DBG((TB, _T("length MF bits %ld"), lenMFBits));
|
|
|
|
/************************************************************************/
|
|
/* Work out how much memory we need and get a buffer */
|
|
/************************************************************************/
|
|
*pDataLen = sizeof(TS_CLIP_MFPICT) + lenMFBits;
|
|
hNewData = GlobalAlloc(GHND, *pDataLen);
|
|
if (hNewData == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get MF buffer")));
|
|
DC_QUIT;
|
|
}
|
|
pNewData = (PDCUINT8)GlobalLock(hNewData);
|
|
if (NULL == pNewData) {
|
|
TRC_ERR((TB,_T("Failed to lock MF buffer")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRC_DBG((TB, _T("Got data to send len %ld"), *pDataLen));
|
|
|
|
/************************************************************************/
|
|
/* Copy the MF header and bits into the buffer. */
|
|
/************************************************************************/
|
|
((PTS_CLIP_MFPICT)pNewData)->mm = pMFP->mm;
|
|
((PTS_CLIP_MFPICT)pNewData)->xExt = pMFP->xExt;
|
|
((PTS_CLIP_MFPICT)pNewData)->yExt = pMFP->yExt;
|
|
|
|
lenMFBits = GetMetaFileBitsEx(hMF, lenMFBits,
|
|
(pNewData + sizeof(TS_CLIP_MFPICT)));
|
|
if (lenMFBits == 0)
|
|
{
|
|
TRC_SYSTEM_ERROR("GetMetaFileBitsEx");
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* all OK */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Got %ld bits of MF data"), lenMFBits));
|
|
TRC_DATA_DBG("MF bits",
|
|
(pNewData + sizeof(TS_CLIP_MFPICT)),
|
|
(DCUINT)lenMFBits);
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* Unlock any global mem. */
|
|
/************************************************************************/
|
|
if (pMFP)
|
|
{
|
|
GlobalUnlock(hData);
|
|
}
|
|
if (pNewData)
|
|
{
|
|
GlobalUnlock(hNewData);
|
|
}
|
|
if (hMF)
|
|
{
|
|
DeleteMetaFile(hMF);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* if things went wrong, then free the new data */
|
|
/************************************************************************/
|
|
if ((rc == FALSE) && (hNewData != NULL))
|
|
{
|
|
GlobalFree(hNewData);
|
|
hNewData = NULL;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(hNewData);
|
|
} /* ClipGetMFData */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ClipSetMFData */
|
|
/****************************************************************************/
|
|
HANDLE DCINTERNAL CClip::ClipSetMFData(DCUINT32 dataLen,
|
|
PDCVOID pData)
|
|
{
|
|
DCBOOL rc = FALSE;
|
|
HGLOBAL hMFBits = NULL;
|
|
PDCVOID pMFMem = NULL;
|
|
HMETAFILE hMF = NULL;
|
|
HGLOBAL hMFPict = NULL;
|
|
LPMETAFILEPICT pMFPict = NULL;
|
|
|
|
DC_BEGIN_FN("CClip::ClipSetMFData");
|
|
|
|
TRC_DATA_DBG("Received MF data", pData, (DCUINT)dataLen);
|
|
|
|
/************************************************************************/
|
|
/* Allocate memory to hold the MF bits (we need the handle to pass to */
|
|
/* SetMetaFileBits). */
|
|
/************************************************************************/
|
|
hMFBits = GlobalAlloc(GHND, dataLen - (DCUINT32)sizeof(TS_CLIP_MFPICT));
|
|
if (hMFBits == NULL)
|
|
{
|
|
TRC_SYSTEM_ERROR("GlobalAlloc");
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Lock the handle and copy in the MF header. */
|
|
/************************************************************************/
|
|
pMFMem = GlobalLock(hMFBits);
|
|
if (pMFMem == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to lock MF mem")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
DC_HMEMCPY(pMFMem,
|
|
(PDCVOID)((PDCUINT8)pData + sizeof(TS_CLIP_MFPICT)),
|
|
dataLen - sizeof(TS_CLIP_MFPICT) );
|
|
|
|
GlobalUnlock(hMFBits);
|
|
|
|
/************************************************************************/
|
|
/* Now use the copied MF bits to create the actual MF bits and get a */
|
|
/* handle to the MF. */
|
|
/************************************************************************/
|
|
hMF = SetMetaFileBitsEx(dataLen - sizeof(TS_CLIP_MFPICT), (PDCUINT8)pMFMem);
|
|
if (hMF == NULL)
|
|
{
|
|
TRC_SYSTEM_ERROR("SetMetaFileBits");
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Allocate a new METAFILEPICT structure, and use the data from the */
|
|
/* sent header. */
|
|
/************************************************************************/
|
|
hMFPict = GlobalAlloc(GHND, sizeof(METAFILEPICT));
|
|
pMFPict = (LPMETAFILEPICT)GlobalLock(hMFPict);
|
|
if (!pMFPict)
|
|
{
|
|
TRC_ERR((TB, _T("Couldn't allocate METAFILEPICT")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
pMFPict->mm = (LONG)((PTS_CLIP_MFPICT)pData)->mm;
|
|
pMFPict->xExt = (LONG)((PTS_CLIP_MFPICT)pData)->xExt;
|
|
pMFPict->yExt = (LONG)((PTS_CLIP_MFPICT)pData)->yExt;
|
|
pMFPict->hMF = hMF;
|
|
|
|
GlobalUnlock(hMFPict);
|
|
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* tidy up */
|
|
/************************************************************************/
|
|
if (!rc)
|
|
{
|
|
if (hMFPict)
|
|
{
|
|
GlobalFree(hMFPict);
|
|
}
|
|
}
|
|
|
|
{
|
|
if (hMFBits)
|
|
{
|
|
GlobalFree(hMFBits);
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(hMFPict);
|
|
|
|
} /* ClipSetMFData */
|
|
#endif
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ClipBitmapToDIB - convert CF_BITMAP format to CF_DIB format */
|
|
/****************************************************************************/
|
|
HANDLE DCINTERNAL CClip::ClipBitmapToDIB(HANDLE hData, PDCUINT32 pDataLen)
|
|
{
|
|
BITMAP bmpDetails = {0};
|
|
DWORD buffSize, buffWidth;
|
|
DWORD paletteBytes;
|
|
WORD bpp;
|
|
DWORD numCols;
|
|
int rc;
|
|
HANDLE hDIBitmap = NULL;
|
|
HPDCVOID pDIBitmap = NULL;
|
|
HPDCVOID pBits = NULL;
|
|
PBITMAPINFO pBmpInfo = NULL;
|
|
HDC hDC = NULL;
|
|
DCBOOL allOK = FALSE;
|
|
|
|
DC_BEGIN_FN("CClip::ClipBitmapToDIB");
|
|
|
|
*pDataLen = 0;
|
|
|
|
/************************************************************************/
|
|
/* get the details of the bitmap */
|
|
/************************************************************************/
|
|
if (0 == GetObject(hData, sizeof(bmpDetails), &bmpDetails)) {
|
|
TRC_ERR((TB, _T("Failed to get bitmap details")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRC_NRM((TB, _T("Bitmap details: width %d, height %d, #planes %d, bpp %d"),
|
|
bmpDetails.bmWidth, bmpDetails.bmHeight, bmpDetails.bmPlanes,
|
|
bmpDetails.bmBitsPixel));
|
|
|
|
/************************************************************************/
|
|
/* Space required for bits is */
|
|
/* */
|
|
/* (width * bpp / 8) rounded up to multiple of 4 bytes */
|
|
/* * height */
|
|
/* */
|
|
/************************************************************************/
|
|
bpp = (WORD)(bmpDetails.bmBitsPixel * bmpDetails.bmPlanes);
|
|
buffWidth = ((bmpDetails.bmWidth * bpp) + 7) / 8;
|
|
buffWidth = DC_ROUND_UP_4(buffWidth);
|
|
|
|
buffSize = buffWidth * bmpDetails.bmHeight;
|
|
TRC_DBG((TB, _T("Buffer size %ld (W %ld, H %d)"),
|
|
buffSize, buffWidth, bmpDetails.bmHeight));
|
|
|
|
/************************************************************************/
|
|
/* Now add some space for the bitmapinfo - this includes a color table */
|
|
/************************************************************************/
|
|
numCols = 1 << bpp;
|
|
if (bpp <= 8)
|
|
{
|
|
paletteBytes = numCols * sizeof(RGBQUAD);
|
|
TRC_NRM((TB, _T("%ld colors => %ld palette bytes"), numCols, paletteBytes));
|
|
}
|
|
else
|
|
{
|
|
if (bpp == 24)
|
|
{
|
|
/****************************************************************/
|
|
/* No bitmasks or palette info (compression==BI_RGB) */
|
|
/****************************************************************/
|
|
paletteBytes = 0;
|
|
TRC_NRM((TB, _T("%ld colors => 0 bitfield bytes"), numCols));
|
|
}
|
|
else
|
|
{
|
|
/****************************************************************/
|
|
/* 3 DWORD color masks for >8bpp (compression==BI_BITFIELDS) */
|
|
/****************************************************************/
|
|
paletteBytes = 3 * sizeof(DWORD);
|
|
TRC_NRM((TB, _T("%ld colors => %ld bitfield bytes"), numCols, paletteBytes));
|
|
}
|
|
}
|
|
buffSize += (sizeof(BITMAPINFOHEADER) + paletteBytes);
|
|
TRC_NRM((TB, _T("Buffer size %ld"), buffSize));
|
|
|
|
/************************************************************************/
|
|
/* Allocate memory to hold everything */
|
|
/************************************************************************/
|
|
hDIBitmap = GlobalAlloc(GHND, buffSize);
|
|
if (hDIBitmap == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to alloc %ld bytes"), buffSize));
|
|
DC_QUIT;
|
|
}
|
|
pDIBitmap = GlobalLock(hDIBitmap);
|
|
if (pDIBitmap == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to lock hDIBitmap")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* bmp info is at the start */
|
|
/* space for bits are in the middle somewhere */
|
|
/************************************************************************/
|
|
pBmpInfo = (PBITMAPINFO)pDIBitmap;
|
|
pBits = (HPDCVOID)((HPDCUINT8)pDIBitmap +
|
|
sizeof(BITMAPINFOHEADER) + paletteBytes);
|
|
TRC_NRM((TB, _T("pBmpInfo at %p, pBits at %p"), pBmpInfo, pBits));
|
|
|
|
/************************************************************************/
|
|
/* set up the desired bitmap info */
|
|
/************************************************************************/
|
|
pBmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
pBmpInfo->bmiHeader.biWidth = bmpDetails.bmWidth;
|
|
pBmpInfo->bmiHeader.biHeight = bmpDetails.bmHeight;
|
|
pBmpInfo->bmiHeader.biPlanes = 1;
|
|
pBmpInfo->bmiHeader.biBitCount = bpp;
|
|
if ((bpp <= 8) || (bpp == 24))
|
|
{
|
|
pBmpInfo->bmiHeader.biCompression = BI_RGB;
|
|
}
|
|
else
|
|
{
|
|
pBmpInfo->bmiHeader.biCompression = BI_BITFIELDS;
|
|
}
|
|
pBmpInfo->bmiHeader.biSizeImage = 0;
|
|
pBmpInfo->bmiHeader.biXPelsPerMeter = 0;
|
|
pBmpInfo->bmiHeader.biYPelsPerMeter = 0;
|
|
pBmpInfo->bmiHeader.biClrUsed = 0;
|
|
pBmpInfo->bmiHeader.biClrImportant = 0;
|
|
|
|
/************************************************************************/
|
|
/* get a DC */
|
|
/************************************************************************/
|
|
hDC = GetDC(NULL);
|
|
if (!hDC)
|
|
{
|
|
TRC_SYSTEM_ERROR("GetDC");
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* now get the bits */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("GetDIBits")));
|
|
rc = GetDIBits(hDC, // hdc
|
|
(HBITMAP)hData, // hbm
|
|
0, // nStartScan
|
|
bmpDetails.bmHeight, // nNumScans
|
|
pBits, // pBits
|
|
pBmpInfo, // pbmi
|
|
DIB_RGB_COLORS); // iUsage
|
|
TRC_NRM((TB, _T("GetDIBits returns %d"), rc));
|
|
if (!rc)
|
|
{
|
|
TRC_SYSTEM_ERROR("GetDIBits");
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* All seems to be OK */
|
|
/************************************************************************/
|
|
*pDataLen = buffSize;
|
|
TRC_NRM((TB, _T("All done: data %p, len %ld"), hDIBitmap, *pDataLen));
|
|
allOK = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* Finished with the DC - free it */
|
|
/************************************************************************/
|
|
if (hDC)
|
|
{
|
|
TRC_DBG((TB, _T("Free the DC")));
|
|
ReleaseDC(NULL, hDC);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Free the return buffer if this didn't work */
|
|
/************************************************************************/
|
|
if (!allOK)
|
|
{
|
|
if (pDIBitmap)
|
|
{
|
|
TRC_DBG((TB, _T("Unlock DIBitmap")));
|
|
GlobalUnlock(hDIBitmap);
|
|
}
|
|
if (hDIBitmap)
|
|
{
|
|
TRC_DBG((TB, _T("Free DIBitmap")));
|
|
GlobalFree(hDIBitmap);
|
|
hDIBitmap = NULL;
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(hDIBitmap);
|
|
|
|
} /* ClipBitmapToDIB */
|
|
|
|
DCBOOL DCINTERNAL CClip::ClipIsExcludedFormat(PDCTCHAR formatName)
|
|
{
|
|
DCBOOL rc = FALSE;
|
|
DCINT i;
|
|
|
|
DC_BEGIN_FN("CClip::ClipIsExcludedFormat");
|
|
|
|
/************************************************************************/
|
|
/* check there is a format name - all banned formats have one! */
|
|
/************************************************************************/
|
|
if (*formatName == _T('\0'))
|
|
{
|
|
TRC_ALT((TB, _T("No format name supplied!")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* search the banned format list for the supplied format name */
|
|
/************************************************************************/
|
|
TRC_DBG((TB, _T("Looking at format '%s'"), formatName));
|
|
TRC_DATA_DBG("Format name data", formatName, TS_FORMAT_NAME_LEN);
|
|
|
|
// if File Cut/Copy is on AND Drive Redirection is on, we can handle
|
|
// more formats
|
|
if (_CB.fFileCutCopyOn && _CB.fDrivesRedirected)
|
|
{
|
|
for (i = 0; i < CB_EXCLUDED_FORMAT_COUNT; i++)
|
|
{
|
|
TRC_DBG((TB, _T("comparing with '%s'"), g_excludedFormatList[i]));
|
|
if (DC_WSTRCMP((PDCWCHAR)formatName,
|
|
(PDCWCHAR)g_excludedFormatList[i]) == 0)
|
|
{
|
|
TRC_NRM((TB, _T("Found excluded format '%s'"), formatName));
|
|
rc = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < CB_EXCLUDED_FORMAT_COUNT_NO_RD; i++)
|
|
{
|
|
TRC_DBG((TB, _T("comparing with '%s'"), g_excludedFormatList_NO_RD[i]));
|
|
if (DC_WSTRCMP((PDCWCHAR)formatName,
|
|
(PDCWCHAR)g_excludedFormatList_NO_RD[i]) == 0)
|
|
{
|
|
TRC_NRM((TB, _T("Found excluded format '%s'"), formatName));
|
|
rc = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
|
|
return(rc);
|
|
} /* ClipIsExcludedFormat */
|
|
|
|
#ifndef OS_WINCE
|
|
//
|
|
// ClipCleanTempPath
|
|
// - Returns 0 if successful
|
|
// nonzero if failed
|
|
// - Attempts to wipe the temp directory of TS related files
|
|
//
|
|
int CClip::ClipCleanTempPath()
|
|
{
|
|
int result;
|
|
SHFILEOPSTRUCTW fileOpStructW;
|
|
PRDPDR_DATA prdpdrData = _pVCMgr->GetInitData();
|
|
|
|
#ifndef UNICODE
|
|
#error function assumes unicode
|
|
#endif
|
|
_CB.baseTempDirW[wcslen(_CB.baseTempDirW)] = L'\0' ;
|
|
fileOpStructW.pFrom = _CB.baseTempDirW ;
|
|
fileOpStructW.pTo = NULL ;
|
|
fileOpStructW.hwnd = NULL ;
|
|
fileOpStructW.wFunc = FO_DELETE ;
|
|
fileOpStructW.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI |
|
|
FOF_SIMPLEPROGRESS;
|
|
fileOpStructW.hNameMappings = NULL ;
|
|
|
|
if (prdpdrData->szClipCleanTempDirString[0] != 0) {
|
|
fileOpStructW.lpszProgressTitle = prdpdrData->szClipCleanTempDirString;
|
|
}
|
|
else {
|
|
fileOpStructW.lpszProgressTitle = L"Cleaning temp directory";
|
|
}
|
|
|
|
//
|
|
// Use SHFileOperation instead of SHFileOperationW to ensure
|
|
// it goes through the unicode wrapper. Note SHFileOperationW
|
|
// is not available on 95 so the wrapper dynamically binds to
|
|
// the entry point.
|
|
//
|
|
|
|
result = SHFileOperation(&fileOpStructW) ;
|
|
return result ;
|
|
|
|
}
|
|
|
|
#else
|
|
//We dont want to use the recycle bin on CE
|
|
int CClip::ClipCleanTempPath()
|
|
{
|
|
return (_CB.fFileCutCopyOn) ? DeleteDirectory(_CB.baseTempDirW, FALSE) : ERROR_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// ClipCopyToTempDirectory, ClipCopyToTempDirectoryA, ClipCopyToTempDirectoryW
|
|
// - Arguments:
|
|
// pSrcFiles = buffer containing the names/path of the files to be copied
|
|
// - Returns 0 if successful
|
|
// nonzero if failed
|
|
// - Given a list of file names/paths, this function will attempt to copy them
|
|
// to the temp directory
|
|
//
|
|
int CClip::ClipCopyToTempDirectory(PVOID pSrcFiles, BOOL fWide)
|
|
{
|
|
int result ;
|
|
if (fWide)
|
|
result = ClipCopyToTempDirectoryW(pSrcFiles) ;
|
|
else
|
|
result = ClipCopyToTempDirectoryA(pSrcFiles) ;
|
|
|
|
return result ;
|
|
|
|
}
|
|
|
|
#ifndef OS_WINCE
|
|
int CClip::ClipCopyToTempDirectoryW(PVOID pSrcFiles)
|
|
{
|
|
SHFILEOPSTRUCTW fileOpStructW ;
|
|
HMODULE hmodSH32DLL;
|
|
PRDPDR_DATA prdpdrData = _pVCMgr->GetInitData();
|
|
int result = 1;
|
|
|
|
typedef HRESULT (STDAPICALLTYPE FNSHFileOperationW)(LPSHFILEOPSTRUCT);
|
|
FNSHFileOperationW *pfnSHFileOperationW;
|
|
|
|
// get the handle to shell32.dll library
|
|
hmodSH32DLL = LoadLibrary(TEXT("SHELL32.DLL"));
|
|
|
|
if (hmodSH32DLL != NULL) {
|
|
// get the proc address for SHFileOperation
|
|
pfnSHFileOperationW = (FNSHFileOperationW *)GetProcAddress(hmodSH32DLL, "SHFileOperationW");
|
|
|
|
if (pfnSHFileOperationW != NULL) {
|
|
|
|
_CB.tempDirW[wcslen(_CB.tempDirW)] = L'\0' ;
|
|
fileOpStructW.pFrom = (WCHAR*) pSrcFiles ;
|
|
fileOpStructW.pTo = _CB.tempDirW ;
|
|
fileOpStructW.hwnd = NULL ;
|
|
fileOpStructW.wFunc = _CB.dropEffect ;
|
|
fileOpStructW.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR |
|
|
FOF_SIMPLEPROGRESS | FOF_ALLOWUNDO ;
|
|
fileOpStructW.hNameMappings = NULL ;
|
|
|
|
if (prdpdrData->szClipPasteInfoString[0] != 0) {
|
|
fileOpStructW.lpszProgressTitle = prdpdrData->szClipPasteInfoString;
|
|
}
|
|
else {
|
|
fileOpStructW.lpszProgressTitle = L"Preparing paste information...";
|
|
}
|
|
|
|
//result = SHFileOperationW(&fileOpStructW) ;
|
|
result = (*pfnSHFileOperationW) (&fileOpStructW);
|
|
}
|
|
|
|
FreeLibrary(hmodSH32DLL);
|
|
}
|
|
|
|
return result ;
|
|
}
|
|
#else
|
|
//SHFileOperation on CE does not support copying multiple files
|
|
int CClip::ClipCopyToTempDirectoryW(PVOID pSrcFiles)
|
|
{
|
|
DC_BEGIN_FN("CClip::ClipCopyToTempDirectoryW") ;
|
|
|
|
TRC_ASSERT((pSrcFiles != NULL), (TB, _T("pSrcFiles is NULL")));
|
|
|
|
WCHAR *pFiles = (WCHAR *)pSrcFiles;
|
|
|
|
WCHAR szDest[MAX_PATH+1];
|
|
wcsncpy(szDest, _CB.tempDirW, MAX_PATH);
|
|
int nTempLen = wcslen(szDest);
|
|
|
|
while(*pFiles)
|
|
{
|
|
int nLen = wcslen(pFiles);
|
|
WCHAR *pFile = wcsrchr(pFiles, L'\\');
|
|
if (pFile && nLen < MAX_PATH)
|
|
{
|
|
wcsncat(szDest, pFile, MAX_PATH - nTempLen - 1);
|
|
|
|
DWORD dwAttrib = GetFileAttributes(pFiles);
|
|
if ((dwAttrib != 0xffffffff) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
WIN32_FIND_DATA fd;
|
|
WCHAR szSrc[MAX_PATH];
|
|
|
|
wcscpy(szSrc, pFiles);
|
|
if (!CopyDirectory(szSrc, szDest, &fd))
|
|
{
|
|
TRC_ERR((TB, _T("CopyDirectory from %s to %s failed. GLE=0x%08x"), pFiles, szDest, GetLastError())) ;
|
|
return GetLastError();
|
|
}
|
|
}
|
|
else if (!CopyFile(pFiles, szDest, FALSE))
|
|
{
|
|
TRC_ERR((TB, _T("CopyFile from %s to %s failed. GLE=0x%08x"), pFiles, szDest, GetLastError())) ;
|
|
return GetLastError();
|
|
}
|
|
szDest[nTempLen] = L'\0';
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Invalid filename : %s"), pFiles)) ;
|
|
}
|
|
pFiles += nLen + 1;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int CClip::ClipCopyToTempDirectoryA(PVOID pSrcFiles)
|
|
{
|
|
#ifndef OS_WINCE
|
|
SHFILEOPSTRUCTA fileOpStructA ;
|
|
int result ;
|
|
|
|
_CB.tempDirA[strlen(_CB.tempDirA)] = '\0' ;
|
|
fileOpStructA.pFrom = (char*) pSrcFiles ;
|
|
fileOpStructA.pTo = _CB.tempDirA ;
|
|
fileOpStructA.hwnd = NULL ;
|
|
fileOpStructA.wFunc = _CB.dropEffect ;
|
|
fileOpStructA.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR |
|
|
FOF_SIMPLEPROGRESS | FOF_ALLOWUNDO ;
|
|
fileOpStructA.hNameMappings = NULL ;
|
|
fileOpStructA.lpszProgressTitle = _CB.pasteInfoA;
|
|
|
|
result = SHFileOperationA(&fileOpStructA) ;
|
|
return result ;
|
|
#else
|
|
DC_BEGIN_FN("CClip::ClipConvertToTempPathA") ;
|
|
TRC_ASSERT((FALSE), (TB, _T("CE doesnt support ClipConvertToTempPathA")));
|
|
DC_END_FN() ;
|
|
return E_FAIL;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// ClipConvertToTempPath, ClipConvertToTempPathA, ClipConvertToTempPathW
|
|
// - Arguments:
|
|
// pOldData = Buffer containing the original file path
|
|
// pData = Buffer receiving the new file path
|
|
// fWide = Wide or Ansi characters
|
|
// - Returns S_OK if pOldData was a network path
|
|
// S_FALSE if pOldData was not a network path
|
|
// E_FAIL if it failed
|
|
// - Given a unc file path, this function will strip out the old path, and
|
|
// prepend a path to the client's TS temp directory
|
|
//
|
|
HRESULT CClip::ClipConvertToTempPath(PVOID pOldData, PVOID pData, ULONG cbData, BOOL fWide)
|
|
{
|
|
HRESULT result ;
|
|
DC_BEGIN_FN("CClip::ClipConvertToTempPath") ;
|
|
if (!pOldData)
|
|
{
|
|
TRC_ERR((TB, _T("Original string pointer is NULL"))) ;
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
if (!pData)
|
|
{
|
|
TRC_ERR((TB, _T("Destination string pointer is NULL"))) ;
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
if (fWide) {
|
|
result = ClipConvertToTempPathW(pOldData, pData, cbData / sizeof(WCHAR)) ;
|
|
} else {
|
|
result = ClipConvertToTempPathA(pOldData, pData, cbData) ;
|
|
}
|
|
DC_EXIT_POINT:
|
|
return result ;
|
|
DC_END_FN() ;
|
|
}
|
|
|
|
HRESULT CClip::ClipConvertToTempPathW(PVOID pOldData, PVOID pData, ULONG cchData)
|
|
{
|
|
WCHAR* filePath ;
|
|
#ifndef OS_WINCE
|
|
WCHAR* driveLetter ;
|
|
WCHAR* uncPath ;
|
|
WCHAR* prependText ;
|
|
DWORD charSize ;
|
|
DWORD driveLetterLength ;
|
|
#endif
|
|
HRESULT hr;
|
|
|
|
DC_BEGIN_FN("CClip::ClipConvertToTempPathW") ;
|
|
|
|
// if this is a UNC path beginning with a "\\"
|
|
if (((WCHAR*)pOldData)[0] == L'\\' &&
|
|
((WCHAR*)pOldData)[1] == L'\\')
|
|
{
|
|
// prepend the new file path with the temp directory
|
|
hr = StringCchCopyW((WCHAR*) pData, cchData, _CB.tempDirW);
|
|
if (SUCCEEDED(hr)) {
|
|
filePath = wcsrchr((WCHAR*) pOldData, L'\\');
|
|
hr = StringCchCatW((WCHAR*) pData, cchData, filePath);
|
|
}
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB,_T("Failed to copy and cat string: 0x%x"),hr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_NRM((TB, _T("Not a UNC path"))) ;
|
|
hr = StringCchCopyW((WCHAR*) pData, cchData, (WCHAR*) pOldData);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
|
|
#ifdef OS_WINCE
|
|
//Send it as "Files:" to the server
|
|
if( (((WCHAR*)pData)[0] == L'\\') && ((wcslen((WCHAR *)pData) + sizeof(CEROOTDIRNAME)/sizeof(WCHAR)) < MAX_PATH) )
|
|
{
|
|
WCHAR szFile[MAX_PATH];
|
|
wcscpy(szFile, (WCHAR *)pData);
|
|
wcscpy((WCHAR *)pData, CEROOTDIRNAME);
|
|
wcscat((WCHAR *)pData, szFile);
|
|
}
|
|
else
|
|
{
|
|
DC_END_FN() ;
|
|
return S_FALSE;
|
|
}
|
|
#endif
|
|
|
|
DC_END_FN() ;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CClip::ClipConvertToTempPathA(PVOID pOldData, PVOID pData, ULONG cchData)
|
|
{
|
|
#ifndef OS_WINCE
|
|
char* filePath ;
|
|
char* driveLetter ;
|
|
char* uncPath ;
|
|
char* prependText ;
|
|
DWORD charSize ;
|
|
DWORD driveLetterLength ;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
DC_BEGIN_FN("CClip::ClipConvertToTempPathA") ;
|
|
|
|
charSize = sizeof(char) ;
|
|
|
|
// if this is a UNC path beginning with a "\\"
|
|
if (((char*) pOldData)[0] == '\\' &&
|
|
((char*) pOldData)[1] == '\\')
|
|
{
|
|
// prepend the new file path with the temp directory
|
|
hr = StringCchCopyA((char*) pData, cchData, _CB.tempDirA);
|
|
if (SUCCEEDED(hr)) {
|
|
filePath = strrchr((char*) pOldData, '\\');
|
|
if (filePath) {
|
|
hr = StringCchCatA((char*) pData, cchData, filePath);
|
|
} else {
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB,_T("Failed to copy and cat string: 0x%x"),hr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_NRM((TB, _T("Not a UNC path"))) ;
|
|
hr = StringCchCopyA((char*) pData, cchData, (char*) pOldData);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
DC_END_FN() ;
|
|
|
|
return hr;
|
|
#else
|
|
DC_BEGIN_FN("CClip::ClipConvertToTempPathA") ;
|
|
TRC_ASSERT((FALSE), (TB, _T("CE doesnt support ClipConvertToTempPathA")));
|
|
DC_END_FN() ;
|
|
return S_FALSE ;
|
|
#endif
|
|
}
|
|
|
|
#ifndef OS_WINCE
|
|
//
|
|
// ClipGetNewFilePathLength
|
|
// - Arguments:
|
|
// pData = Buffer containing a filepath
|
|
// fWide = Wide or Ansi (TRUE if wide, FALSE if ansi)
|
|
// - Returns new size of the drop file
|
|
// 0 if it fails
|
|
// - Given a UNC file path, this returns the new size required
|
|
// if the directory structure is removed, and is replaced by
|
|
// the temp directory path
|
|
// - Otherwise, if it doesn't explicitly fail, it returns the
|
|
// old length
|
|
//
|
|
UINT CClip::ClipGetNewFilePathLength(PVOID pData, BOOL fWide)
|
|
{
|
|
UINT result ;
|
|
DC_BEGIN_FN("CClip::ClipGetNewFilePathLength") ;
|
|
if (!pData)
|
|
{
|
|
TRC_ERR((TB, _T("Filename is NULL"))) ;
|
|
result = 0 ;
|
|
}
|
|
if (fWide)
|
|
result = ClipGetNewFilePathLengthW((WCHAR*)pData) ;
|
|
else
|
|
result = ClipGetNewFilePathLengthA((char*)pData) ;
|
|
DC_EXIT_POINT:
|
|
DC_END_FN() ;
|
|
|
|
return result ;
|
|
}
|
|
|
|
UINT CClip::ClipGetNewFilePathLengthW(WCHAR* wszOldFilepath)
|
|
{
|
|
UINT oldLength = wcslen(wszOldFilepath) ;
|
|
UINT newLength = oldLength ;
|
|
UINT remainingLength = oldLength ;
|
|
byte charSize = sizeof(WCHAR) ;
|
|
DC_BEGIN_FN("CClip::ClipGetNewFilePathLengthW") ;
|
|
|
|
// if the old filename didn't even have space for "c:\" (with NULL),
|
|
// then its probably invalid
|
|
if (4 > oldLength)
|
|
{
|
|
newLength = 0 ;
|
|
DC_QUIT ;
|
|
}
|
|
if ((L'\\' ==wszOldFilepath[0]) && (L'\\' ==wszOldFilepath[1]))
|
|
{
|
|
while ((0 != remainingLength) && (L'\\' != wszOldFilepath[remainingLength]))
|
|
{
|
|
remainingLength-- ;
|
|
}
|
|
|
|
// Add the length of the temp directory path, and subtract the
|
|
// path preceeding the filename ("path\filename" -> "\filename")
|
|
// (\\server\sharename\path\morepath\filename
|
|
newLength = oldLength - remainingLength + wcslen(_CB.tempDirW) ;
|
|
}
|
|
DC_EXIT_POINT:
|
|
DC_END_FN() ;
|
|
return (newLength + 1) * charSize ; // +1 is for the NULL terminator
|
|
}
|
|
|
|
UINT CClip::ClipGetNewFilePathLengthA(char* szOldFilepath)
|
|
{
|
|
UINT oldLength = strlen(szOldFilepath) ;
|
|
UINT newLength = oldLength ;
|
|
UINT remainingLength = oldLength ;
|
|
byte charSize = sizeof(char) ;
|
|
DC_BEGIN_FN("CClip::ClipGetNewFilePathLengthA") ;
|
|
|
|
// if the old filename didn't even have space for "c:\" (with NULL),
|
|
// then it's probably invalid
|
|
if (4 > oldLength)
|
|
{
|
|
newLength = 0 ;
|
|
DC_QUIT ;
|
|
}
|
|
if (('\\' == szOldFilepath[0]) && ('\\' == szOldFilepath[1]))
|
|
{
|
|
while ((0 != remainingLength) && ('\\' != szOldFilepath[remainingLength]))
|
|
{
|
|
remainingLength-- ;
|
|
}
|
|
|
|
// Add the length of the temp directory path, and subtract the
|
|
// path preceeding the filename ("path\filename" -> "\filename")
|
|
// (\\server\sharename\path\morepath\filename
|
|
newLength = oldLength - remainingLength + strlen(_CB.tempDirA) ;
|
|
}
|
|
DC_EXIT_POINT:
|
|
DC_END_FN() ;
|
|
return (newLength + 1) * charSize ; // +1 is for the NULL terminator
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// ClipGetNewDropfilesSize
|
|
// - Arguments:
|
|
// pData = Buffer containing a DROPFILES struct
|
|
// oldSize = The size of the DROPFILES struct
|
|
// fWide = Wide or Ansi (TRUE if wide, FALSE if ansi)
|
|
// - Returns new size of the drop file
|
|
// 0 if it fails
|
|
// - Given a set of paths, this function will return the new
|
|
// size required by the DROPFILES struct, if the UNC paths
|
|
// are replaced by the temp directory path
|
|
//
|
|
ULONG CClip::ClipGetNewDropfilesSize(PVOID pData, ULONG oldSize, BOOL fWide)
|
|
{
|
|
DC_BEGIN_FN("CClip::TS_GetNewDropfilesSize") ;
|
|
if (fWide)
|
|
return ClipGetNewDropfilesSizeW(pData, oldSize) ;
|
|
else
|
|
return ClipGetNewDropfilesSizeA(pData, oldSize) ;
|
|
DC_END_FN() ;
|
|
}
|
|
ULONG CClip::ClipGetNewDropfilesSizeW(PVOID pData, ULONG oldSize)
|
|
{
|
|
ULONG newSize = oldSize ;
|
|
#ifndef OS_WINCE
|
|
WCHAR* filenameW ;
|
|
#endif
|
|
WCHAR* fullFilePathW ;
|
|
byte charSize ;
|
|
|
|
DC_BEGIN_FN("CClip::TS_GetNewDropfilesSizeW") ;
|
|
charSize = sizeof(WCHAR) ;
|
|
if (!pData)
|
|
{
|
|
TRC_ERR((TB,_T("Pointer to dropfile is NULL"))) ;
|
|
return 0 ;
|
|
}
|
|
|
|
#ifdef OS_WINCE
|
|
newSize = 0;
|
|
#endif
|
|
// The start of the first filename
|
|
fullFilePathW = (WCHAR*) ((byte*) pData + ((DROPFILES*) pData)->pFiles) ;
|
|
|
|
while (L'\0' != fullFilePathW[0])
|
|
{
|
|
#ifndef OS_WINCE
|
|
// If it is a UNC path
|
|
if (fullFilePathW[0] == L'\\' &&
|
|
fullFilePathW[1] == L'\\')
|
|
{
|
|
filenameW = wcsrchr(fullFilePathW, L'\\');
|
|
|
|
// Add the length of the temp directory path, and subtract the
|
|
// path preceeding the filename ("path\filename" -> "\filename")
|
|
// (\\server\sharename\path\morepath\filename
|
|
newSize += (wcslen(_CB.tempDirW) - (filenameW - fullFilePathW) )
|
|
* charSize ;
|
|
}
|
|
#else
|
|
newSize++;
|
|
#endif
|
|
fullFilePathW = fullFilePathW + (wcslen(fullFilePathW) + 1) ;
|
|
}
|
|
|
|
#ifdef OS_WINCE
|
|
newSize = oldSize + (newSize*sizeof(CEROOTDIRNAME)); //for the "Files:" (the sizeof operator includes space for the extra null)
|
|
#else
|
|
// Add space for extra null character
|
|
newSize += charSize ;
|
|
#endif
|
|
DC_END_FN() ;
|
|
return newSize ;
|
|
}
|
|
|
|
ULONG CClip::ClipGetNewDropfilesSizeA(PVOID pData, ULONG oldSize)
|
|
{
|
|
#ifndef OS_WINCE
|
|
ULONG newSize = oldSize ;
|
|
char* filename ;
|
|
char* fullFilePath ;
|
|
byte charSize ;
|
|
|
|
DC_BEGIN_FN("CClip::TS_GetNewDropfilesSizeW") ;
|
|
charSize = sizeof(char) ;
|
|
if (!pData)
|
|
{
|
|
TRC_ERR((TB,_T("Pointer to dropfile is NULL"))) ;
|
|
return 0 ;
|
|
}
|
|
|
|
// The start of the first filename
|
|
fullFilePath = (char*) ((byte*) pData + ((DROPFILES*) pData)->pFiles) ;
|
|
|
|
while ('\0' != fullFilePath[0])
|
|
{
|
|
// If it is a UNC path
|
|
if (fullFilePath[0] == '\\' &&
|
|
fullFilePath[1] == '\\')
|
|
{
|
|
filename = strrchr(fullFilePath, '\\');
|
|
|
|
// Add the length of the temp directory path, and subtract
|
|
// the path preceeding the filename itself, excluding the backlash
|
|
// (\\server\sharename\path\morepath\filename
|
|
newSize += (strlen(_CB.tempDirA) - (filename - fullFilePath) )
|
|
* charSize ;
|
|
}
|
|
fullFilePath = fullFilePath + (strlen(fullFilePath) + 1) ;
|
|
}
|
|
|
|
// Add space for extra null character
|
|
newSize += charSize ;
|
|
DC_END_FN() ;
|
|
return newSize ;
|
|
#else
|
|
DC_BEGIN_FN("CClip::TS_GetNewDropfilesSizeA") ;
|
|
TRC_ASSERT((FALSE), (TB, _T("CE doesnt support ClipGetNewDropfilesSizeA")));
|
|
DC_END_FN() ;
|
|
return 0 ;
|
|
#endif
|
|
}
|
|
|
|
|
|
//
|
|
// ClipSetAndSendTempDirectory
|
|
// - Returns TRUE if temp directory was successfully set and sent
|
|
// FALSE otherwise (no file redirection, or failed sending path)
|
|
// - Sets the Temp paths for the client, and sends the path
|
|
// in wide characters to the Server
|
|
//
|
|
|
|
BOOL CClip::ClipSetAndSendTempDirectory(void)
|
|
{
|
|
UINT wResult ;
|
|
BOOL fSuccess ;
|
|
PTS_CLIP_PDU pClipPDU ;
|
|
DCINT32 pduLen ;
|
|
HRESULT hr;
|
|
|
|
DC_BEGIN_FN("CClip::ClipSetAndSendTempDirectory") ;
|
|
// if we don't have drive redirection, then don't bother sending a path
|
|
if (!_CB.fDrivesRedirected)
|
|
{
|
|
TRC_ALT((TB, _T("File redirection is off; don't set temp path."))) ;
|
|
fSuccess = FALSE ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
#ifdef OS_WINCE
|
|
if ((fSuccess = InitializeCeShell(_CB.viewerWindow)) == FALSE)
|
|
{
|
|
TRC_ALT((TB, _T("Failed to initialize ceshell. File copy through clipboard disabled."))) ;
|
|
DC_QUIT ;
|
|
}
|
|
#endif
|
|
|
|
#ifndef OS_WINCE
|
|
if (0 == GetTempPathA(MAX_PATH, _CB.baseTempDirA))
|
|
{
|
|
TRC_ERR((TB, _T("Failed getting path to temp directory."))) ;
|
|
fSuccess = FALSE ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
// Each session gets it own temp directory
|
|
if (0 == GetTempFileNameA(_CB.baseTempDirA, "_TS", 0, _CB.tempDirA)) {
|
|
TRC_ERR((TB, _T("Getting temp file name failed; GetLastError=%u"),
|
|
GetLastError()));
|
|
fSuccess = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
DeleteFileA(_CB.tempDirA) ;
|
|
if (0 == CreateDirectoryA(_CB.tempDirA, NULL)) {
|
|
TRC_ERR((TB, _T("Creating temp directory failed; GetLastError=%u"),
|
|
GetLastError()));
|
|
fSuccess = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
hr = StringCbCopyA(_CB.baseTempDirA,
|
|
sizeof(_CB.baseTempDirA),
|
|
_CB.tempDirA);
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB,_T("Failed to cpy tempdir to basetempdir: 0x%x"),hr));
|
|
fSuccess = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
|
|
// We always send MAX_PATH*sizeof(WCHAR) byte for simplicity
|
|
pduLen = MAX_PATH*sizeof(WCHAR) + sizeof(TS_CLIP_PDU);
|
|
// GetACP always returns a valid value
|
|
if (0 == MultiByteToWideChar(GetACP(), MB_ERR_INVALID_CHARS,
|
|
_CB.baseTempDirA, -1, _CB.baseTempDirW,
|
|
sizeof(_CB.baseTempDirW)/(sizeof(_CB.baseTempDirW[0])) - 1))
|
|
{
|
|
TRC_ERR((TB, _T("Failed conversion to wide char; error %d"),
|
|
GetLastError())) ;
|
|
fSuccess = FALSE ;
|
|
DC_QUIT ;
|
|
}
|
|
#else
|
|
if (0 == GetTempPathW(MAX_PATH, _CB.baseTempDirW))
|
|
{
|
|
TRC_ERR((TB, _T("Failed getting path to temp directory."))) ;
|
|
fSuccess = FALSE ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
// Each session gets it own temp directory
|
|
if (0 == GetTempFileNameW(_CB.baseTempDirW, L"_TS", 0, _CB.tempDirW, MAX_PATH-(sizeof(CEROOTDIRNAME)/sizeof(WCHAR)) ) {
|
|
TRC_ERR((TB, _T("Getting temp file name failed; GetLastError=%u"),
|
|
GetLastError()));
|
|
fSuccess = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
DeleteFile(_CB.tempDirW) ;
|
|
if (0 == CreateDirectory(_CB.tempDirW, NULL)) {
|
|
TRC_ERR((TB, _T("Creating temp directory failed; GetLastError=%u"),
|
|
GetLastError()));
|
|
fSuccess = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
wcscpy(_CB.baseTempDirW, _CB.tempDirW) ;
|
|
pduLen = (MAX_PATH*sizeof(WCHAR)) + sizeof(TS_CLIP_PDU);
|
|
#endif
|
|
|
|
pClipPDU = (PTS_CLIP_PDU) ClipAlloc(pduLen) ;
|
|
if (!pClipPDU)
|
|
{
|
|
TRC_ERR((TB,_T("Unable to allocate %d bytes"), pduLen));
|
|
fSuccess = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Fill in the PDU ; we send a packet of size MAX_PATH for simplicity
|
|
DC_MEMSET(pClipPDU, 0, sizeof(TS_CLIP_PDU));
|
|
pClipPDU->msgType = TS_CB_TEMP_DIRECTORY;
|
|
pClipPDU->dataLen = MAX_PATH*sizeof(WCHAR) ;
|
|
|
|
TRC_DBG((TB, _T("Copying all the data")));
|
|
#ifndef OS_WINCE
|
|
ClipMemcpy(pClipPDU->data, _CB.baseTempDirW, pClipPDU->dataLen) ;
|
|
#else
|
|
TSUINT8 *pData;
|
|
int nDSize;
|
|
|
|
pData = pClipPDU->data;
|
|
nDSize = sizeof(CEROOTDIRNAME) - sizeof(WCHAR);
|
|
ClipMemcpy(pData, CEROOTDIRNAME, nDSize) ;
|
|
pData += nDSize;
|
|
ClipMemcpy(pData, _CB.baseTempDirW, pClipPDU->dataLen - nDSize) ;
|
|
#endif
|
|
|
|
TRC_NRM((TB, _T("Sending temp directory path.")));
|
|
wResult = _CB.channelEP.pVirtualChannelWriteEx
|
|
(_CB.initHandle, _CB.channelHandle, pClipPDU, pduLen, (LPVOID)pClipPDU);
|
|
if (CHANNEL_RC_OK != wResult)
|
|
{
|
|
TRC_ERR((TB, _T("Failed sending temp directory 0x%08x"),
|
|
GetLastError())) ;
|
|
ClipFreeBuf((PDCUINT8)pClipPDU);
|
|
fSuccess = FALSE ;
|
|
DC_QUIT ;
|
|
}
|
|
fSuccess = TRUE ;
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN() ;
|
|
return fSuccess ;
|
|
}
|
|
/****************************************************************************/
|
|
/* ClipOnFormatList - we got a list of formats from the server */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CClip::ClipOnFormatList(PTS_CLIP_PDU pClipPDU)
|
|
{
|
|
DCUINT16 response = TS_CB_RESPONSE_OK;
|
|
DCUINT numFormats;
|
|
TS_CLIP_FORMAT UNALIGNED* fmtList;
|
|
DCUINT i;
|
|
DCTCHAR formatName[TS_FORMAT_NAME_LEN + 1] = { 0 };
|
|
PTS_CLIP_PDU pClipRsp;
|
|
#ifndef OS_WINCE
|
|
DCBOOL fSuccess;
|
|
LPFORMATETC pFormatEtc ;
|
|
#endif
|
|
LPDATAOBJECT pIDataObject = NULL ;
|
|
HRESULT hr ;
|
|
TS_CLIP_PDU UNALIGNED* pUlClipPDU = (TS_CLIP_PDU UNALIGNED*)pClipPDU;
|
|
#ifdef OS_WINCE
|
|
DCUINT uRtf1 = 0xffffffff, uRtf2 = 0xffffffff;
|
|
#endif
|
|
|
|
DC_BEGIN_FN("CClip::ClipOnFormatList");
|
|
|
|
if (_pClipData == NULL) {
|
|
TRC_ALT((TB, _T("The clipData is NULL, we just bail")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Do state checks */
|
|
/************************************************************************/
|
|
CB_CHECK_STATE(CB_EVENT_FORMAT_LIST);
|
|
if (_CB.state == CB_STATE_PENDING_FORMAT_LIST_RSP)
|
|
{
|
|
/********************************************************************/
|
|
/* we've just sent a format list to the server. We always win, so */
|
|
/* we just ignore this message. */
|
|
/********************************************************************/
|
|
TRC_ALT((TB, _T("Format list race - we win so ignoring")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Sanity check */
|
|
/************************************************************************/
|
|
if (_CB.clipOpen)
|
|
{
|
|
TRC_ALT((TB, _T("Clipboard is still open")));
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* empty the client/server mapping table */
|
|
/****************************************************************/
|
|
DC_MEMSET(_CB.idMap, 0, sizeof(_CB.idMap));
|
|
|
|
/****************************************************************/
|
|
/* work out how many formats we got */
|
|
/****************************************************************/
|
|
numFormats = (pUlClipPDU->dataLen) / sizeof(TS_CLIP_FORMAT);
|
|
TRC_NRM((TB, _T("PDU contains %d formats"), numFormats));
|
|
hr = _pClipData->SetNumFormats(numFormats) ;
|
|
if (SUCCEEDED(hr)) {
|
|
hr = _pClipData->QueryInterface(IID_IDataObject, (PPVOID) &pIDataObject) ;
|
|
}
|
|
#ifdef OS_WINCE
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (OpenClipboard(_CB.dataWindow))
|
|
{
|
|
if (EmptyClipboard())
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
CloseClipboard();
|
|
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WINDOWS, GetLastError());
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WINDOWS, GetLastError());
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
#endif
|
|
if (SUCCEEDED(hr)) {
|
|
TRC_ASSERT((numFormats <= CB_MAX_FORMATS),
|
|
(TB, _T("Format list contains more than %d formats"),
|
|
CB_MAX_FORMATS));
|
|
|
|
/****************************************************************/
|
|
/* and register them */
|
|
/****************************************************************/
|
|
fmtList = (TS_CLIP_FORMAT UNALIGNED*)pUlClipPDU->data;
|
|
for (i = 0; i < numFormats; i++)
|
|
{
|
|
TRC_DBG((TB, _T("format number %d, server id %d"),
|
|
i, fmtList[i].formatID));
|
|
|
|
//
|
|
// If file copy and paste is disabled, we don't accept HDROPs.
|
|
//
|
|
|
|
if (fmtList[i].formatID == CF_HDROP && _CB.fFileCutCopyOn == FALSE) {
|
|
continue;
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* If we got a name... */
|
|
/****************************************************************/
|
|
if (fmtList[i].formatName[0] != 0)
|
|
{
|
|
/************************************************************/
|
|
/* clear out any garbage */
|
|
/************************************************************/
|
|
#ifndef OS_WINCE
|
|
DC_MEMSET(formatName, 0, TS_FORMAT_NAME_LEN);
|
|
#else
|
|
DC_MEMSET(formatName, 0, sizeof(formatName));
|
|
#endif
|
|
//
|
|
// fmtList[i].formatName is not NULL terminated so explicity
|
|
// do a byte count copy
|
|
//
|
|
StringCbCopy(formatName, TS_FORMAT_NAME_LEN + sizeof(TCHAR),
|
|
(PDCTCHAR)(fmtList[i].formatName));
|
|
|
|
if (ClipIsExcludedFormat(formatName))
|
|
{
|
|
TRC_NRM((TB, _T("Dropped format '%s'"), formatName));
|
|
continue;
|
|
}
|
|
/************************************************************/
|
|
/* name is sorted */
|
|
/************************************************************/
|
|
TRC_NRM((TB, _T("Got name '%s'"), formatName));
|
|
|
|
}
|
|
else
|
|
{
|
|
DC_MEMSET(formatName, 0, TS_FORMAT_NAME_LEN);
|
|
}
|
|
/****************************************************************/
|
|
/* store the server id */
|
|
/****************************************************************/
|
|
_CB.idMap[i].serverID = fmtList[i].formatID;
|
|
TRC_NRM((TB, _T("server id %d"), _CB.idMap[i].serverID));
|
|
|
|
/****************************************************************/
|
|
/* get local name (if needed) */
|
|
/****************************************************************/
|
|
if (formatName[0] != 0)
|
|
{
|
|
#ifdef OS_WINCE
|
|
//The protocol limits clipboard format names to 16 widechars. Thus it becomes impossible
|
|
//to distinguish between "Rich Text Format" and "Rich Text Format Without Objects"
|
|
//This should be removed once the protocol is fixed in Longhorn
|
|
if (0 == DC_TSTRNCMP(formatName, CFSTR_RTF, (sizeof(CFSTR_RTF)/sizeof(TCHAR)) - 1))
|
|
{
|
|
if (uRtf1 == 0xffffffff)
|
|
uRtf1 = i;
|
|
else
|
|
uRtf2 = i;
|
|
continue;
|
|
}
|
|
else
|
|
#endif
|
|
_CB.idMap[i].clientID = RegisterClipboardFormat(formatName);
|
|
}
|
|
else
|
|
{
|
|
/************************************************************/
|
|
/* it's a predefined format so we can just use the ID */
|
|
/************************************************************/
|
|
_CB.idMap[i].clientID = _CB.idMap[i].serverID;
|
|
}
|
|
#ifdef OS_WINCE
|
|
if (_CB.idMap[i].serverID == CF_HDROP)
|
|
_CB.idMap[i].clientID = gfmtShellPidlArray;
|
|
#endif
|
|
|
|
/************************************************************/
|
|
/* and add the format to the local CB */
|
|
/************************************************************/
|
|
TRC_DBG((TB, _T("Adding format '%s', server ID %d, client ID %d"),
|
|
fmtList[i].formatName,
|
|
_CB.idMap[i].serverID,
|
|
_CB.idMap[i].clientID));
|
|
|
|
|
|
if (0 != _CB.idMap[i].clientID) {
|
|
#ifndef OS_WINCE
|
|
pFormatEtc = new FORMATETC ;
|
|
|
|
if (pFormatEtc) {
|
|
|
|
pFormatEtc->cfFormat = (CLIPFORMAT) _CB.idMap[i].clientID ;
|
|
pFormatEtc->dwAspect = DVASPECT_CONTENT ;
|
|
pFormatEtc->ptd = NULL ;
|
|
pFormatEtc->lindex = -1 ;
|
|
pFormatEtc->tymed = TYMED_HGLOBAL ;
|
|
|
|
// Need to set the clipboard state before SetData.
|
|
CB_SET_STATE(CB_STATE_LOCAL_CB_OWNER, CB_EVENT_FORMAT_LIST);
|
|
pIDataObject->SetData(pFormatEtc, NULL, TRUE) ;
|
|
delete pFormatEtc;
|
|
}
|
|
|
|
#else
|
|
CB_SET_STATE(CB_STATE_LOCAL_CB_OWNER, CB_EVENT_FORMAT_LIST);
|
|
SetClipboardData((CLIPFORMAT) _CB.idMap[i].clientID, NULL);
|
|
#endif
|
|
}
|
|
else
|
|
TRC_NRM((TB,_T("Invalid format dropped"))) ;
|
|
}
|
|
#ifdef OS_WINCE
|
|
//we will choose the lower format id as belonging to "Rich Text Format"
|
|
//This is our best guess and it seems to work
|
|
ClipFixupRichTextFormats(uRtf1, uRtf2);
|
|
#endif
|
|
|
|
#ifndef OS_WINCE
|
|
hr = OleSetClipboard(pIDataObject) ;
|
|
#else
|
|
EnterCriticalSection(&gcsDataObj);
|
|
|
|
if (gpDataObj)
|
|
gpDataObj->Release();
|
|
pIDataObject->AddRef();
|
|
gpDataObj = pIDataObject;
|
|
|
|
LeaveCriticalSection(&gcsDataObj);
|
|
hr = S_OK;
|
|
CloseClipboard();
|
|
#endif
|
|
if (pIDataObject)
|
|
pIDataObject->Release();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
response = TS_CB_RESPONSE_OK;
|
|
_CB.clipOpen = FALSE ;
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("OleSetClipboard failed, error = 0x%08x"), hr)) ;
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
_CB.clipOpen = FALSE ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pIDataObject)
|
|
pIDataObject->Release();
|
|
TRC_ERR((TB, _T("Error getting pointer to an IDataObject"))) ;
|
|
pIDataObject = NULL ;
|
|
response = TS_CB_RESPONSE_FAIL ;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Now build the response */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Get perm TX buffer")));
|
|
pClipRsp = ClipGetPermBuf();
|
|
|
|
/************************************************************************/
|
|
/* and now the specific bits */
|
|
/************************************************************************/
|
|
pClipRsp->msgType = TS_CB_FORMAT_LIST_RESPONSE;
|
|
pClipRsp->msgFlags = response;
|
|
pClipRsp->dataLen = 0;
|
|
|
|
/************************************************************************/
|
|
/* finally we send it */
|
|
/************************************************************************/
|
|
if (_CB.channelEP.pVirtualChannelWriteEx
|
|
(_CB.initHandle, _CB.channelHandle, pClipRsp, sizeof(TS_CLIP_PDU), (LPVOID)pClipRsp)
|
|
!= CHANNEL_RC_OK)
|
|
{
|
|
TRC_ERR((TB, _T("Failed VC write: setting clip data to NULL")));
|
|
ClipFreeBuf((PDCUINT8)pClipRsp);
|
|
response = TS_CB_RESPONSE_FAIL ;
|
|
}
|
|
/************************************************************************/
|
|
/* Update the state according to how we got on */
|
|
/************************************************************************/
|
|
if (response == TS_CB_RESPONSE_OK)
|
|
{
|
|
CB_SET_STATE(CB_STATE_LOCAL_CB_OWNER, CB_EVENT_FORMAT_LIST);
|
|
}
|
|
else
|
|
{
|
|
CB_SET_STATE(CB_STATE_ENABLED, CB_EVENT_FORMAT_LIST);
|
|
}
|
|
DC_EXIT_POINT:
|
|
|
|
DC_END_FN();
|
|
return;
|
|
} /* ClipOnFormatList */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ClipOnFormatListResponse - got the format list response */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CClip::ClipOnFormatListResponse(PTS_CLIP_PDU pClipPDU)
|
|
{
|
|
DC_BEGIN_FN("CClip::ClipOnFormatListResponse");
|
|
|
|
CB_CHECK_STATE(CB_EVENT_FORMAT_LIST_RSP);
|
|
|
|
/************************************************************************/
|
|
/* if the response is OK... */
|
|
/************************************************************************/
|
|
if (pClipPDU->msgFlags & TS_CB_RESPONSE_OK)
|
|
{
|
|
/********************************************************************/
|
|
/* we are now the shared CB owner */
|
|
/********************************************************************/
|
|
TRC_NRM((TB, _T("Got OK fmt list rsp")));
|
|
CB_SET_STATE(CB_STATE_SHARED_CB_OWNER, CB_EVENT_FORMAT_LIST_RSP);
|
|
}
|
|
else
|
|
{
|
|
/********************************************************************/
|
|
/* nothing specific to do */
|
|
/********************************************************************/
|
|
TRC_ALT((TB, _T("Got fmt list rsp failed")));
|
|
CB_SET_STATE(CB_STATE_ENABLED, CB_EVENT_FORMAT_LIST_RSP);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* There may have been another update while we were waiting - deal with */
|
|
/* it by faking an update here */
|
|
/************************************************************************/
|
|
if (_CB.moreToDo == TRUE)
|
|
{
|
|
TRC_ALT((TB, _T("More to do on list rsp")));
|
|
_CB.moreToDo = FALSE;
|
|
ClipDrawClipboard(FALSE);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return;
|
|
} /* ClipOnFormatListResponse */
|
|
|
|
|
|
/****************************************************************************/
|
|
// ClipOnFormatRequest
|
|
// - Server wants a format
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CClip::ClipOnFormatRequest(PTS_CLIP_PDU pClipPDU)
|
|
{
|
|
DCUINT32 formatID;
|
|
DCUINT32 dataLen = 0;
|
|
DCUINT numEntries;
|
|
DCUINT16 dwEntries;
|
|
|
|
HANDLE hData = NULL;
|
|
HANDLE hNewData = NULL;
|
|
HPDCVOID pData = NULL;
|
|
DROPFILES* pDropFiles ;
|
|
#ifndef OS_WINCE
|
|
DROPFILES tempDropfile ;
|
|
#endif
|
|
HPDCVOID pNewData = NULL;
|
|
DCUINT16 response = TS_CB_RESPONSE_OK;
|
|
PTS_CLIP_PDU pClipRsp = NULL;
|
|
PTS_CLIP_PDU pClipNew;
|
|
DCUINT32 pduLen;
|
|
BOOL fDrivePath ;
|
|
BOOL fWide ;
|
|
byte charSize ;
|
|
ULONG newSize, oldSize ;
|
|
HPDCVOID pOldFilename ;
|
|
HPDCVOID pFileList = NULL ;
|
|
HPDCVOID pTmpFileList = NULL;
|
|
HPDCVOID pFilename = NULL ;
|
|
#ifndef OS_WINCE
|
|
char* fileList ;
|
|
WCHAR* fileListW ;
|
|
SHFILEOPSTRUCTA fileOpStructA ;
|
|
SHFILEOPSTRUCTW fileOpStructW ;
|
|
HRESULT result ;
|
|
DCTCHAR formatName[TS_FORMAT_NAME_LEN] ;
|
|
#endif
|
|
HRESULT hr;
|
|
|
|
DC_BEGIN_FN("CClip::ClipOnFormatRequest");
|
|
|
|
//
|
|
// Set the response to failure before making the state check
|
|
//
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
CB_CHECK_STATE(CB_EVENT_FORMAT_DATA_RQ);
|
|
response = TS_CB_RESPONSE_OK;
|
|
|
|
/************************************************************************/
|
|
/* Make sure the local CB is open */
|
|
/************************************************************************/
|
|
if ((_CB.rcvOpen) || OpenClipboard(_CB.viewerWindow))
|
|
{
|
|
/********************************************************************/
|
|
/* It was/is open */
|
|
/********************************************************************/
|
|
TRC_NRM((TB, _T("CB opened")));
|
|
_CB.rcvOpen = TRUE;
|
|
|
|
/********************************************************************/
|
|
/* Extract the format from the PDU */
|
|
/********************************************************************/
|
|
TRC_DATA_DBG("pdu data", pClipPDU->data, (DCUINT)pClipPDU->dataLen);
|
|
|
|
//
|
|
// Verify that we have enough data to extract a format ID.
|
|
//
|
|
|
|
if (pClipPDU->dataLen < sizeof(DCUINT32)) {
|
|
TRC_ERR((TB,_T("Not enough data to extract a format ID.")));
|
|
_CB.channelEP.pVirtualChannelCloseEx(_CB.initHandle, _CB.channelHandle);
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
formatID = *((PDCUINT32_UA)pClipPDU->data);
|
|
TRC_NRM((TB, _T("Requesting format %ld"), formatID));
|
|
|
|
/********************************************************************/
|
|
/* If the Server asked for CF_DIB, we may have to translate from */
|
|
/* CF_BITMAP */
|
|
/********************************************************************/
|
|
if ((formatID == CF_DIB) && (!_CB.DIBFormatExists))
|
|
{
|
|
TRC_NRM((TB, _T("Server asked for CF_DIB - get CF_BITMAP")));
|
|
formatID = CF_BITMAP;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Get a handle to the data */
|
|
/********************************************************************/
|
|
#ifdef OS_WINCE
|
|
if (formatID == CF_HDROP)
|
|
formatID = gfmtShellPidlArray;
|
|
#endif
|
|
hData = GetClipboardData((UINT)formatID);
|
|
TRC_DBG((TB, _T("Got format %ld at %p"), formatID, hData));
|
|
if (hData == NULL)
|
|
{
|
|
/****************************************************************/
|
|
/* Oops! */
|
|
/****************************************************************/
|
|
TRC_ERR((TB, _T("Failed to get format %ld"), formatID));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
else
|
|
{
|
|
/****************************************************************/
|
|
/* Got handle, now what happens next depends on the flavour of */
|
|
/* data we're looking at... */
|
|
/****************************************************************/
|
|
if (formatID == CF_PALETTE)
|
|
{
|
|
TRC_DBG((TB, _T("CF_PALETTE requested")));
|
|
/************************************************************/
|
|
/* Find out how many entries there are in the palette and */
|
|
/* allocate enough memory to hold them all. */
|
|
/************************************************************/
|
|
if (GetObject(hData, sizeof(DCUINT16), &dwEntries) == 0)
|
|
{
|
|
TRC_DBG((TB, _T("Failed to get count of palette entries")));
|
|
dwEntries = 256;
|
|
}
|
|
numEntries = (DCUINT)dwEntries;
|
|
TRC_DBG((TB, _T("Need mem for %u palette entries"), numEntries));
|
|
|
|
dataLen = sizeof(LOGPALETTE) - sizeof(PALETTEENTRY) +
|
|
(numEntries * sizeof(PALETTEENTRY));
|
|
|
|
hNewData = GlobalAlloc(GHND, dataLen);
|
|
if (hNewData == 0)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get %ld bytes for palette"),
|
|
dataLen));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
else
|
|
{
|
|
/********************************************************/
|
|
/* now get the palette entries into the new buffer */
|
|
/********************************************************/
|
|
pData = (HPDCUINT8)GlobalLock(hNewData);
|
|
if (NULL == pData) {
|
|
TRC_ERR((TB,_T("Failed to lock palette entries")));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT;
|
|
}
|
|
|
|
numEntries = GetPaletteEntries((HPALETTE)hData,
|
|
0,
|
|
numEntries,
|
|
(PALETTEENTRY*)pData);
|
|
TRC_DATA_DBG("Palette entries", pData, (DCUINT)dataLen);
|
|
GlobalUnlock(hNewData);
|
|
TRC_DBG((TB, _T("Got %u palette entries"), numEntries));
|
|
if (numEntries == 0)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get any palette entries")));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
dataLen = numEntries * sizeof(PALETTEENTRY);
|
|
|
|
/********************************************************/
|
|
/* all ok - set up hData to point to the new data */
|
|
/********************************************************/
|
|
hData = hNewData;
|
|
}
|
|
|
|
}
|
|
#ifndef OS_WINCE
|
|
else if (formatID == CF_METAFILEPICT)
|
|
{
|
|
TRC_NRM((TB, _T("Metafile data to get")));
|
|
hNewData = ClipGetMFData(hData, &dataLen);
|
|
if (!hNewData)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to set MF data")));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
else
|
|
{
|
|
/********************************************************/
|
|
/* all ok - set up hData to point to the new data */
|
|
/********************************************************/
|
|
hData = hNewData;
|
|
}
|
|
}
|
|
#endif
|
|
else if (formatID == CF_BITMAP)
|
|
{
|
|
/************************************************************/
|
|
/* We've gt CF_BITMAP data. This will be because the */
|
|
/* Server has asked for CF_DIB data - we never send */
|
|
/* CF_BITMAP to the Server. Convert it to CF_DIB format. */
|
|
/************************************************************/
|
|
TRC_NRM((TB, _T("Convert CF_BITMAP to CF_DIB")));
|
|
hNewData = ClipBitmapToDIB(hData, &dataLen);
|
|
if (hNewData)
|
|
{
|
|
hData = hNewData;
|
|
}
|
|
else
|
|
{
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
}
|
|
// Since we won't even send an HDROP format to the server if the
|
|
// local drives are redirected, and we always send a new formatlist
|
|
// when we reconnect to a server, this does not have to be touched
|
|
#ifndef OS_WINCE
|
|
else if (formatID == CF_HDROP)
|
|
#else
|
|
else if (formatID == gfmtShellPidlArray)
|
|
#endif
|
|
{
|
|
SIZE_T cbDropFiles;
|
|
BYTE *pbLastByte, *pbStartByte, *pbLastPossibleNullStart;
|
|
BOOL fTrailingFileNamesValid;
|
|
ULONG cbRemaining;
|
|
|
|
TRC_NRM((TB,_T("HDROP requested"))) ;
|
|
#ifdef OS_WINCE
|
|
HANDLE hPidlArray = IDListToHDrop(hData);
|
|
if (!hPidlArray)
|
|
{
|
|
TRC_ERR((TB,_T("Failed to get file list from clipboard"))) ;
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
hData = hPidlArray;
|
|
#endif
|
|
|
|
//
|
|
// Make sure that we have at least a DROPFILES structure in
|
|
// memory.
|
|
|
|
cbDropFiles = GlobalSize(hData);
|
|
if (cbDropFiles < sizeof(DROPFILES)) {
|
|
TRC_ERR((TB,_T("Unexpected global memory size!"))) ;
|
|
_CB.channelEP.pVirtualChannelCloseEx(_CB.initHandle, _CB.channelHandle);
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
pDropFiles = (DROPFILES*) GlobalLock(hData) ;
|
|
if (!pDropFiles)
|
|
{
|
|
TRC_ERR((TB,_T("Failed to lock %p"), hData)) ;
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
|
|
}
|
|
fWide = ((DROPFILES*) pDropFiles)->fWide ;
|
|
charSize = fWide ? sizeof(WCHAR) : sizeof(char) ;
|
|
|
|
//
|
|
// Check that the data behind the DROPFILES data structure
|
|
// pointed to by pDropFiles is valid. Every drop file list
|
|
// is terminated by two NULL characters. So, simply scan
|
|
// through the memory after the DROPFILES structure and make
|
|
// sure that there is a double NULL before the last byte.
|
|
//
|
|
|
|
if (pDropFiles->pFiles < sizeof(DROPFILES)
|
|
|| pDropFiles->pFiles > cbDropFiles) {
|
|
TRC_ERR((TB,_T("File name offset invalid!"))) ;
|
|
_CB.channelEP.pVirtualChannelCloseEx(_CB.initHandle, _CB.channelHandle);
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
pbStartByte = (BYTE*) pDropFiles + pDropFiles->pFiles;
|
|
pbLastByte = (BYTE*) pDropFiles + cbDropFiles - 1;
|
|
fTrailingFileNamesValid = FALSE;
|
|
|
|
//
|
|
// Make pbLastPossibleNullStart point to the last place where a
|
|
// double NULL could possibly start.
|
|
//
|
|
// Examples: Assume pbLastByte = 9
|
|
// Then for ASCII: pbLastPossibleNullStart = 8 (9 - 2 * 1 + 1)
|
|
// And for UNICODE: pbLastPossibleNullStart = 6 (9 - 2 * 2 + 1)
|
|
//
|
|
|
|
pbLastPossibleNullStart = pbLastByte - (2 * charSize) + 1;
|
|
|
|
if (fWide) {
|
|
for (WCHAR* pwch = (WCHAR*) pbStartByte; (BYTE*) pwch <= pbLastPossibleNullStart; pwch++) {
|
|
if (*pwch == NULL && *(pwch + 1) == NULL) {
|
|
fTrailingFileNamesValid = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
for (BYTE* pch = pbStartByte; pch <= pbLastPossibleNullStart; pch++) {
|
|
if (*pch == NULL && *(pch + 1) == NULL) {
|
|
fTrailingFileNamesValid = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!fTrailingFileNamesValid) {
|
|
TRC_ERR((TB,_T("DROPFILES structure invalid!"))) ;
|
|
_CB.channelEP.pVirtualChannelCloseEx(_CB.initHandle, _CB.channelHandle);
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// DROPFILES are valid so we can continue.
|
|
//
|
|
|
|
if (!_CB.fAlreadyCopied)
|
|
{
|
|
// if it's not a drive path, then copy to a temp directory
|
|
pFileList = (byte*)pDropFiles + pDropFiles->pFiles ;
|
|
#ifndef OS_WINCE
|
|
fDrivePath = fWide ? (0 != wcschr((WCHAR*) pFileList, L':'))
|
|
: (0 != strchr((char*) pFileList, ':')) ;
|
|
#else //copy to temp dir if it is a network path
|
|
fDrivePath = fWide ? (! ( (((WCHAR *)pFileList)[0] == L'\\') && (((WCHAR *)pFileList)[1] == L'\\')) )
|
|
: (! ( (((CHAR *)pFileList)[0] == '\\') && (((CHAR *)pFileList)[1] == '\\')) ) ;
|
|
#endif
|
|
if (!fDrivePath)
|
|
{
|
|
// ClipCopyToTempDirectory returns 0 if successful
|
|
if (0 != ClipCopyToTempDirectory(pFileList, fWide))
|
|
{
|
|
TRC_ERR((TB,_T("Copy to tmp directory failed"))) ;
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
_CB.fAlreadyCopied = TRUE ;
|
|
DC_QUIT ;
|
|
}
|
|
}
|
|
_CB.fAlreadyCopied = TRUE ;
|
|
}
|
|
|
|
// Now that we copied the files, we want to convert the file
|
|
// paths to something the server will understand
|
|
|
|
// Allocate space for new filepaths
|
|
oldSize = (ULONG) GlobalSize(hData) ;
|
|
newSize = ClipGetNewDropfilesSize(pDropFiles, oldSize, fWide) ;
|
|
hNewData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE, newSize) ;
|
|
if (!hNewData)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get %ld bytes for HDROP"),
|
|
newSize));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
pNewData = GlobalLock(hNewData) ;
|
|
if (!pNewData)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get lock %p for HDROP"),
|
|
hNewData));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
// Just copy the old DROPFILES data members (unchanged)
|
|
((DROPFILES*) pNewData)->pFiles = pDropFiles->pFiles ;
|
|
((DROPFILES*) pNewData)->pt = pDropFiles->pt ;
|
|
((DROPFILES*) pNewData)->fNC = pDropFiles->fNC ;
|
|
((DROPFILES*) pNewData)->fWide = pDropFiles->fWide ;
|
|
|
|
// The first filename in a DROPFILES data structure begins
|
|
// DROPFILES.pFiles bytes away from the head of the DROPFILES
|
|
pOldFilename = (byte*) pDropFiles + ((DROPFILES*) pDropFiles)->pFiles ;
|
|
pFilename = (byte*) pNewData + ((DROPFILES*) pNewData)->pFiles ;
|
|
|
|
pbLastByte = (BYTE*) pNewData + newSize - 1;
|
|
cbRemaining = (ULONG) (pbLastByte - (BYTE*) pFilename + 1);
|
|
|
|
while (fWide ? (L'\0' != ((WCHAR*) pOldFilename)[0]) : ('\0' != ((char*) pOldFilename)[0]))
|
|
{
|
|
if (FAILED(ClipConvertToTempPath(pOldFilename, pFilename, cbRemaining, fWide)))
|
|
{
|
|
TRC_ERR((TB, _T("Failed conversion"))) ;
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0 ;
|
|
DC_QUIT ;
|
|
}
|
|
if (fWide)
|
|
{
|
|
pOldFilename = (byte*) pOldFilename + (wcslen((WCHAR*)pOldFilename) + 1) * sizeof(WCHAR) ;
|
|
pFilename = (byte*) pFilename + (wcslen((WCHAR*)pFilename) + 1) * sizeof(WCHAR) ;
|
|
}
|
|
else
|
|
{
|
|
pOldFilename = (byte*) pOldFilename + (strlen((char*)pOldFilename) + 1) * sizeof(char) ;
|
|
pFilename = (byte*) pFilename + (strlen((char*)pFilename) + 1) * sizeof(char) ;
|
|
}
|
|
cbRemaining = (ULONG) (pbLastByte - (BYTE*) pFilename + 1);
|
|
}
|
|
if (fWide)
|
|
{
|
|
((WCHAR*) pFilename)[0] = L'\0' ;
|
|
}
|
|
else
|
|
{
|
|
((char*) pFilename)[0] = '\0' ;
|
|
}
|
|
GlobalUnlock(hNewData) ;
|
|
hData = hNewData ;
|
|
response = TS_CB_RESPONSE_OK ;
|
|
dataLen = (ULONG) GlobalSize(hData) ;
|
|
#ifdef OS_WINCE
|
|
GlobalFree(hPidlArray);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifndef OS_WINCE
|
|
// Check to see if we are processing the FileName/FileNameW
|
|
// OLE 1 formats; if so, we convert th
|
|
if (0 != GetClipboardFormatName(formatID, formatName, TS_FORMAT_NAME_LEN))
|
|
{
|
|
if ((0 == _tcscmp(formatName, TEXT("FileName"))) ||
|
|
(0 == _tcscmp(formatName, TEXT("FileNameW"))))
|
|
{
|
|
size_t cbOldFileName;
|
|
|
|
if (!_tcscmp(formatName, TEXT("FileNameW")))
|
|
{
|
|
fWide = TRUE ;
|
|
charSize = sizeof(WCHAR) ;
|
|
}
|
|
else
|
|
{
|
|
fWide = FALSE ;
|
|
charSize = 1 ;
|
|
}
|
|
|
|
//
|
|
// Extract the file name, but ensure that it is properly
|
|
// NULL terminated.
|
|
//
|
|
|
|
pOldFilename = GlobalLock(hData);
|
|
|
|
if (!pOldFilename)
|
|
{
|
|
TRC_ERR((TB, _T("No filename/Unable to lock %p"),
|
|
hData));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
oldSize = (ULONG) GlobalSize(hData) ;
|
|
|
|
if (fWide) {
|
|
hr = StringCbLengthW((WCHAR*) pOldFilename,
|
|
oldSize,
|
|
&cbOldFileName);
|
|
} else {
|
|
hr = StringCbLengthA((CHAR*) pOldFilename,
|
|
oldSize,
|
|
&cbOldFileName);
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB, _T("File name not NULL terminated!")));
|
|
_CB.channelEP.pVirtualChannelCloseEx(_CB.initHandle, _CB.channelHandle);
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
if (!_CB.fAlreadyCopied)
|
|
{
|
|
// if its not a drive path, then copy to a temp
|
|
// directory. We have to copy over the filename to
|
|
// string that is one character larger, because we
|
|
// need to add an extra NULL for the SHFileOperation
|
|
UINT cbSize= oldSize + charSize;
|
|
pTmpFileList = LocalAlloc(LPTR, cbSize);
|
|
if (NULL == pTmpFileList) {
|
|
TRC_ERR((TB,_T("pTmpFileList alloc failed")));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
_CB.fAlreadyCopied = TRUE;
|
|
DC_QUIT ;
|
|
}
|
|
if (fWide)
|
|
{
|
|
hr = StringCbCopyW((WCHAR*)pTmpFileList, cbSize,
|
|
(WCHAR*)pOldFilename) ;
|
|
fDrivePath = (0 != wcschr((WCHAR*) pTmpFileList, L':')) ;
|
|
}
|
|
else
|
|
{
|
|
hr = StringCbCopyA((char*)pTmpFileList, cbSize,
|
|
(char*)pOldFilename) ;
|
|
fDrivePath = (0 != strchr((char*) pTmpFileList, ':')) ;
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB,_T("Failed to cpy filelist string: 0x%x"),hr));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
_CB.fAlreadyCopied = TRUE;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
if (fDrivePath)
|
|
{
|
|
// ClipCopyToTempDirectory returns 0 if successful
|
|
if (0 != ClipCopyToTempDirectory(pTmpFileList, fWide))
|
|
{
|
|
TRC_ERR((TB,_T("Copy to tmp directory failed"))) ;
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
_CB.fAlreadyCopied = TRUE ;
|
|
DC_QUIT ;
|
|
}
|
|
}
|
|
_CB.fAlreadyCopied = TRUE ;
|
|
LocalFree(pTmpFileList);
|
|
pTmpFileList = NULL;
|
|
}
|
|
newSize = ClipGetNewFilePathLength(pOldFilename, fWide) ;
|
|
hNewData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE, newSize) ;
|
|
if (!hNewData)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get %ld bytes for HDROP"),
|
|
newSize));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
pFilename = GlobalLock(hNewData) ;
|
|
if (!pFilename)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get lock %p for HDROP"),
|
|
hNewData));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
if (FAILED(ClipConvertToTempPath(pOldFilename, pFilename, newSize, fWide)))
|
|
{
|
|
TRC_ERR((TB, _T("Failed conversion"))) ;
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
GlobalUnlock(hNewData) ;
|
|
hData = hNewData ;
|
|
response = TS_CB_RESPONSE_OK ;
|
|
dataLen = newSize ;
|
|
DC_QUIT ;
|
|
}
|
|
}
|
|
#endif
|
|
/************************************************************/
|
|
/* just get the length of the block */
|
|
/************************************************************/
|
|
dataLen = (DCUINT32)GlobalSize(hData);
|
|
TRC_DBG((TB, _T("Got data len %ld"), dataLen));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/********************************************************************/
|
|
/* Failed to open CB - send a failure response */
|
|
/********************************************************************/
|
|
TRC_ERR((TB, _T("Failed to open CB")));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
/************************************************************************/
|
|
/* By default, we'll use the permanent send buffer */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Get perm TX buffer")));
|
|
|
|
pduLen = dataLen + sizeof(TS_CLIP_PDU);
|
|
pClipNew = (PTS_CLIP_PDU)ClipAlloc(pduLen);
|
|
|
|
if (pClipNew != NULL)
|
|
{
|
|
/****************************************************************/
|
|
/* Use the new buffer */
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("Free perm TX buffer")));
|
|
pClipRsp = pClipNew;
|
|
}
|
|
else
|
|
{
|
|
/****************************************************************/
|
|
/* Fail the request */
|
|
/****************************************************************/
|
|
TRC_ERR((TB, _T("Failed to alloc %ld bytes"), pduLen));
|
|
pClipRsp = ClipGetPermBuf();
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
pduLen = sizeof(TS_CLIP_PDU);
|
|
}
|
|
|
|
DC_MEMSET(pClipRsp, 0, sizeof(*pClipRsp));
|
|
pClipRsp->msgType = TS_CB_FORMAT_DATA_RESPONSE;
|
|
pClipRsp->msgFlags = response;
|
|
pClipRsp->dataLen = dataLen;
|
|
|
|
if (pTmpFileList) {
|
|
LocalFree(pTmpFileList);
|
|
pTmpFileList = NULL;
|
|
}
|
|
|
|
|
|
// Copy data, if any
|
|
if (dataLen != 0)
|
|
{
|
|
TRC_DBG((TB, _T("Copying all the data")));
|
|
pData = (HPDCUINT8)GlobalLock(hData);
|
|
if (NULL != pData) {
|
|
ClipMemcpy(pClipRsp->data, pData, dataLen);
|
|
GlobalUnlock(hData);
|
|
}
|
|
else {
|
|
TRC_ERR(( TB, _T("Failed to lock data")));
|
|
pClipRsp->msgFlags = TS_CB_RESPONSE_FAIL;
|
|
pClipRsp->dataLen = 0;
|
|
pduLen = sizeof(TS_CLIP_PDU);
|
|
}
|
|
}
|
|
|
|
// Send the PDU
|
|
TRC_NRM((TB, _T("Sending format data rsp")));
|
|
if (_CB.channelEP.pVirtualChannelWriteEx
|
|
(_CB.initHandle, _CB.channelHandle, (LPVOID)pClipRsp, pduLen, pClipRsp)
|
|
!= CHANNEL_RC_OK)
|
|
{
|
|
ClipFreeBuf((PDCUINT8)pClipRsp);
|
|
}
|
|
|
|
// close the clipboard if we need to
|
|
if (_CB.rcvOpen)
|
|
{
|
|
TRC_DBG((TB, _T("Closing CB")));
|
|
_CB.rcvOpen = FALSE;
|
|
if (!CloseClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseClipboard");
|
|
}
|
|
}
|
|
|
|
// if we got any new data, we need to free it
|
|
if (hNewData)
|
|
{
|
|
TRC_DBG((TB, _T("Freeing new data")));
|
|
GlobalFree(hNewData);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return;
|
|
} /* ClipOnFormatRequest */
|
|
|
|
/****************************************************************************/
|
|
// ClipOnFormatDataComplete
|
|
// - Server response to our request for data
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CClip::ClipOnFormatDataComplete(PTS_CLIP_PDU pClipPDU)
|
|
{
|
|
HANDLE hData = NULL;
|
|
HPDCVOID pData;
|
|
LOGPALETTE * pLogPalette = NULL;
|
|
DCUINT32 numEntries;
|
|
DCUINT32 memLen;
|
|
#ifndef OS_WINCE
|
|
|
|
HRESULT hr ;
|
|
#endif
|
|
|
|
DC_BEGIN_FN("CClip::ClipOnFormatDataComplete");
|
|
|
|
/************************************************************************/
|
|
/* check the response */
|
|
/************************************************************************/
|
|
if (_pClipData == NULL) {
|
|
TRC_ALT((TB, _T("The clipData is NULL, we just bail")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (!(pClipPDU->msgFlags & TS_CB_RESPONSE_OK))
|
|
{
|
|
TRC_ALT((TB, _T("Got fmt data rsp failed for %d"), _CB.pendingClientID));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Got the data */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Got OK fmt data rsp for %d"), _CB.pendingClientID));
|
|
|
|
#ifndef OS_WINCE
|
|
/************************************************************************/
|
|
/* For some formats we still need to do some work */
|
|
/************************************************************************/
|
|
if (_CB.pendingClientID == CF_METAFILEPICT)
|
|
{
|
|
/********************************************************************/
|
|
/* Metafile format - create a metafile from the data */
|
|
/********************************************************************/
|
|
TRC_NRM((TB, _T("Rx data is for metafile")));
|
|
hData = ClipSetMFData(pClipPDU->dataLen, pClipPDU->data);
|
|
if (hData == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to set MF data")));
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if (_CB.pendingClientID == CF_PALETTE)
|
|
{
|
|
/********************************************************************/
|
|
/* Palette format - create a palette from the data */
|
|
/********************************************************************/
|
|
|
|
/********************************************************************/
|
|
/* Allocate memory for a LOGPALETTE structure large enough to hold */
|
|
/* all the PALETTE ENTRY structures, and fill it in. */
|
|
/********************************************************************/
|
|
TRC_NRM((TB, _T("Rx data is for palette")));
|
|
numEntries = (pClipPDU->dataLen / sizeof(PALETTEENTRY));
|
|
memLen = (sizeof(LOGPALETTE) +
|
|
((numEntries - 1) * sizeof(PALETTEENTRY)));
|
|
TRC_DBG((TB, _T("%ld palette entries, allocate %ld bytes"),
|
|
numEntries, memLen));
|
|
pLogPalette = (LOGPALETTE*)ClipAlloc(memLen);
|
|
if (pLogPalette != NULL)
|
|
{
|
|
pLogPalette->palVersion = 0x300;
|
|
pLogPalette->palNumEntries = (WORD)numEntries;
|
|
|
|
ClipMemcpy(pLogPalette->palPalEntry,
|
|
pClipPDU->data,
|
|
pClipPDU->dataLen);
|
|
|
|
/****************************************************************/
|
|
/* now create a palette */
|
|
/****************************************************************/
|
|
hData = CreatePalette(pLogPalette);
|
|
if (hData == NULL)
|
|
{
|
|
TRC_SYSTEM_ERROR("CreatePalette");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get %ld bytes"), memLen));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_NRM((TB, _T("Rx data can just go on CB")));
|
|
/********************************************************************/
|
|
/* We need to copy the data, as the receive buffer will be freed on */
|
|
/* return from this function. */
|
|
/********************************************************************/
|
|
hData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE,
|
|
pClipPDU->dataLen);
|
|
if (hData != NULL)
|
|
{
|
|
pData = GlobalLock(hData);
|
|
if (pData != NULL)
|
|
{
|
|
TRC_NRM((TB, _T("Copy %ld bytes from %p to %p"),
|
|
pClipPDU->dataLen, pClipPDU->data, pData));
|
|
ClipMemcpy(pData, pClipPDU->data, pClipPDU->dataLen);
|
|
GlobalUnlock(hData);
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Failed to lock %p (%ld bytes)"),
|
|
hData, pClipPDU->dataLen));
|
|
GlobalFree(hData);
|
|
hData = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Failed to alloc %ld bytes"), pClipPDU->dataLen));
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
/************************************************************************/
|
|
/* tidy up */
|
|
/************************************************************************/
|
|
if (pLogPalette != NULL)
|
|
{
|
|
ClipFree(pLogPalette);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Set the state, and we're done. Note that this is done when we get a */
|
|
/* failure response too. */
|
|
/************************************************************************/
|
|
CB_SET_STATE(CB_STATE_LOCAL_CB_OWNER, CB_EVENT_FORMAT_DATA_RSP);
|
|
_pClipData->SetClipData(hData, _CB.pendingClientID ) ;
|
|
|
|
TRC_ASSERT( NULL != _GetDataSync[TS_RECEIVE_COMPLETED],
|
|
(TB,_T("data sync is NULL")));
|
|
SetEvent(_GetDataSync[TS_RECEIVE_COMPLETED]) ;
|
|
|
|
DC_END_FN();
|
|
return;
|
|
} /* ClipOnFormatDataComplete */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ClipRemoteFormatFromLocalID */
|
|
/****************************************************************************/
|
|
DCUINT DCINTERNAL CClip::ClipRemoteFormatFromLocalID(DCUINT id)
|
|
{
|
|
DCUINT i;
|
|
DCUINT retID = 0;
|
|
|
|
for (i = 0; i < CB_MAX_FORMATS; i++)
|
|
{
|
|
if (_CB.idMap[i].clientID == id)
|
|
{
|
|
retID = _CB.idMap[i].serverID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(retID);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ClipOnWriteComplete */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CClip::ClipOnWriteComplete(LPVOID pData)
|
|
{
|
|
PTS_CLIP_PDU pClipPDU;
|
|
|
|
DC_BEGIN_FN("CClip::ClipOnWriteComplete");
|
|
|
|
TRC_NRM((TB, _T("Free buffer at %p"), pData));
|
|
pClipPDU = (PTS_CLIP_PDU)pData;
|
|
TRC_DBG((TB, _T("Message type %hx, flags %hx"),
|
|
pClipPDU->msgType, pClipPDU->msgFlags));
|
|
|
|
/************************************************************************/
|
|
/* Free the buffer */
|
|
/************************************************************************/
|
|
TRC_DBG((TB, _T("Write from buffer %p complete"), pData));
|
|
ClipFreeBuf((PDCUINT8)pData);
|
|
|
|
DC_END_FN();
|
|
return;
|
|
}
|
|
|
|
HRESULT CClip::ClipCreateDataSyncEvents()
|
|
{
|
|
HRESULT hr = E_FAIL ;
|
|
|
|
DC_BEGIN_FN("CClip::ClipCreateDataSyncEvents") ;
|
|
// Create events for controlling the Clipboard thread
|
|
_GetDataSync[TS_RECEIVE_COMPLETED] = CreateEvent(NULL, FALSE, FALSE, NULL) ;
|
|
_GetDataSync[TS_RESET_EVENT] = CreateEvent(NULL, FALSE, FALSE, NULL) ;
|
|
|
|
if (!_GetDataSync[TS_RECEIVE_COMPLETED])
|
|
{
|
|
TRC_ERR((TB, _T("Failed CreateEvent RECEIVE_COMPLETED; Error = %d"),
|
|
GetLastError())) ;
|
|
hr = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
if (!_GetDataSync[TS_RESET_EVENT])
|
|
{
|
|
TRC_ERR((TB, _T("Failed CreateEvent RESET_EVENT; Error = %d"),
|
|
GetLastError())) ;
|
|
hr = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
hr = S_OK ;
|
|
DC_EXIT_POINT:
|
|
DC_END_FN() ;
|
|
return hr ;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* ClipOnInitialized */
|
|
/****************************************************************************/
|
|
DCINT32 DCAPI CClip::ClipOnInitialized(DCVOID)
|
|
{
|
|
DC_BEGIN_FN("CClip::ClipOnInitialized") ;
|
|
HRESULT hr = E_FAIL ;
|
|
BOOL fthreadStarted = FALSE ;
|
|
|
|
hr = ClipCreateDataSyncEvents() ;
|
|
DC_QUIT_ON_FAIL(hr);
|
|
|
|
/************************************************************************/
|
|
/* Register a message for communication between the two threads */
|
|
/************************************************************************/
|
|
_CB.regMsg = WM_USER_CHANGE_THREAD;
|
|
|
|
TRC_NRM((TB, _T("Registered window message %x"), _CB.regMsg));
|
|
|
|
_CB.pClipThreadData = (PUT_THREAD_DATA) LocalAlloc(LPTR, sizeof(UT_THREAD_DATA)) ;
|
|
if (NULL == _CB.pClipThreadData)
|
|
{
|
|
TRC_ERR((TB, _T("Unable to allocate %d bytes for thread data"),
|
|
sizeof(UT_THREAD_DATA))) ;
|
|
hr = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
if (_pUtObject) {
|
|
fthreadStarted = _pUtObject->UT_StartThread(ClipStaticMain,
|
|
_CB.pClipThreadData, this);
|
|
}
|
|
|
|
if (!fthreadStarted)
|
|
{
|
|
hr = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
// If we got to this point, then we succeeded initialized everything
|
|
hr = S_OK ;
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
// On failure be sure to clear out the data syncs.
|
|
// we will not connect the virtual channel if the
|
|
// data sync are NULL
|
|
if (FAILED(hr)) {
|
|
_GetDataSync[TS_RECEIVE_COMPLETED] = NULL;
|
|
_GetDataSync[TS_RESET_EVENT] = NULL;
|
|
}
|
|
|
|
return hr ;
|
|
}
|
|
/****************************************************************************/
|
|
/* ClipStaticMain */
|
|
/****************************************************************************/
|
|
DCVOID DCAPI ClipStaticMain(PDCVOID param)
|
|
{
|
|
((CClip*) param)->ClipMain() ;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* ClipOnInit */
|
|
/****************************************************************************/
|
|
DCINT DCAPI CClip::ClipOnInit(DCVOID)
|
|
{
|
|
ATOM registerClassRc;
|
|
WNDCLASS viewerWindowClass;
|
|
WNDCLASS tmpWndClass;
|
|
DCINT allOk = FALSE ;
|
|
|
|
DC_BEGIN_FN("CClip::ClipOnInit");
|
|
|
|
/************************************************************************/
|
|
/* Create an invisible window which we will register as a clipboard */
|
|
/* viewer */
|
|
/* Only register if prev instance did not already register the class */
|
|
/************************************************************************/
|
|
if(!GetClassInfo(_CB.hInst, CB_VIEWER_CLASS, &tmpWndClass))
|
|
{
|
|
TRC_NRM((TB, _T("Register Main Window class, data %p, hInst %p"),
|
|
&viewerWindowClass, _CB.hInst));
|
|
viewerWindowClass.style = 0;
|
|
viewerWindowClass.lpfnWndProc = StaticClipViewerWndProc;
|
|
viewerWindowClass.cbClsExtra = 0;
|
|
viewerWindowClass.cbWndExtra = sizeof(void*);
|
|
viewerWindowClass.hInstance = _CB.hInst ;
|
|
viewerWindowClass.hIcon = NULL;
|
|
viewerWindowClass.hCursor = NULL;
|
|
viewerWindowClass.hbrBackground = (HBRUSH)GetStockObject(HOLLOW_BRUSH);
|
|
viewerWindowClass.lpszMenuName = NULL;
|
|
viewerWindowClass.lpszClassName = CB_VIEWER_CLASS;
|
|
|
|
TRC_DATA_NRM("Register class data", &viewerWindowClass, sizeof(WNDCLASS));
|
|
registerClassRc = RegisterClass (&viewerWindowClass);
|
|
|
|
if (registerClassRc == 0)
|
|
{
|
|
/****************************************************************/
|
|
/* Failed to register CB viewer class */
|
|
/****************************************************************/
|
|
TRC_ERR((TB, _T("Failed to register Cb Viewer class")));
|
|
}
|
|
TRC_NRM((TB, _T("Registered class")));
|
|
}
|
|
|
|
_CB.viewerWindow =
|
|
CreateWindowEx(
|
|
#ifndef OS_WINCE
|
|
WS_EX_NOPARENTNOTIFY,
|
|
#else
|
|
0,
|
|
#endif
|
|
CB_VIEWER_CLASS, /* window class name */
|
|
_T("CB Viewer Window"), /* window caption */
|
|
#ifndef OS_WINCE
|
|
WS_OVERLAPPEDWINDOW, /* window style */
|
|
#else
|
|
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
|
|
#endif
|
|
0, /* initial x position */
|
|
0, /* initial y position */
|
|
100, /* initial x size */
|
|
100, /* initial y size */
|
|
NULL, /* parent window */
|
|
NULL, /* window menu handle */
|
|
_CB.hInst, /* program inst handle */
|
|
this); /* creation parameters */
|
|
|
|
/************************************************************************/
|
|
/* Check we created the window OK */
|
|
/************************************************************************/
|
|
if (_CB.viewerWindow == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to create CB Viewer Window")));
|
|
DC_QUIT ;
|
|
}
|
|
TRC_DBG((TB, _T("Viewer Window handle %p"), _CB.viewerWindow));
|
|
|
|
#ifdef OS_WINCE
|
|
if ((_CB.dataWindow = CECreateCBDataWindow(_CB.hInst)) == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to create CB Data Window")));
|
|
DC_QUIT ;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_SEMAPHORE
|
|
/************************************************************************/
|
|
/* Create the permanent TX buffer semaphore */
|
|
/************************************************************************/
|
|
_CB.txPermBufSem = CreateSemaphore(NULL, 1, 1, NULL);
|
|
if (_CB.txPermBufSem == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to create semaphore")));
|
|
DC_QUIT;
|
|
}
|
|
TRC_NRM((TB, _T("Create perm TX buffer sem %p"), _CB.txPermBufSem));
|
|
#endif
|
|
|
|
#ifdef OS_WINCE
|
|
gfmtShellPidlArray = RegisterClipboardFormat(CFSTR_SHELLPIDLARRAY);
|
|
#endif
|
|
/************************************************************************/
|
|
/* Update the state */
|
|
/************************************************************************/
|
|
CB_SET_STATE(CB_STATE_INITIALIZED, CB_TRACE_EVENT_CB_CLIPMAIN);
|
|
|
|
allOk = TRUE ;
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
|
|
return allOk;
|
|
|
|
} /* ClipOnInit */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ClipOnTerm */
|
|
/****************************************************************************/
|
|
DCBOOL DCAPI CClip::ClipOnTerm(LPVOID pInitHandle)
|
|
{
|
|
BOOL fSuccess = FALSE ;
|
|
BOOL fThreadEnded = FALSE ;
|
|
DC_BEGIN_FN("CClip::ClipOnTerm");
|
|
|
|
/************************************************************************/
|
|
/* Check the state - if we're still connected, we should disconnect */
|
|
/* before shutting down */
|
|
/************************************************************************/
|
|
ClipCheckState(CB_EVENT_CB_TERM);
|
|
if (_CB.state != CB_STATE_INITIALIZED)
|
|
{
|
|
TRC_ALT((TB, _T("Terminated when not disconnected")));
|
|
ClipOnDisconnected(pInitHandle);
|
|
}
|
|
|
|
// If we had file cut/copy on, we should clean up after ourselves
|
|
if (_CB.fFileCutCopyOn)
|
|
{
|
|
SendMessage(_CB.viewerWindow, WM_USER_CLEANUP_ON_TERM, 0, 0);
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* Destroy the window and unregister the class (the WM_DESTROY handling */
|
|
/* will deal with removing the window from the viewer chain) */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Destroying CB window...")));
|
|
if (!PostMessage(_CB.viewerWindow, WM_CLOSE, 0, 0))
|
|
{
|
|
TRC_SYSTEM_ERROR("DestroyWindow");
|
|
}
|
|
|
|
if (!UnregisterClass(CB_VIEWER_CLASS, _CB.hInst))
|
|
{
|
|
TRC_SYSTEM_ERROR("UnregisterClass");
|
|
}
|
|
|
|
#ifdef OS_WINCE
|
|
TRC_NRM((TB, _T("Destroying CB data window...")));
|
|
if (!PostMessage(_CB.dataWindow, WM_CLOSE, 0, 0))
|
|
{
|
|
TRC_SYSTEM_ERROR("DestroyWindow");
|
|
}
|
|
|
|
if (!UnregisterClass(CB_DATAWINDOW_CLASS, _CB.hInst))
|
|
{
|
|
TRC_SYSTEM_ERROR("UnregisterClass");
|
|
}
|
|
#endif
|
|
if (_pClipData)
|
|
{
|
|
// Decrement the reference count of the IDataObject object. At this stage,
|
|
// this should cause the reference count to drop to zero and the IDataObject
|
|
// object's destructor should be called. This destructor will release the
|
|
// reference to this CClipData object, resulting in a reference count of 1.
|
|
// Hence the call to Release() below will result in the CClipData destructor
|
|
// being called.
|
|
|
|
_pClipData->TearDown();
|
|
_pClipData->Release() ;
|
|
_pClipData = NULL;
|
|
}
|
|
if (_CB.pClipThreadData)
|
|
{
|
|
fThreadEnded = _pUtObject->UT_DestroyThread(*_CB.pClipThreadData);
|
|
if (!fThreadEnded)
|
|
{
|
|
TRC_ERR((TB, _T("Error while ending thread"))) ;
|
|
fSuccess = FALSE;
|
|
DC_QUIT ;
|
|
}
|
|
LocalFree( _CB.pClipThreadData );
|
|
_CB.pClipThreadData = NULL;
|
|
}
|
|
|
|
if (_pUtObject)
|
|
{
|
|
LocalFree(_pUtObject);
|
|
_pUtObject = NULL;
|
|
}
|
|
fSuccess = TRUE ;
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* Update our state */
|
|
/************************************************************************/
|
|
CB_SET_STATE(CB_STATE_NOT_INIT, CB_EVENT_CB_TERM);
|
|
|
|
DC_END_FN();
|
|
return fSuccess ;
|
|
|
|
} /* ClipOnTerm */
|
|
|
|
DCVOID DCAPI CClip::ClipMain(DCVOID)
|
|
{
|
|
DC_BEGIN_FN("CClip::ClipMain");
|
|
MSG msg ;
|
|
|
|
#ifndef OS_WINCE
|
|
HRESULT result = OleInitialize(NULL) ;
|
|
#else
|
|
HRESULT result = CoInitializeEx(NULL, COINIT_MULTITHREADED) ;
|
|
#endif
|
|
if (SUCCEEDED(result))
|
|
{
|
|
if (0 != ClipOnInit())
|
|
{
|
|
TRC_NRM((TB, _T("Start Clip Thread message loop"))) ;
|
|
while (GetMessage (&msg, NULL, 0, 0))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Failed initialized clipboard thread"))) ;
|
|
}
|
|
|
|
#ifndef OS_WINCE
|
|
// We assume OleUninitialize works, as it has no return value
|
|
OleUninitialize() ;
|
|
#else
|
|
CoUninitialize() ;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("OleInitialize Failed"))) ;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
TRC_NRM((TB, _T("Exit Clip Thread message loop"))) ;
|
|
DC_END_FN();
|
|
}
|
|
/****************************************************************************/
|
|
/* ClipOnConnected */
|
|
/****************************************************************************/
|
|
VOID DCINTERNAL CClip::ClipOnConnected(LPVOID pInitHandle)
|
|
{
|
|
UINT rc;
|
|
|
|
DC_BEGIN_FN("CClip::ClipOnConnected");
|
|
|
|
if (!IsDataSyncReady()) {
|
|
TRC_ERR((TB, _T("Data Sync not ready")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
_CB.fDrivesRedirected = _pVCMgr->GetInitData()->fEnableRedirectDrives;
|
|
_CB.fFileCutCopyOn = _CB.fDrivesRedirected;
|
|
/************************************************************************/
|
|
/* Open our channel */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Entry points are at %p"), &(_CB.channelEP)));
|
|
TRC_NRM((TB, _T("Call ChannelOpen at %p"), _CB.channelEP.pVirtualChannelOpenEx));
|
|
rc = _CB.channelEP.pVirtualChannelOpenEx(pInitHandle,
|
|
&_CB.channelHandle,
|
|
CLIP_CHANNEL,
|
|
(PCHANNEL_OPEN_EVENT_EX_FN)MakeProcInstance(
|
|
(FARPROC)ClipOpenEventFnEx, _CB.hInst)
|
|
);
|
|
|
|
TRC_NRM((TB, _T("Opened %s: %ld, rc %d"), CLIP_CHANNEL,_CB.channelHandle, rc));
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ClipOnConnected */
|
|
/****************************************************************************/
|
|
VOID DCINTERNAL CClip::ClipOnMonitorReady(VOID)
|
|
{
|
|
DC_BEGIN_FN("CClip::ClipOnMonitorReady");
|
|
|
|
/************************************************************************/
|
|
/* The monitor has woken up. Check the state */
|
|
/************************************************************************/
|
|
CB_CHECK_STATE(CB_EVENT_CB_ENABLE);
|
|
|
|
// Update the state
|
|
|
|
CB_SET_STATE(CB_STATE_ENABLED, CB_TRACE_EVENT_CB_MONITOR_READY);
|
|
|
|
// Get the temp directory, and send it off to the server
|
|
// if it fails, then we turn off File Cut/Copy
|
|
_CB.fFileCutCopyOn = ClipSetAndSendTempDirectory() ;
|
|
|
|
// We now send the list of clipboard formats we have at this end to the
|
|
// server by faking a draw of the local clipboard. We pass TRUE to
|
|
// force a send because the server needs to have the value of our
|
|
// TS_CB_ASCII_NAMES flag (RAID #313251).
|
|
ClipDrawClipboard(TRUE);
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ClipOnDisconnected */
|
|
/****************************************************************************/
|
|
VOID DCINTERNAL CClip::ClipOnDisconnected(LPVOID pInitHandle)
|
|
{
|
|
DC_BEGIN_FN("CClip::ClipOnDisconnected");
|
|
|
|
DC_IGNORE_PARAMETER(pInitHandle);
|
|
|
|
//
|
|
// Reset the clipboard thread
|
|
// if it waits for data
|
|
//
|
|
if ( NULL != _GetDataSync[TS_RESET_EVENT] )
|
|
{
|
|
SetEvent( _GetDataSync[TS_RESET_EVENT] );
|
|
}
|
|
/************************************************************************/
|
|
/* Check the state */
|
|
/************************************************************************/
|
|
ClipCheckState(CB_EVENT_CB_DISABLE);
|
|
|
|
/************************************************************************/
|
|
/* If we are the local clipboard owner, then we must empty it - once */
|
|
/* disconnected, we won't be able to satisfy any further format */
|
|
/* requests. Note that we are still the local CB owner even if we are */
|
|
/* waiting on some data from the server */
|
|
/************************************************************************/
|
|
if (_CB.state == CB_STATE_LOCAL_CB_OWNER)
|
|
{
|
|
TRC_NRM((TB, _T("Disable received while local CB owner")));
|
|
|
|
/********************************************************************/
|
|
/* Open the clipboard if needed */
|
|
/********************************************************************/
|
|
if ((!_CB.rcvOpen) && !OpenClipboard(NULL))
|
|
{
|
|
TRC_ERR((TB, _T("Failed to open CB when emptying required")));
|
|
}
|
|
else
|
|
{
|
|
/****************************************************************/
|
|
/* It was/is open */
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("CB opened")));
|
|
_CB.rcvOpen = TRUE;
|
|
|
|
/****************************************************************/
|
|
/* Empty it */
|
|
/****************************************************************/
|
|
if (!EmptyClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("EmptyClipboard");
|
|
}
|
|
}
|
|
}
|
|
/************************************************************************/
|
|
/* If there is pending format data, we should SetClipboardData to NULL */
|
|
/* so that app can close the clipboard */
|
|
/************************************************************************/
|
|
else if (_CB.state == CB_STATE_PENDING_FORMAT_DATA_RSP) {
|
|
TRC_NRM((TB, _T("Pending format data: setting clipboard data to NULL")));
|
|
SetClipboardData(_CB.pendingClientID, NULL);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Ensure that we close the local CB */
|
|
/************************************************************************/
|
|
if (_CB.rcvOpen)
|
|
{
|
|
_CB.rcvOpen = FALSE;
|
|
if (!CloseClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseClipboard");
|
|
}
|
|
TRC_NRM((TB, _T("CB closed")));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* If we were sending or receiving data, unlock and free the buffers as */
|
|
/* required */
|
|
/************************************************************************/
|
|
if (_CB.rxpBuffer)
|
|
{
|
|
TRC_NRM((TB, _T("Freeing recieve buffer %p"), _CB.rxpBuffer));
|
|
ClipFree(_CB.rxpBuffer);
|
|
_CB.rxpBuffer = NULL;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* Update our state */
|
|
/************************************************************************/
|
|
CB_SET_STATE(CB_STATE_INITIALIZED, CB_TRACE_EVENT_CB_DISCONNECT);
|
|
|
|
DC_END_FN();
|
|
return;
|
|
} /* ClipOnDisconnected */
|
|
|
|
/****************************************************************************/
|
|
// ClipOnDataReceived
|
|
/****************************************************************************/
|
|
DCVOID DCAPI CClip::ClipOnDataReceived(LPVOID pData,
|
|
UINT32 dataLength,
|
|
UINT32 totalLength,
|
|
UINT32 dataFlags)
|
|
{
|
|
PTS_CLIP_PDU pClipPDU;
|
|
DCBOOL freeTheBuffer = TRUE;
|
|
DC_BEGIN_FN("CClip::ClipOnDataReceived");
|
|
|
|
//
|
|
// Verify that there is enough data to make up or create a clip PDU header.
|
|
//
|
|
|
|
if (totalLength < sizeof(TS_CLIP_PDU)) {
|
|
TRC_ERR((TB, _T("Not enough data to form a clip header.")));
|
|
_CB.channelEP.pVirtualChannelCloseEx(_CB.initHandle, _CB.channelHandle);
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Check we're all up and running */
|
|
/************************************************************************/
|
|
if (_CB.state == CB_STATE_NOT_INIT)
|
|
{
|
|
pClipPDU = (PTS_CLIP_PDU)pData;
|
|
TRC_ERR((TB, _T("Clip message type %hd received when not init"),
|
|
pClipPDU->msgType));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Special case: if the entire message fits in one chunk, there's no */
|
|
/* need to copy it */
|
|
/************************************************************************/
|
|
if (CHANNEL_FLAG_ONLY == (dataFlags & CHANNEL_FLAG_ONLY))
|
|
{
|
|
TRC_DBG((TB, _T("Single chunk message")));
|
|
pClipPDU = (PTS_CLIP_PDU)pData;
|
|
}
|
|
else
|
|
{
|
|
|
|
/********************************************************************/
|
|
/* It's a segmented message - rebuild it */
|
|
/********************************************************************/
|
|
if (dataFlags & CHANNEL_FLAG_FIRST)
|
|
{
|
|
/****************************************************************/
|
|
/* If it's the first segment, allocate a buffer to rebuild the */
|
|
/* message in. */
|
|
/****************************************************************/
|
|
TRC_DBG((TB, _T("Alloc %ld-byte buffer"), totalLength));
|
|
_CB.rxpBuffer = (HPDCUINT8)ClipAlloc(totalLength);
|
|
if (_CB.rxpBuffer == NULL)
|
|
{
|
|
/************************************************************/
|
|
/* Failed to alloc a buffer. We have to do something, */
|
|
/* otherwise the Client app can hang waiting for data. */
|
|
/* Fake a failure response. */
|
|
/************************************************************/
|
|
TRC_ERR((TB, _T("Failed to alloc %ld-byte buffer"), totalLength));
|
|
pClipPDU = (PTS_CLIP_PDU)pData;
|
|
pClipPDU->msgFlags = TS_CB_RESPONSE_FAIL;
|
|
pClipPDU->dataLen = 0;
|
|
dataFlags |= CHANNEL_FLAG_LAST;
|
|
|
|
/************************************************************/
|
|
/* Now handle it as if it were complete. Subsequent chunks */
|
|
/* will be discarded. */
|
|
/************************************************************/
|
|
goto MESSAGE_COMPLETE;
|
|
}
|
|
|
|
_CB.rxpBufferCurrent = _CB.rxpBuffer;
|
|
_CB.rxBufferLen = totalLength;
|
|
_CB.rxBufferLeft = totalLength;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Check that we have a buffer to copy into */
|
|
/********************************************************************/
|
|
if (_CB.rxpBuffer == NULL)
|
|
{
|
|
TRC_NRM((TB, _T("Previous buffer alloc failure - discard data")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Check that there is enough room */
|
|
/********************************************************************/
|
|
if (dataLength > _CB.rxBufferLeft)
|
|
{
|
|
TRC_ERR((TB, _T("Not enough room in rx buffer: need/got %ld/%ld"),
|
|
dataLength, _CB.rxBufferLeft));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Copy the data */
|
|
/********************************************************************/
|
|
TRC_DBG((TB, _T("Copy %ld bytes from %p to %p"),
|
|
dataLength, pData, _CB.rxpBufferCurrent));
|
|
ClipMemcpy(_CB.rxpBufferCurrent, pData, dataLength);
|
|
_CB.rxpBufferCurrent += dataLength;
|
|
_CB.rxBufferLeft -= dataLength;
|
|
TRC_DBG((TB, _T("Next copy to %p, left %ld"),
|
|
_CB.rxpBufferCurrent, _CB.rxBufferLeft));
|
|
|
|
/********************************************************************/
|
|
/* If this wasn't the last chunk, there's nothing more to do */
|
|
/********************************************************************/
|
|
if (!(dataFlags & CHANNEL_FLAG_LAST))
|
|
{
|
|
TRC_DBG((TB, _T("Not last chunk")));
|
|
freeTheBuffer = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Check we got the entire message */
|
|
/********************************************************************/
|
|
if (_CB.rxBufferLeft != 0)
|
|
{
|
|
TRC_ERR((TB, _T("Incomplete data, expected/got %ld/%ld"),
|
|
_CB.rxBufferLen, _CB.rxBufferLen - _CB.rxBufferLeft));
|
|
DC_QUIT;
|
|
}
|
|
|
|
pClipPDU = (PTS_CLIP_PDU)(_CB.rxpBuffer);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* We allow monitor ready thru because that's what put us in the call! */
|
|
/************************************************************************/
|
|
if ((_CB.state == CB_STATE_INITIALIZED)
|
|
&& (pClipPDU->msgType != TS_CB_MONITOR_READY))
|
|
{
|
|
TRC_ERR((TB, _T("Clip message type %hd received when not in call"),
|
|
pClipPDU->msgType));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* now switch on the packet type */
|
|
/************************************************************************/
|
|
MESSAGE_COMPLETE:
|
|
TRC_NRM((TB, _T("Processing msg type %hd when in state %d"),
|
|
pClipPDU->msgType, _CB.state));
|
|
TRC_DATA_DBG("pdu", pClipPDU,
|
|
(DCUINT)pClipPDU->dataLen + sizeof(TS_CLIP_PDU));
|
|
|
|
//
|
|
// Verify that the data in the dataLen in pClipPDU is consistent with the
|
|
// length given in the dataLength parameter.
|
|
//
|
|
|
|
if (pClipPDU->dataLen > totalLength - sizeof(TS_CLIP_PDU)) {
|
|
TRC_ERR((TB, _T("Length from network differs from published length.")));
|
|
_CB.channelEP.pVirtualChannelCloseEx(_CB.initHandle, _CB.channelHandle);
|
|
DC_QUIT;
|
|
}
|
|
|
|
switch (pClipPDU->msgType)
|
|
{
|
|
case TS_CB_MONITOR_READY:
|
|
{
|
|
/****************************************************************/
|
|
/* The monitor has initialised - we can complete our start up */
|
|
/* now. */
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("rx monitor ready")));
|
|
TRC_ASSERT( NULL != _GetDataSync[TS_RESET_EVENT],
|
|
(TB,_T("data sync is NULL")));
|
|
SetEvent(_GetDataSync[TS_RESET_EVENT]) ;
|
|
|
|
ClipOnMonitorReady();
|
|
}
|
|
break;
|
|
|
|
case TS_CB_FORMAT_LIST:
|
|
{
|
|
/****************************************************************/
|
|
// The server has some new formats for us.
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("Rx Format list")));
|
|
// Free the Clipboard thread, if locked
|
|
TRC_ASSERT( NULL != _GetDataSync[TS_RESET_EVENT],
|
|
(TB,_T("data sync is NULL")));
|
|
SetEvent(_GetDataSync[TS_RESET_EVENT]) ;
|
|
ClipDecoupleToClip(pClipPDU) ;
|
|
}
|
|
break;
|
|
|
|
case TS_CB_FORMAT_LIST_RESPONSE:
|
|
{
|
|
/****************************************************************/
|
|
// The server has received our new formats.
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("Rx Format list Rsp")));
|
|
ClipOnFormatListResponse(pClipPDU);
|
|
}
|
|
break;
|
|
|
|
case TS_CB_FORMAT_DATA_REQUEST:
|
|
{
|
|
/****************************************************************/
|
|
// An app on the server wants to paste one of the formats from
|
|
// our clipboard.
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("Rx Data Request")));
|
|
ClipOnFormatRequest(pClipPDU);
|
|
}
|
|
break;
|
|
|
|
case TS_CB_FORMAT_DATA_RESPONSE:
|
|
{
|
|
/****************************************************************/
|
|
// Here's some format data for us
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("Rx Format Data rsp in state %d with flags %02x"),
|
|
_CB.state, pClipPDU->msgFlags));
|
|
ClipOnFormatDataComplete(pClipPDU);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
/****************************************************************/
|
|
/* Don't know what this one is! */
|
|
/****************************************************************/
|
|
TRC_ERR((TB, _T("Unknown clip message type %hd"), pClipPDU->msgType));
|
|
}
|
|
break;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* Maybe free the receive buffer */
|
|
/************************************************************************/
|
|
if (freeTheBuffer && _CB.rxpBuffer)
|
|
{
|
|
TRC_NRM((TB, _T("Free receive buffer")));
|
|
ClipFree(_CB.rxpBuffer);
|
|
_CB.rxpBuffer = NULL;
|
|
}
|
|
DC_END_FN();
|
|
return;
|
|
|
|
} /* CB_OnPacketReceived */
|
|
|
|
DCVOID DCAPI CClip::ClipDecoupleToClip (PTS_CLIP_PDU pData)
|
|
{
|
|
ULONG cbPDU ;
|
|
DC_BEGIN_FN("CClip::ClipDecoupleToClip");
|
|
// Allocate space for the PDU and its data, and then copy it
|
|
cbPDU = sizeof(TS_CLIP_PDU) + pData->dataLen ;
|
|
PDCVOID newBuffer = LocalAlloc(LPTR, cbPDU) ;
|
|
|
|
if (NULL != newBuffer)
|
|
DC_MEMCPY(newBuffer, pData, cbPDU) ;
|
|
else
|
|
return;
|
|
|
|
TRC_NRM((TB, _T("Pass %d bytes to clipboard thread"), cbPDU));
|
|
PostMessage(_CB.viewerWindow,
|
|
_CB.regMsg,
|
|
cbPDU,
|
|
(LPARAM) newBuffer);
|
|
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Open Event callback */
|
|
/****************************************************************************/
|
|
VOID VCAPITYPE VCEXPORT DCLOADDS CClip::ClipOpenEventFnEx(LPVOID lpUserParam,
|
|
DWORD openHandle,
|
|
UINT event,
|
|
LPVOID pData,
|
|
UINT32 dataLength,
|
|
UINT32 totalLength,
|
|
UINT32 dataFlags)
|
|
{
|
|
DC_BEGIN_FN("CClip::ClipOpenEventFnEx");
|
|
|
|
TRC_ASSERT(((VCManager*)lpUserParam != NULL), (TB, _T("lpUserParam is NULL, no instance data")));
|
|
if(!lpUserParam)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CClip* pClip = ((VCManager*)lpUserParam)->GetClip();
|
|
TRC_ASSERT((pClip != NULL), (TB, _T("pClip is NULL in ClipOpenEventFnEx")));
|
|
if(!pClip)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pClip->ClipInternalOpenEventFn(openHandle, event, pData, dataLength,
|
|
totalLength, dataFlags);
|
|
|
|
DC_END_FN();
|
|
return;
|
|
}
|
|
|
|
|
|
VOID VCAPITYPE VCEXPORT DCLOADDS CClip::ClipInternalOpenEventFn(DWORD openHandle,
|
|
UINT event,
|
|
LPVOID pData,
|
|
UINT32 dataLength,
|
|
UINT32 totalLength,
|
|
UINT32 dataFlags)
|
|
{
|
|
DC_BEGIN_FN("CClip::ClipOpenEventFn");
|
|
|
|
DC_IGNORE_PARAMETER(openHandle)
|
|
|
|
switch (event)
|
|
{
|
|
/********************************************************************/
|
|
/* Data received from Server */
|
|
/********************************************************************/
|
|
case CHANNEL_EVENT_DATA_RECEIVED:
|
|
{
|
|
TRC_NRM((TB, _T("Data in: handle %ld, len %ld (of %ld), flags %lx"),
|
|
openHandle, dataLength, totalLength, dataFlags)) ;
|
|
TRC_DATA_NRM("Data", pData, (DCUINT)dataLength) ;
|
|
ClipOnDataReceived(pData, dataLength, totalLength, dataFlags) ;
|
|
}
|
|
break;
|
|
|
|
/********************************************************************/
|
|
/* Write operation completed */
|
|
/********************************************************************/
|
|
case CHANNEL_EVENT_WRITE_COMPLETE:
|
|
case CHANNEL_EVENT_WRITE_CANCELLED:
|
|
{
|
|
TRC_NRM((TB, _T("Write %s %p"),
|
|
event == CHANNEL_EVENT_WRITE_COMPLETE ? "complete" : "cancelled",
|
|
pData));
|
|
ClipOnWriteComplete(pData);
|
|
}
|
|
break;
|
|
|
|
/********************************************************************/
|
|
/* Er, that didn't happen, did it? */
|
|
/********************************************************************/
|
|
default:
|
|
{
|
|
TRC_ERR((TB, _T("Unexpected event %d"), event));
|
|
}
|
|
break;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Init Event callback */
|
|
/****************************************************************************/
|
|
VOID VCAPITYPE VCEXPORT CClip::ClipInitEventFn(LPVOID pInitHandle,
|
|
UINT event,
|
|
LPVOID pData,
|
|
UINT dataLength)
|
|
{
|
|
DC_BEGIN_FN("CClip::ClipInitEventFn");
|
|
|
|
DC_IGNORE_PARAMETER(dataLength)
|
|
DC_IGNORE_PARAMETER(pData)
|
|
|
|
switch (event)
|
|
{
|
|
/********************************************************************/
|
|
/* Client initialized (no data) */
|
|
/********************************************************************/
|
|
case CHANNEL_EVENT_INITIALIZED:
|
|
{
|
|
TRC_NRM((TB, _T("CHANNEL_EVENT_INITIALIZED: %p"), pInitHandle));
|
|
ClipOnInitialized();
|
|
}
|
|
break;
|
|
|
|
/********************************************************************/
|
|
/* Connection established (data = name of Server) */
|
|
/********************************************************************/
|
|
case CHANNEL_EVENT_CONNECTED:
|
|
{
|
|
TRC_NRM((TB, _T("CHANNEL_EVENT_CONNECTED: %p, Server %s"),
|
|
pInitHandle, pData));
|
|
|
|
if (IsDataSyncReady()) {
|
|
ClipOnConnected(pInitHandle);
|
|
}
|
|
else {
|
|
TRC_ERR((TB,_T("data sync not ready on CHANNEL_EVENT_CONNECTED")));
|
|
}
|
|
}
|
|
break;
|
|
|
|
/********************************************************************/
|
|
/* Connection established with old Server, so no channel support */
|
|
/********************************************************************/
|
|
case CHANNEL_EVENT_V1_CONNECTED:
|
|
{
|
|
TRC_NRM((TB, _T("CHANNEL_EVENT_V1_CONNECTED: %p, Server %s"),
|
|
pInitHandle, pData));
|
|
}
|
|
break;
|
|
|
|
/********************************************************************/
|
|
/* Connection ended (no data) */
|
|
/********************************************************************/
|
|
case CHANNEL_EVENT_DISCONNECTED:
|
|
{
|
|
TRC_NRM((TB, _T("CHANNEL_EVENT_DISCONNECTED: %p"), pInitHandle));
|
|
ClipOnDisconnected(pInitHandle);
|
|
}
|
|
break;
|
|
|
|
/********************************************************************/
|
|
/* Client terminated (no data) */
|
|
/********************************************************************/
|
|
case CHANNEL_EVENT_TERMINATED:
|
|
{
|
|
TRC_NRM((TB, _T("CHANNEL_EVENT_TERMINATED: %p"), pInitHandle));
|
|
ClipOnTerm(pInitHandle);
|
|
}
|
|
break;
|
|
|
|
/********************************************************************/
|
|
/* Unknown event */
|
|
/********************************************************************/
|
|
default:
|
|
{
|
|
TRC_ERR((TB, _T("Unkown channel event %d: %p"), event, pInitHandle));
|
|
}
|
|
break;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Clip window proc */
|
|
/****************************************************************************/
|
|
LRESULT CALLBACK DCEXPORT DCLOADDS CClip::StaticClipViewerWndProc(HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
CClip* pClip = (CClip*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
if(WM_CREATE == message)
|
|
{
|
|
//pull out the this pointer and stuff it in the window class
|
|
LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam;
|
|
pClip = (CClip*)lpcs->lpCreateParams;
|
|
|
|
SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)pClip);
|
|
}
|
|
|
|
//
|
|
// Delegate the message to the appropriate instance
|
|
//
|
|
|
|
if(pClip)
|
|
{
|
|
return pClip->ClipViewerWndProc(hwnd, message, wParam, lParam);
|
|
}
|
|
else
|
|
{
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
LRESULT CALLBACK DCEXPORT DCLOADDS CClip::ClipViewerWndProc(HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
PTS_CLIP_PDU pClipPDU = NULL;
|
|
#ifndef OS_WINCE
|
|
DCUINT32 pduLen;
|
|
DCUINT32 dataLen;
|
|
PDCUINT32 pFormatID;
|
|
MSG msg;
|
|
#endif
|
|
DCBOOL drawRc;
|
|
DCBOOL gotRsp = FALSE;
|
|
LRESULT rc = 0;
|
|
HRESULT hr ;
|
|
|
|
DC_BEGIN_FN("CClip::ClipViewerWndProc");
|
|
|
|
// We first handle messages from the other thread
|
|
if (message == _CB.regMsg)
|
|
{
|
|
pClipPDU = (PTS_CLIP_PDU)lParam;
|
|
switch (pClipPDU->msgType)
|
|
{
|
|
case TS_CB_FORMAT_LIST:
|
|
{
|
|
TRC_NRM((TB, _T("TS_CB_FORMAT_LIST received")));
|
|
ClipOnFormatList(pClipPDU);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
TRC_ERR((TB, _T("Unknown event %d"), pClipPDU->msgType));
|
|
}
|
|
break;
|
|
}
|
|
|
|
TRC_NRM((TB, _T("Freeing processed PDU")));
|
|
LocalFree(pClipPDU);
|
|
|
|
DC_QUIT;
|
|
}
|
|
else if (message == WM_USER_CLEANUP_ON_TERM) {
|
|
|
|
TRC_NRM((TB, _T("Cleanup temp directory")));
|
|
|
|
if (0 != ClipCleanTempPath())
|
|
{
|
|
TRC_NRM((TB, _T("Failed while trying to clean temp directory"))) ;
|
|
}
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
switch (message)
|
|
{
|
|
case WM_CREATE:
|
|
{
|
|
/****************************************************************/
|
|
/* We've been created - check the state */
|
|
/****************************************************************/
|
|
CB_CHECK_STATE(CB_EVENT_WM_CREATE);
|
|
|
|
#ifndef OS_WINCE
|
|
/****************************************************************/
|
|
/* Add the window to the clipboard viewer chain. */
|
|
/****************************************************************/
|
|
_CB.nextViewer = SetClipboardViewer(hwnd);
|
|
#else
|
|
ghwndClip = hwnd;
|
|
InitializeCriticalSection(&gcsDataObj);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
/****************************************************************/
|
|
/* We're being destroyed - check the state */
|
|
/****************************************************************/
|
|
CB_CHECK_STATE(CB_EVENT_WM_DESTROY);
|
|
|
|
#ifndef OS_WINCE
|
|
/****************************************************************/
|
|
/* Remove ourselves from the CB Chain */
|
|
/****************************************************************/
|
|
ChangeClipboardChain(hwnd, _CB.nextViewer);
|
|
#endif
|
|
#ifdef OS_WINCE
|
|
ghwndClip = NULL;
|
|
EnterCriticalSection(&gcsDataObj);
|
|
if (gpDataObj)
|
|
{
|
|
gpDataObj->Release();
|
|
gpDataObj = NULL;
|
|
}
|
|
LeaveCriticalSection(&gcsDataObj);
|
|
DeleteCriticalSection(&gcsDataObj);
|
|
|
|
if (ghCeshellDll)
|
|
{
|
|
pfnEDList_Uninitialize();
|
|
FreeLibrary(ghCeshellDll);
|
|
}
|
|
#endif
|
|
_CB.nextViewer = NULL;
|
|
PostQuitMessage(0);
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
{
|
|
DestroyWindow(hwnd);
|
|
}
|
|
break;
|
|
|
|
#ifndef OS_WINCE
|
|
case WM_CHANGECBCHAIN:
|
|
{
|
|
/****************************************************************/
|
|
/* The CB viewer chain is chainging - check the state */
|
|
/****************************************************************/
|
|
CB_CHECK_STATE(CB_EVENT_WM_CHANGECBCHAIN);
|
|
|
|
/****************************************************************/
|
|
/* If the next window is closing, repair the chain. */
|
|
/****************************************************************/
|
|
if ((HWND)wParam == _CB.nextViewer)
|
|
{
|
|
_CB.nextViewer = (HWND) lParam;
|
|
}
|
|
else if (_CB.nextViewer != NULL)
|
|
{
|
|
/************************************************************/
|
|
/* pass the message to the next link. */
|
|
/************************************************************/
|
|
SendMessage(_CB.nextViewer, message, wParam, lParam);
|
|
}
|
|
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case WM_DRAWCLIPBOARD:
|
|
{
|
|
/****************************************************************/
|
|
/* The local clipboard contents have been changed. Check the */
|
|
/* state */
|
|
/****************************************************************/
|
|
if (ClipCheckState(CB_EVENT_WM_DRAWCLIPBOARD) != CB_TABLE_OK)
|
|
{
|
|
TRC_NRM((TB, _T("dropping drawcb - pass on to next viewer")));
|
|
/************************************************************/
|
|
/* check for state pending format list response */
|
|
/************************************************************/
|
|
if (_CB.state == CB_STATE_PENDING_FORMAT_LIST_RSP)
|
|
{
|
|
TRC_ALT((TB, _T("got a draw while processing last")));
|
|
/********************************************************/
|
|
/* we were still waiting for the server to acknowledge */
|
|
/* the last format list when we got a new one - when it */
|
|
/* does, we'll have to to send it the new one. */
|
|
/********************************************************/
|
|
_CB.moreToDo = TRUE;
|
|
}
|
|
|
|
if (_CB.nextViewer != NULL)
|
|
{
|
|
/********************************************************/
|
|
/* But not before we pass the message to the next link. */
|
|
/********************************************************/
|
|
SendMessage(_CB.nextViewer, message, wParam, lParam);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* If it wasn't us that generated this change, then notify the */
|
|
/* remote */
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("CB contents have changed...")));
|
|
drawRc = FALSE;
|
|
_CB.moreToDo = FALSE;
|
|
#ifndef OS_WINCE
|
|
LPDATAOBJECT pIDataObject = NULL;
|
|
|
|
if (_pClipData != NULL) {
|
|
_pClipData->QueryInterface(IID_IDataObject, (PPVOID) &pIDataObject) ;
|
|
hr = OleIsCurrentClipboard(pIDataObject) ;
|
|
#else
|
|
hr = S_FALSE;
|
|
#endif
|
|
|
|
if ((S_FALSE == hr))
|
|
{
|
|
TRC_NRM((TB, _T("...and it wasn't us")));
|
|
#ifdef OS_WINCE
|
|
if (_CB.fFileCutCopyOn)
|
|
DeleteDirectory(_CB.baseTempDirW);//Temp space is at a premium on CE. So delete any copied files now
|
|
#endif
|
|
drawRc = ClipDrawClipboard(TRUE);
|
|
}
|
|
else
|
|
{
|
|
TRC_NRM((TB, _T("...and it was us - ignoring")));
|
|
}
|
|
#ifndef OS_WINCE
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* Maybe pass on the draw clipboard message? */
|
|
/****************************************************************/
|
|
if (_CB.nextViewer != NULL)
|
|
{
|
|
TRC_NRM((TB, _T("Notify next viewer")));
|
|
SendMessage(_CB.nextViewer, message, wParam, lParam);
|
|
}
|
|
if (pIDataObject)
|
|
{
|
|
pIDataObject->Release();
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
|
|
case WM_EMPTY_CLIPBOARD:
|
|
{
|
|
/****************************************************************/
|
|
/* Open the clipboard if needed */
|
|
/****************************************************************/
|
|
if ((!_CB.clipOpen) && !OpenClipboard(NULL))
|
|
{
|
|
UINT count = (DCUINT) wParam;
|
|
|
|
TRC_ERR((TB, _T("Failed to open CB when emptying required")));
|
|
|
|
// Unfortunately, we are in the racing condition with the app that
|
|
// has the clipboard open. So, we need to keep trying until the app
|
|
// closes the clipboard.
|
|
if (count++ < 10) {
|
|
|
|
#ifdef OS_WIN32
|
|
Sleep(0);
|
|
#endif
|
|
PostMessage(_CB.viewerWindow, WM_EMPTY_CLIPBOARD, count, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/************************************************************/
|
|
/* It was/is open */
|
|
/************************************************************/
|
|
TRC_NRM((TB, _T("CB opened")));
|
|
|
|
_CB.clipOpen = TRUE;
|
|
|
|
/************************************************************/
|
|
/* Empty it */
|
|
/************************************************************/
|
|
if (!EmptyClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("EmptyClipboard");
|
|
}
|
|
|
|
/************************************************************/
|
|
/* update the state */
|
|
/************************************************************/
|
|
CB_SET_STATE(CB_STATE_LOCAL_CB_OWNER, CB_TRACE_EVENT_WM_EMPTY_CLIPBOARD);
|
|
}
|
|
|
|
|
|
/****************************************************************/
|
|
/* Ensure that we close the local CB */
|
|
/****************************************************************/
|
|
if (_CB.clipOpen)
|
|
{
|
|
_CB.clipOpen = FALSE;
|
|
if (!CloseClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseClipboard");
|
|
}
|
|
TRC_NRM((TB, _T("CB closed")));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE_CLIPBOARD:
|
|
{
|
|
_CB.pendingClose = FALSE;
|
|
TRC_DBG((TB, _T("Maybe close clipboard on WM_CLOSE_CLIPBOARD")));
|
|
if (_CB.clipOpen)
|
|
{
|
|
TRC_NRM((TB, _T("Closing clipboard on WM_CLOSE_CLIPBOARD")));
|
|
_CB.clipOpen = FALSE;
|
|
if (!CloseClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseClipboard");
|
|
}
|
|
TRC_DBG((TB, _T("CB closed on WM_CLOSE_CLIPBOARD")));
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
/****************************************************************/
|
|
/* Ignore all other messages. */
|
|
/****************************************************************/
|
|
rc = DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
break;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return(rc);
|
|
} /* ClipViewerWndProc */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* ClipChannelEntry - called from VirtualChannelEntry in vcint.cpp */
|
|
/****************************************************************************/
|
|
BOOL VCAPITYPE VCEXPORT CClip::ClipChannelEntry(PCHANNEL_ENTRY_POINTS_EX pEntryPoints)
|
|
{
|
|
DC_BEGIN_FN("CClip::ClipChannelEntry");
|
|
|
|
TRC_NRM((TB, _T("Size %ld, Init %p, Open %p, Close %p, Write %p"),
|
|
pEntryPoints->cbSize,
|
|
pEntryPoints->pVirtualChannelInitEx, pEntryPoints->pVirtualChannelOpenEx,
|
|
pEntryPoints->pVirtualChannelCloseEx, pEntryPoints->pVirtualChannelWriteEx));
|
|
|
|
/************************************************************************/
|
|
/* Save the function pointers -- the memory pointed to by pEntryPoints */
|
|
/* is only valid for the duration of this call. */
|
|
/************************************************************************/
|
|
DC_MEMCPY(&(_CB.channelEP), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_EX));
|
|
TRC_NRM((TB, _T("Save entry points %p at address %p"),
|
|
pEntryPoints, &(_CB.channelEP)));
|
|
|
|
DC_END_FN();
|
|
return TRUE;
|
|
}
|
|
|
|
DCINT DCAPI CClip::ClipGetData (DCUINT cfFormat)
|
|
{
|
|
PTS_CLIP_PDU pClipPDU = NULL;
|
|
DCUINT32 pduLen;
|
|
DCUINT32 dataLen;
|
|
PDCUINT32 pFormatID;
|
|
BOOL success = 0 ;
|
|
|
|
DC_BEGIN_FN("CClip::ClipGetData");
|
|
|
|
CB_CHECK_STATE(CB_EVENT_WM_RENDERFORMAT);
|
|
|
|
/****************************************************************/
|
|
/* and record the requested format */
|
|
/****************************************************************/
|
|
_CB.pendingClientID = cfFormat ;
|
|
_CB.pendingServerID = ClipRemoteFormatFromLocalID
|
|
(_CB.pendingClientID);
|
|
if (!_CB.pendingServerID)
|
|
{
|
|
TRC_NRM((TB, _T("Client format %d not supported/found. Failing"), _CB.pendingClientID)) ;
|
|
DC_QUIT ;
|
|
}
|
|
TRC_NRM((TB, _T("Render format received for %d (server ID %d)"),
|
|
_CB.pendingClientID, _CB.pendingServerID));
|
|
|
|
dataLen = sizeof(DCUINT32);
|
|
pduLen = sizeof(TS_CLIP_PDU) + dataLen;
|
|
|
|
// We can use the permanent send buffer for this
|
|
TRC_NRM((TB, _T("Get perm TX buffer")));
|
|
pClipPDU = ClipGetPermBuf();
|
|
|
|
// Fill in the PDU
|
|
DC_MEMSET(pClipPDU, 0, sizeof(*pClipPDU));
|
|
pClipPDU->msgType = TS_CB_FORMAT_DATA_REQUEST;
|
|
pClipPDU->dataLen = dataLen;
|
|
pFormatID = (PDCUINT32)(pClipPDU->data);
|
|
*pFormatID = (DCUINT32)_CB.pendingServerID;
|
|
|
|
// Send the PDU
|
|
TRC_NRM((TB, _T("Sending format data request")));
|
|
success = (_CB.channelEP.pVirtualChannelWriteEx
|
|
(_CB.initHandle, _CB.channelHandle, pClipPDU, pduLen, pClipPDU)
|
|
== CHANNEL_RC_OK) ;
|
|
if (!success) {
|
|
TRC_ERR((TB, _T("Failed VC write: setting clip data to NULL")));
|
|
ClipFreeBuf((PDCUINT8)pClipPDU);
|
|
SetClipboardData(_CB.pendingClientID, NULL);
|
|
// Yes, the exit point is just below, but it may not be always be
|
|
DC_QUIT ;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
// Update the state
|
|
if (success)
|
|
CB_SET_STATE(CB_STATE_PENDING_FORMAT_DATA_RSP, CB_EVENT_WM_RENDERFORMAT);
|
|
|
|
DC_END_FN();
|
|
return success ;
|
|
}
|
|
|
|
#ifdef OS_WINCE
|
|
DCVOID CClip::ClipFixupRichTextFormats(UINT uRtf1, UINT uRtf2)
|
|
{
|
|
DC_BEGIN_FN("CClip::ClipFixupRichTextFormats");
|
|
if (uRtf1 == 0xffffffff)
|
|
{
|
|
TRC_ASSERT((uRtf2 == 0xffffffff), (TB, _T("uRtf2 is invalid")));
|
|
return;
|
|
}
|
|
|
|
if (uRtf2 == 0xffffffff)
|
|
{
|
|
_CB.idMap[uRtf1].clientID = RegisterClipboardFormat(CFSTR_RTF);
|
|
SetClipboardData(_CB.idMap[uRtf1].clientID, NULL);
|
|
TRC_DBG((TB, _T("Adding format '%s', server ID %d, client ID %d"), CFSTR_RTF, _CB.idMap[uRtf1].serverID, _CB.idMap[uRtf1].clientID));
|
|
return;
|
|
}
|
|
|
|
DCTCHAR *pFormat1 = CFSTR_RTF, *pFormat2 = CFSTR_RTFNOOBJECTS;
|
|
if (_CB.idMap[uRtf1].serverID > _CB.idMap[uRtf2].serverID)
|
|
{
|
|
pFormat1 = CFSTR_RTFNOOBJECTS;
|
|
pFormat2 = CFSTR_RTF;
|
|
}
|
|
|
|
_CB.idMap[uRtf1].clientID = RegisterClipboardFormat(pFormat1);
|
|
_CB.idMap[uRtf2].clientID = RegisterClipboardFormat(pFormat2);
|
|
|
|
TRC_DBG((TB, _T("Adding format '%s', server ID %d, client ID %d"), CFSTR_RTF, _CB.idMap[uRtf1].serverID, _CB.idMap[uRtf1].clientID));
|
|
SetClipboardData(_CB.idMap[uRtf1].clientID, NULL);
|
|
|
|
TRC_DBG((TB, _T("Adding format '%s', server ID %d, client ID %d"), CFSTR_RTFNOOBJECTS, _CB.idMap[uRtf2].serverID, _CB.idMap[uRtf2].clientID));
|
|
SetClipboardData(_CB.idMap[uRtf2].clientID, NULL);
|
|
|
|
DC_END_FN();
|
|
}
|
|
#endif //OS_WINCE
|
|
|
|
CClipData::CClipData(PCClip pClip)
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
DC_BEGIN_FN("CClipData::CClipData") ;
|
|
|
|
_pClip = pClip ;
|
|
_cRef = 0 ;
|
|
_pImpIDataObject = NULL ;
|
|
|
|
//
|
|
// Initialize the single instance critical
|
|
// section lock. This is used to ensure only one
|
|
// thread is accessing _pImpIDataObject at the time
|
|
//
|
|
//
|
|
__try
|
|
{
|
|
InitializeCriticalSection(&_csLock);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
status = GetExceptionCode();
|
|
}
|
|
|
|
if(ERROR_SUCCESS == status)
|
|
{
|
|
_fLockInitialized = TRUE;
|
|
}
|
|
else
|
|
{
|
|
_fLockInitialized = FALSE;
|
|
TRC_ERR((TB,_T("InitializeCriticalSection failed 0x%x."),status));
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
//
|
|
// Call Release() on the contained IDataObject implementation. This is necessary because
|
|
// SetNumFormats() calls AddRef() and if we are terminating, then there must be a way
|
|
// to balance this AddRef() so that the circular reference between CClipData and
|
|
// CImpIDataObject will be broken.
|
|
//
|
|
|
|
void CClipData::TearDown()
|
|
{
|
|
if (_pImpIDataObject != NULL)
|
|
{
|
|
_pImpIDataObject->Release();
|
|
_pImpIDataObject = NULL;
|
|
}
|
|
}
|
|
|
|
CClipData::~CClipData(void)
|
|
{
|
|
DC_BEGIN_FN("CClipData::~CClipData");
|
|
|
|
if(_fLockInitialized)
|
|
{
|
|
DeleteCriticalSection(&_csLock);
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
HRESULT DCINTERNAL CClipData::SetNumFormats(ULONG numFormats)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
DC_BEGIN_FN("CClipData::SetNumFormats");
|
|
|
|
if (_fLockInitialized) {
|
|
EnterCriticalSection(&_csLock);
|
|
if (_pImpIDataObject)
|
|
{
|
|
_pImpIDataObject->Release();
|
|
_pImpIDataObject = NULL;
|
|
}
|
|
_pImpIDataObject = new CImpIDataObject(this) ;
|
|
if (_pImpIDataObject == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Unable to create IDataObject")));
|
|
hr = E_OUTOFMEMORY;
|
|
DC_QUIT;
|
|
}
|
|
else
|
|
{
|
|
_pImpIDataObject->AddRef() ;
|
|
hr = _pImpIDataObject->Init(numFormats);
|
|
DC_QUIT_ON_FAIL(hr);
|
|
}
|
|
LeaveCriticalSection(&_csLock);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
DCVOID CClipData::SetClipData(HGLOBAL hGlobal, DCUINT clipType)
|
|
{
|
|
DC_BEGIN_FN("CClipData::SetClipData");
|
|
|
|
if (_fLockInitialized) {
|
|
EnterCriticalSection(&_csLock);
|
|
if (_pImpIDataObject != NULL) {
|
|
_pImpIDataObject->SetClipData(hGlobal, clipType) ;
|
|
}
|
|
LeaveCriticalSection(&_csLock);
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
STDMETHODIMP CClipData::QueryInterface(REFIID riid, PPVOID ppv)
|
|
{
|
|
DC_BEGIN_FN("CClipData::QueryInterface");
|
|
|
|
//set ppv to NULL just in case the interface isn't found
|
|
*ppv=NULL;
|
|
|
|
if (IID_IUnknown==riid) {
|
|
*ppv=this;
|
|
//AddRef any interface we'll return.
|
|
((LPUNKNOWN)*ppv)->AddRef();
|
|
}
|
|
|
|
if (IID_IDataObject==riid) {
|
|
if (_fLockInitialized) {
|
|
EnterCriticalSection(&_csLock);
|
|
*ppv=_pImpIDataObject ;
|
|
|
|
if (_pImpIDataObject != NULL) {
|
|
//AddRef any interface we'll return.
|
|
((LPUNKNOWN)*ppv)->AddRef();
|
|
}
|
|
|
|
LeaveCriticalSection(&_csLock);
|
|
}
|
|
}
|
|
|
|
if (NULL==*ppv)
|
|
return ResultFromScode(E_NOINTERFACE);
|
|
|
|
DC_END_FN();
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CClipData::AddRef(void)
|
|
{
|
|
DC_BEGIN_FN("CClipData::AddRef");
|
|
DC_END_FN();
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CClipData::Release(void)
|
|
{
|
|
LONG cRef;
|
|
DC_BEGIN_FN("CClipData::Release");
|
|
|
|
cRef = InterlockedDecrement(&_cRef);
|
|
|
|
if (cRef == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return cRef;
|
|
}
|
|
|
|
CImpIDataObject::CImpIDataObject(LPUNKNOWN lpUnk)
|
|
{
|
|
#ifndef OS_WINCE
|
|
byte i ;
|
|
#endif
|
|
DC_BEGIN_FN("CImpIDataObject::CImplDataObject") ;
|
|
|
|
_numFormats = 0 ;
|
|
_maxNumFormats = 0 ;
|
|
_cRef = 0 ;
|
|
_pUnkOuter = lpUnk ;
|
|
if (_pUnkOuter)
|
|
{
|
|
_pUnkOuter->AddRef();
|
|
}
|
|
_pFormats = NULL ;
|
|
_pSTGMEDIUM = NULL ;
|
|
_lastFormatRequested = 0 ;
|
|
_cfDropEffect = (CLIPFORMAT) RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT) ;
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
HRESULT CImpIDataObject::Init(ULONG numFormats)
|
|
{
|
|
DC_BEGIN_FN("CImpIDataObject::Init") ;
|
|
HRESULT hr = S_OK;
|
|
|
|
_maxNumFormats = numFormats ;
|
|
|
|
// Allocate space for the formats only
|
|
if (_pFormats) {
|
|
LocalFree(_pFormats);
|
|
}
|
|
_pFormats = (LPFORMATETC) LocalAlloc(LPTR,_maxNumFormats*sizeof(FORMATETC)) ;
|
|
if (NULL == _pFormats) {
|
|
TRC_ERR((TB,_T("failed to allocate _pFormats")));
|
|
hr = E_OUTOFMEMORY;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (_pSTGMEDIUM) {
|
|
LocalFree(_pSTGMEDIUM);
|
|
}
|
|
_pSTGMEDIUM = (STGMEDIUM*) LocalAlloc(LPTR, sizeof(STGMEDIUM)) ;
|
|
if (NULL == _pSTGMEDIUM)
|
|
{
|
|
TRC_ERR((TB,_T("Failed to allocate STGMEDIUM")));
|
|
hr = E_OUTOFMEMORY;
|
|
DC_QUIT;
|
|
}
|
|
|
|
_pSTGMEDIUM->tymed = TYMED_HGLOBAL ;
|
|
_pSTGMEDIUM->pUnkForRelease = NULL ;
|
|
_pSTGMEDIUM->hGlobal = NULL ;
|
|
|
|
_uiSTGType = 0;
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
DCVOID CImpIDataObject::SetClipData(HGLOBAL hGlobal, DCUINT clipType)
|
|
{
|
|
DC_BEGIN_FN("CImpIDataObject::SetClipData");
|
|
|
|
if (!_pSTGMEDIUM)
|
|
_pSTGMEDIUM = (STGMEDIUM*) LocalAlloc(LPTR, sizeof(STGMEDIUM)) ;
|
|
if (NULL != _pSTGMEDIUM)
|
|
{
|
|
if (CF_PALETTE == clipType) {
|
|
_pSTGMEDIUM->tymed = TYMED_GDI;
|
|
}
|
|
else if (CF_METAFILEPICT == clipType) {
|
|
_pSTGMEDIUM->tymed = TYMED_MFPICT;
|
|
}
|
|
else {
|
|
_pSTGMEDIUM->tymed = TYMED_HGLOBAL;
|
|
}
|
|
|
|
_pSTGMEDIUM->pUnkForRelease = NULL ;
|
|
FreeSTGMEDIUM();
|
|
|
|
_pSTGMEDIUM->hGlobal = hGlobal ;
|
|
_uiSTGType = clipType;
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
DCVOID
|
|
CImpIDataObject::FreeSTGMEDIUM(void)
|
|
{
|
|
if ( NULL == _pSTGMEDIUM->hGlobal )
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch( _uiSTGType )
|
|
{
|
|
case CF_PALETTE:
|
|
DeleteObject( _pSTGMEDIUM->hGlobal );
|
|
break;
|
|
#ifndef OS_WINCE
|
|
case CF_METAFILEPICT:
|
|
{
|
|
LPMETAFILEPICT pMFPict = (LPMETAFILEPICT)GlobalLock( _pSTGMEDIUM->hGlobal );
|
|
if ( NULL != pMFPict )
|
|
{
|
|
if ( NULL != pMFPict->hMF )
|
|
{
|
|
DeleteMetaFile( pMFPict->hMF );
|
|
}
|
|
GlobalUnlock( _pSTGMEDIUM->hGlobal );
|
|
}
|
|
GlobalFree( _pSTGMEDIUM->hGlobal );
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
GlobalFree( _pSTGMEDIUM->hGlobal );
|
|
}
|
|
_pSTGMEDIUM->hGlobal = NULL;
|
|
}
|
|
|
|
CImpIDataObject::~CImpIDataObject(void)
|
|
{
|
|
DC_BEGIN_FN("CImpIDataObject::~CImplDataObject") ;
|
|
|
|
if (_pFormats)
|
|
LocalFree(_pFormats) ;
|
|
|
|
if (_pSTGMEDIUM)
|
|
{
|
|
FreeSTGMEDIUM();
|
|
LocalFree(_pSTGMEDIUM) ;
|
|
}
|
|
|
|
if (_pUnkOuter)
|
|
{
|
|
_pUnkOuter->Release();
|
|
_pUnkOuter = NULL;
|
|
}
|
|
DC_END_FN();
|
|
}
|
|
|
|
// IUnknown members
|
|
// - Delegate to "outer" IUnknown
|
|
STDMETHODIMP CImpIDataObject::QueryInterface(REFIID riid, PPVOID ppv)
|
|
{
|
|
DC_BEGIN_FN("CImpIDataObject::QueryInterface");
|
|
DC_END_FN();
|
|
return _pUnkOuter->QueryInterface(riid, ppv);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CImpIDataObject::AddRef(void)
|
|
{
|
|
DC_BEGIN_FN("CImpIDataObject::AddRef");
|
|
|
|
InterlockedIncrement(&_cRef);
|
|
|
|
DC_END_FN();
|
|
return _pUnkOuter->AddRef();
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CImpIDataObject::Release(void)
|
|
{
|
|
LONG cRef;
|
|
|
|
DC_BEGIN_FN("CImpIDataObject::Release");
|
|
|
|
_pUnkOuter->Release();
|
|
|
|
cRef = InterlockedDecrement(&_cRef) ;
|
|
if (0 == cRef)
|
|
delete this;
|
|
|
|
DC_END_FN() ;
|
|
return cRef;
|
|
}
|
|
|
|
// IDataObject members
|
|
// ***************************************************************************
|
|
// CImpIDataObject::GetData
|
|
// - Here, we have to wait for the data to actually get here before we return.
|
|
// ***************************************************************************
|
|
STDMETHODIMP CImpIDataObject::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
|
|
{
|
|
HRESULT result = E_FAIL; // Assume we fail until we know we haven't
|
|
#ifndef OS_WINCE
|
|
TCHAR formatName[TS_FORMAT_NAME_LEN] ;
|
|
byte charSize ;
|
|
BOOL fWide ;
|
|
WCHAR* fileListW ;
|
|
HPDCVOID pFilename ;
|
|
HPDCVOID pOldFilename ;
|
|
#endif
|
|
HGLOBAL hData = NULL ;
|
|
HPDCVOID pData ;
|
|
HPDCVOID pOldData ;
|
|
#ifndef OS_WINCE
|
|
DROPFILES* pDropFiles ;
|
|
DROPFILES tempDropfile ;
|
|
ULONG oldSize ;
|
|
ULONG newSize ;
|
|
#endif
|
|
DWORD eventSignaled ;
|
|
DWORD* pDropEffect ;
|
|
#ifndef OS_WINCE
|
|
char* fileList ;
|
|
#endif
|
|
PCClip pClip ;
|
|
|
|
DC_BEGIN_FN("CImpIDataObject::GetData");
|
|
|
|
// Should never occur, but we check for sanity's sake
|
|
if (NULL == (PCClipData)_pUnkOuter)
|
|
{
|
|
TRC_ERR((TB, _T("Ptr to outer unknown is NULL"))) ;
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
if (NULL == ((PCClipData)_pUnkOuter)->_pClip)
|
|
{
|
|
TRC_ERR((TB, _T("Ptr to clip class is NULL"))) ;
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
// Since we need to have the CClip class do work for us,
|
|
// we pull out a pointer to it that we stored in the beginning
|
|
pClip = (PCClip) ((PCClipData)_pUnkOuter)->_pClip ;
|
|
|
|
if (!_pSTGMEDIUM)
|
|
{
|
|
TRC_ERR((TB, _T("Transfer medium (STGMEDIUM) is NULL"))) ;
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
TRC_ASSERT( pClip->IsDataSyncReady(),
|
|
(TB, _T("Data Sync not ready")));
|
|
|
|
if (!_pSTGMEDIUM->hGlobal || (pFE->cfFormat != _lastFormatRequested))
|
|
{
|
|
TRC_ASSERT( pClip->IsDataSyncReady(),
|
|
(TB,_T("data sync is NULL")));
|
|
|
|
ResetEvent(pClip->_GetDataSync[TS_RESET_EVENT]) ;
|
|
if (!((PCClipData)_pUnkOuter)->_pClip->ClipGetData(pFE->cfFormat))
|
|
{
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
eventSignaled = WaitForMultipleObjects(
|
|
TS_NUM_EVENTS,
|
|
((PCClipData)_pUnkOuter)->_pClip->_GetDataSync,
|
|
FALSE,
|
|
INFINITE
|
|
) ;
|
|
if ((WAIT_OBJECT_0+TS_RESET_EVENT) == eventSignaled)
|
|
{
|
|
TRC_NRM((TB, _T("Other thread told us to reset. Failing GetData"))) ;
|
|
ResetEvent(((PCClipData)_pUnkOuter)->_pClip->_GetDataSync[TS_RESET_EVENT]) ;
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
// Make sure that we actually got data from the server.
|
|
|
|
if (_pSTGMEDIUM->hGlobal == NULL) {
|
|
TRC_ERR((TB, _T("No format data received from server!")));
|
|
result = E_FAIL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
#ifndef OS_WINCE
|
|
if (CF_HDROP == pFE->cfFormat)
|
|
{
|
|
// if we got an HDROP and we're not NT/2000, we check to see if we
|
|
// have to convert to ansi; otherwise, we're done
|
|
if (pClip->GetOsMinorType() != TS_OSMINORTYPE_WINDOWS_NT)
|
|
{
|
|
pDropFiles = (DROPFILES*) GlobalLock(_pSTGMEDIUM->hGlobal) ;
|
|
if (!pDropFiles)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to lock %p"), hData)) ;
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
// if we definitely have wide characters, then convert
|
|
if (pDropFiles->fWide)
|
|
{
|
|
// temporarily store the original's base dropfile info
|
|
tempDropfile.pFiles = pDropFiles->pFiles ;
|
|
tempDropfile.pt = pDropFiles->pt ;
|
|
tempDropfile.fNC = pDropFiles->fNC ;
|
|
tempDropfile.fWide = 0 ; // we are converting to ANSI now
|
|
|
|
// We divide by the size of wchar_t because we need half as many
|
|
// bytes with ansi as opposed to fWide character strings
|
|
oldSize = (ULONG) GlobalSize(_pSTGMEDIUM->hGlobal) - pDropFiles->pFiles ;
|
|
newSize = oldSize / sizeof(WCHAR) ;
|
|
fileList = (char*) (LocalAlloc(LPTR,newSize)) ;
|
|
if ( NULL == fileList )
|
|
{
|
|
TRC_ERR((TB, _T("Unable to allocate %d bytes"), newSize ));
|
|
result = E_FAIL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// This will convert the wide HDROP filelist to ansi, and
|
|
// put the ansi version into filelist
|
|
// 11-12
|
|
// pDropFiles is probably "foo\0bar\0baz\0\0"
|
|
// I don't believe WC2MB will go past the first \0
|
|
if (!WideCharToMultiByte(GetACP(), NULL, (wchar_t*)
|
|
((byte*) pDropFiles + pDropFiles->pFiles),
|
|
newSize, fileList,
|
|
newSize, NULL, NULL))
|
|
{
|
|
TRC_ERR((TB, _T("Unable convert wide to ansi"), newSize)) ;
|
|
LocalFree( fileList );
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
// Output the first filename for a sanity check
|
|
TRC_NRM((TB, _T("Filename 1 = %hs"), fileList)) ;
|
|
|
|
GlobalUnlock(_pSTGMEDIUM->hGlobal) ;
|
|
// Reallocate the space for the dropfile
|
|
hData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE,
|
|
newSize + tempDropfile.pFiles) ;
|
|
if (!hData)
|
|
{
|
|
TRC_ERR((TB, _T("Allocate memory; err=%d"), GetLastError())) ;
|
|
LocalFree( fileList );
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
pDropFiles = (DROPFILES*) GlobalLock(hData) ;
|
|
if (!pDropFiles)
|
|
{
|
|
TRC_ERR((TB, _T("Unable to lock %p"), hData)) ;
|
|
LocalFree( fileList );
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
pDropFiles->pFiles = tempDropfile.pFiles ;
|
|
pDropFiles->pt = tempDropfile.pt ;
|
|
pDropFiles->fNC = tempDropfile.fNC ;
|
|
pDropFiles->fWide = tempDropfile.fWide ;
|
|
DC_MEMCPY((byte*) pDropFiles + pDropFiles->pFiles,
|
|
fileList, newSize) ;
|
|
// Output the first filename for another sanity check
|
|
TRC_NRM((TB, _T("Filename = %s"), (byte*) pDropFiles + pDropFiles->pFiles)) ;
|
|
LocalFree( fileList );
|
|
}
|
|
GlobalUnlock(hData) ;
|
|
GlobalFree(_pSTGMEDIUM->hGlobal) ;
|
|
_pSTGMEDIUM->hGlobal = hData ;
|
|
}
|
|
}
|
|
#else
|
|
if (gfmtShellPidlArray == pFE->cfFormat)
|
|
{
|
|
HANDLE hNewData = HDropToIDList(_pSTGMEDIUM->hGlobal);
|
|
GlobalFree(_pSTGMEDIUM->hGlobal);
|
|
_pSTGMEDIUM->hGlobal = hNewData;
|
|
}
|
|
#endif
|
|
// We check the dropeffect format, because we strip out
|
|
// shortcuts/links, and store the dropeffects. The dropeffect is
|
|
// what some apps (explorer) use to decide if they should copy, move
|
|
// or link
|
|
else if (_cfDropEffect == pFE->cfFormat)
|
|
{
|
|
if (GlobalSize(_pSTGMEDIUM->hGlobal) < sizeof(DWORD)) {
|
|
TRC_ERR((TB, _T("Unexpected global memory size!")));
|
|
result = E_FAIL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
pDropEffect = (DWORD*) GlobalLock(_pSTGMEDIUM->hGlobal) ;
|
|
if (!pDropEffect)
|
|
{
|
|
TRC_NRM((TB, _T("Unable to lock %p"), _pSTGMEDIUM->hGlobal)) ;
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
// Strip out shortcuts/links
|
|
*pDropEffect = *pDropEffect ^ DROPEFFECT_LINK ;
|
|
// Strip out moves
|
|
*pDropEffect = *pDropEffect ^ DROPEFFECT_MOVE ;
|
|
pClip->SetDropEffect(*pDropEffect) ;
|
|
if (GlobalUnlock(_pSTGMEDIUM->hGlobal))
|
|
{
|
|
TRC_ASSERT(GetLastError() == NO_ERROR,
|
|
(TB, _T("Unable to unlock HGLOBAL we just locked"))) ;
|
|
}
|
|
}
|
|
pSTM->tymed = _pSTGMEDIUM->tymed ;
|
|
pSTM->hGlobal = _pSTGMEDIUM->hGlobal ;
|
|
_pSTGMEDIUM->hGlobal = NULL;
|
|
pSTM->pUnkForRelease = _pSTGMEDIUM->pUnkForRelease ;
|
|
result = S_OK ;
|
|
DC_QUIT ;
|
|
}
|
|
else
|
|
{
|
|
pSTM->tymed = _pSTGMEDIUM->tymed ;
|
|
pSTM->hGlobal = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE,
|
|
GlobalSize(_pSTGMEDIUM->hGlobal)) ;
|
|
pData = GlobalLock(pSTM->hGlobal) ;
|
|
pOldData = GlobalLock(_pSTGMEDIUM->hGlobal) ;
|
|
if (!pData || !pOldData)
|
|
return E_FAIL ;
|
|
DC_MEMCPY(pData, pOldData, GlobalSize(_pSTGMEDIUM->hGlobal)) ;
|
|
GlobalUnlock(pSTM->hGlobal) ;
|
|
GlobalUnlock(_pSTGMEDIUM->hGlobal) ;
|
|
|
|
pSTM->pUnkForRelease = _pSTGMEDIUM->pUnkForRelease ;
|
|
}
|
|
|
|
if (!pSTM->hGlobal)
|
|
{
|
|
TRC_NRM((TB, _T("Clipboard data request failed"))) ;
|
|
return E_FAIL ;
|
|
}
|
|
DC_EXIT_POINT:
|
|
|
|
DC_END_FN();
|
|
return result ;
|
|
}
|
|
|
|
STDMETHODIMP CImpIDataObject::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
|
|
{
|
|
DC_BEGIN_FN("CImpIDataObject::GetDataHere") ;
|
|
DC_END_FN();
|
|
return ResultFromScode(E_NOTIMPL) ;
|
|
}
|
|
|
|
STDMETHODIMP CImpIDataObject::QueryGetData(LPFORMATETC pFE)
|
|
{
|
|
ULONG i = 0 ;
|
|
HRESULT hr = DV_E_CLIPFORMAT ;
|
|
|
|
DC_BEGIN_FN("CImpIDataObject::QueryGetData") ;
|
|
|
|
TRC_NRM((TB, _T("Format ID %d requested"), pFE->cfFormat)) ;
|
|
while (i < _numFormats)
|
|
{
|
|
if (_pFormats[i].cfFormat == pFE->cfFormat) {
|
|
hr = S_OK ;
|
|
break ;
|
|
}
|
|
i++ ;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return hr ;
|
|
}
|
|
|
|
STDMETHODIMP CImpIDataObject::GetCanonicalFormatEtc(LPFORMATETC pFEIn, LPFORMATETC pFEOut)
|
|
{
|
|
DC_BEGIN_FN("CImpIDataObject::GetCanonicalFormatEtc") ;
|
|
DC_END_FN();
|
|
return ResultFromScode(E_NOTIMPL) ;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// CImpIDataObject::SetData
|
|
// - Due to the fact that the RDP only passes the simple clipboard format, and
|
|
// the fact that we obtain all of our clipboard data from memory later, pSTM
|
|
// is really ignored at this point. It isn't until GetData is called that
|
|
// the remote clipboard data is received, and a valid global memory handle
|
|
// is generated.
|
|
// - Thus, pSTM and fRelease are ignored.
|
|
// - So out _pSTGMEDIUM is generated using generic values
|
|
// ***************************************************************************
|
|
|
|
STDMETHODIMP CImpIDataObject::SetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM, BOOL fRelease)
|
|
{
|
|
TCHAR formatName[TS_FORMAT_NAME_LEN] = {0} ;
|
|
byte i ;
|
|
DC_BEGIN_FN("CImpIDataObject::SetData");
|
|
|
|
DC_IGNORE_PARAMETER(pSTM) ;
|
|
|
|
// Reset the the last format requested to 0
|
|
_lastFormatRequested = 0 ;
|
|
|
|
TRC_NRM((TB, _T("Adding format %d to IDataObject"), pFE->cfFormat)) ;
|
|
|
|
if (_numFormats < _maxNumFormats)
|
|
{
|
|
for (i=0; i < _numFormats; i++)
|
|
{
|
|
if (pFE->cfFormat == _pFormats[i].cfFormat)
|
|
{
|
|
TRC_NRM((TB, _T("Duplicate format. Discarded"))) ;
|
|
return DV_E_FORMATETC ;
|
|
}
|
|
}
|
|
_pFormats[_numFormats] = *pFE ;
|
|
_numFormats++ ;
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Cannot add any more formats"))) ;
|
|
return E_FAIL ;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return S_OK ;
|
|
}
|
|
|
|
STDMETHODIMP CImpIDataObject::EnumFormatEtc(DWORD dwDir, LPENUMFORMATETC *ppEnum)
|
|
{
|
|
PCEnumFormatEtc pEnum;
|
|
|
|
*ppEnum=NULL;
|
|
|
|
/*
|
|
* From an external point of view there are no SET formats,
|
|
* because we want to allow the user of this component object
|
|
* to be able to stuff ANY format in via Set. Only external
|
|
* users will call EnumFormatEtc and they can only Get.
|
|
*/
|
|
|
|
switch (dwDir)
|
|
{
|
|
case DATADIR_GET:
|
|
pEnum=new CEnumFormatEtc(_pUnkOuter);
|
|
break;
|
|
|
|
case DATADIR_SET:
|
|
default:
|
|
pEnum=new CEnumFormatEtc(_pUnkOuter);
|
|
break;
|
|
}
|
|
|
|
if (NULL==pEnum)
|
|
return ResultFromScode(E_FAIL);
|
|
else
|
|
{
|
|
//Let the enumerator copy our format list.
|
|
pEnum->Init(_pFormats, _numFormats) ;
|
|
|
|
pEnum->AddRef();
|
|
}
|
|
|
|
*ppEnum=pEnum;
|
|
return NO_ERROR ;
|
|
}
|
|
STDMETHODIMP CImpIDataObject::DAdvise(LPFORMATETC pFE, DWORD dwFlags,
|
|
LPADVISESINK pIAdviseSink, LPDWORD pdwConn)
|
|
{
|
|
return ResultFromScode(E_NOTIMPL) ;
|
|
}
|
|
STDMETHODIMP CImpIDataObject::DUnadvise(DWORD dwConn)
|
|
{
|
|
return ResultFromScode(E_NOTIMPL) ;
|
|
}
|
|
STDMETHODIMP CImpIDataObject::EnumDAdvise(LPENUMSTATDATA *ppEnum)
|
|
{
|
|
return ResultFromScode(E_NOTIMPL) ;
|
|
}
|
|
|
|
CEnumFormatEtc::CEnumFormatEtc(LPUNKNOWN pUnkRef)
|
|
{
|
|
DC_BEGIN_FN("CEnumFormatEtc::CEnumFormatEtc");
|
|
|
|
_cRef = 0 ;
|
|
_pUnkRef = pUnkRef ;
|
|
if (_pUnkRef)
|
|
{
|
|
_pUnkRef->AddRef();
|
|
}
|
|
_iCur = 0;
|
|
|
|
DC_END_FN() ;
|
|
}
|
|
|
|
DCVOID CEnumFormatEtc::Init(LPFORMATETC pFormats, ULONG numFormats)
|
|
{
|
|
DC_BEGIN_FN("CEnumFormatEtc::Init");
|
|
|
|
_cItems = numFormats;
|
|
_pFormats = (LPFORMATETC) LocalAlloc(LPTR,_cItems*sizeof(FORMATETC)) ;
|
|
if (_pFormats)
|
|
{
|
|
memcpy(_pFormats, pFormats, _cItems*sizeof(FORMATETC)) ;
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Unable to allocate memory for formats"))) ;
|
|
}
|
|
|
|
DC_END_FN() ;
|
|
}
|
|
CEnumFormatEtc::~CEnumFormatEtc()
|
|
{
|
|
DC_BEGIN_FN("CEnumFormatEtc::~CEnumFormatEtc");
|
|
if (_pUnkRef)
|
|
{
|
|
_pUnkRef->Release();
|
|
_pUnkRef = NULL;
|
|
}
|
|
if (NULL !=_pFormats)
|
|
LocalFree(_pFormats) ;
|
|
DC_END_FN() ;
|
|
}
|
|
|
|
STDMETHODIMP CEnumFormatEtc::QueryInterface(REFIID riid, PPVOID ppv)
|
|
{
|
|
DC_BEGIN_FN("CEnumFormatEtc::QueryInterface");
|
|
*ppv=NULL;
|
|
|
|
/*
|
|
* Enumerators are separate objects, not the data object, so
|
|
* we only need to support out IUnknown and IEnumFORMATETC
|
|
* interfaces here with no concern for aggregation.
|
|
*/
|
|
if (IID_IUnknown==riid || IID_IEnumFORMATETC==riid)
|
|
*ppv=this;
|
|
|
|
if (NULL!=*ppv)
|
|
{
|
|
((LPUNKNOWN)*ppv)->AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
DC_END_FN() ;
|
|
return ResultFromScode(E_NOINTERFACE);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CEnumFormatEtc::AddRef(void)
|
|
{
|
|
LONG cRef;
|
|
|
|
cRef = InterlockedIncrement(&_cRef);
|
|
|
|
_pUnkRef->AddRef();
|
|
|
|
return cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CEnumFormatEtc::Release(void)
|
|
{
|
|
LONG cRef;
|
|
DC_BEGIN_FN("CEnumFormatEtc::Release");
|
|
|
|
_pUnkRef->Release();
|
|
|
|
cRef = InterlockedDecrement(&_cRef) ;
|
|
if (0 == cRef)
|
|
delete this;
|
|
|
|
DC_END_FN() ;
|
|
return cRef;
|
|
}
|
|
|
|
STDMETHODIMP CEnumFormatEtc::Next(ULONG cFE, LPFORMATETC pFE, ULONG *pulFE)
|
|
{
|
|
ULONG cReturn=0L;
|
|
|
|
if (NULL==_pFormats)
|
|
return ResultFromScode(S_FALSE);
|
|
|
|
if (NULL==pulFE)
|
|
{
|
|
if (1L!=cFE)
|
|
return ResultFromScode(E_POINTER);
|
|
}
|
|
else
|
|
*pulFE=0L;
|
|
|
|
if (NULL==pFE || _iCur >= _cItems)
|
|
return ResultFromScode(S_FALSE);
|
|
|
|
while (_iCur < _cItems && cFE > 0)
|
|
{
|
|
*pFE=_pFormats[_iCur];
|
|
pFE++;
|
|
_iCur++;
|
|
cReturn++;
|
|
cFE--;
|
|
}
|
|
|
|
if (NULL!=pulFE)
|
|
*pulFE=cReturn;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CEnumFormatEtc::Skip(ULONG cSkip)
|
|
{
|
|
if ((_iCur+cSkip) >= _cItems)
|
|
return ResultFromScode(S_FALSE);
|
|
|
|
_iCur+=cSkip;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CEnumFormatEtc::Reset(void)
|
|
{
|
|
_iCur=0;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CEnumFormatEtc::Clone(LPENUMFORMATETC *ppEnum)
|
|
{
|
|
#ifndef OS_WINCE
|
|
PCEnumFormatEtc pNew = NULL;
|
|
LPMALLOC pIMalloc;
|
|
LPFORMATETC prgfe;
|
|
BOOL fRet=TRUE;
|
|
ULONG cb;
|
|
|
|
*ppEnum=NULL;
|
|
#else
|
|
BOOL fRet=FALSE;
|
|
#endif
|
|
|
|
#ifndef OS_WINCE
|
|
//Copy the memory for the list.
|
|
if (FAILED(CoGetMalloc(MEMCTX_TASK, &pIMalloc)))
|
|
return ResultFromScode(E_OUTOFMEMORY);
|
|
|
|
cb=_cItems*sizeof(FORMATETC);
|
|
prgfe=(LPFORMATETC)pIMalloc->Alloc(cb);
|
|
|
|
if (NULL!=prgfe)
|
|
{
|
|
//Copy the formats
|
|
memcpy(prgfe, _pFormats, (int)cb);
|
|
|
|
//Create the clone
|
|
pNew=new CEnumFormatEtc(_pUnkRef);
|
|
|
|
if (NULL != pNew)
|
|
{
|
|
pNew->_iCur=_iCur;
|
|
pNew->_pFormats=prgfe;
|
|
pNew->AddRef();
|
|
fRet=TRUE;
|
|
}
|
|
else
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
}
|
|
|
|
pIMalloc->Release();
|
|
|
|
*ppEnum=pNew;
|
|
#endif
|
|
return fRet ? NOERROR : ResultFromScode(E_OUTOFMEMORY);
|
|
}
|
|
|