/**MOD+**********************************************************************/ /* Module: cclip.cpp */ /* */ /* Purpose: Shared Clipboard Client Addin */ /* */ /* Copyright(C) Microsoft Corporation 1998-1999 */ /* */ /**MOD-**********************************************************************/ /****************************************************************************/ /* Precompiled header */ /****************************************************************************/ #include /****************************************************************************/ /* Trace definitions */ /****************************************************************************/ #define TRC_GROUP TRC_GROUP_NETWORK #define TRC_FILE "cclip" #include #include "vcint.h" #include "drapi.h" /****************************************************************************/ // Headers /****************************************************************************/ #include #ifndef OS_WINCE #include #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); }