You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2006 lines
74 KiB
2006 lines
74 KiB
/**MOD+**********************************************************************/
|
|
/* Module: sclip2.cpp */
|
|
/* */
|
|
/* Purpose: Second thread */
|
|
/* Receives RDP clipboard messages */
|
|
/* */
|
|
/* Copyright(C) Microsoft Corporation 1998 */
|
|
/* */
|
|
/**MOD-**********************************************************************/
|
|
|
|
#include <adcg.h>
|
|
|
|
#define TRC_GROUP TRC_GROUP_CORE
|
|
#define TRC_FILE "sclip2"
|
|
#include <atrcapi.h>
|
|
|
|
#include <pclip.h>
|
|
#include <sclip.h>
|
|
#include <pchannel.h>
|
|
|
|
#include <shlobj.h>
|
|
|
|
/****************************************************************************/
|
|
/* Global data */
|
|
/****************************************************************************/
|
|
#include <sclipdat.h>
|
|
|
|
//
|
|
// CBMConvertToClientPath, CBMConvertToClientPathA, CBMConvertToClientPathW
|
|
// - Arguments:
|
|
// pOldData = Buffer containing the original file path
|
|
// pData = Buffer receiving the new file path
|
|
// - Returns S_OK if pOldData was a drive path
|
|
// E_FAIL if it failed
|
|
// - Given a file path with drive letter, this function will strip out the
|
|
// colon, and prepend the UNC prefix defined by TS_PREPEND_STRING
|
|
//
|
|
//
|
|
// ***** NOTE *****
|
|
// - Currently, if the path is a network path, and not a drive path (C:\path)
|
|
// it simply fails
|
|
//
|
|
HRESULT CBMConvertToClientPath(PVOID pOldData, PVOID pData, size_t cbDest,
|
|
BOOL fWide)
|
|
{
|
|
DC_BEGIN_FN("CBMConvertToClientPath") ;
|
|
if (!pOldData)
|
|
{
|
|
TRC_ERR((TB, _T("Original string pointer is NULL"))) ;
|
|
return E_FAIL ;
|
|
}
|
|
if (!pData)
|
|
{
|
|
TRC_ERR((TB, _T("Destination string pointer is NULL"))) ;
|
|
return E_FAIL ;
|
|
}
|
|
|
|
if (fWide)
|
|
return CBMConvertToClientPathW(pOldData, pData, cbDest) ;
|
|
else
|
|
return CBMConvertToClientPathA(pOldData, pData, cbDest) ;
|
|
|
|
DC_END_FN() ;
|
|
}
|
|
|
|
HRESULT CBMConvertToClientPathW(PVOID pOldData, PVOID pData, size_t cbDest)
|
|
{
|
|
wchar_t* filePath ;
|
|
wchar_t* driveLetter ;
|
|
size_t driveLetterLength ;
|
|
HRESULT hr;
|
|
|
|
DC_BEGIN_FN("CBMConvertToClientPathW") ;
|
|
|
|
// if this is a filepath with a drive letter, we strip the colon, and
|
|
// prefix the path with the temp Directory
|
|
filePath = wcschr((wchar_t*)pOldData, L':') ;
|
|
if (filePath)
|
|
{
|
|
hr = StringCbCopyW((wchar_t*)pData, cbDest, CBM.tempDirW);
|
|
DC_QUIT_ON_FAIL(hr);
|
|
|
|
// Now, we start from after the colon in the drive path, and
|
|
// find the last '\\' so we have just "\filename"
|
|
filePath = wcsrchr(filePath + 1, L'\\');
|
|
|
|
// Add the leftover "\filename"
|
|
if (filePath != NULL) {
|
|
hr = StringCbCatW((wchar_t*)pData, cbDest, filePath);
|
|
DC_QUIT_ON_FAIL(hr);
|
|
}
|
|
TRC_DBG((TB,_T("New filename = %s"), (wchar_t*)pData)) ;
|
|
}
|
|
// else if this is a UNC path beginning with a "\\"
|
|
else if (((wchar_t*) pOldData)[0] == L'\\' &&
|
|
((wchar_t*) pOldData)[1] == L'\\')
|
|
{
|
|
// if we receive a path beginning with the TS_PREPEND_STRING then
|
|
// we should be smart and convert it back to a path with drive letter
|
|
if (0 == _wcsnicmp ((wchar_t *) pOldData,
|
|
LTS_PREPEND_STRING, TS_PREPEND_LENGTH))
|
|
{
|
|
// Skip TS_PREPEND_STRING
|
|
driveLetter = ((wchar_t*) pOldData)+TS_PREPEND_LENGTH ;
|
|
driveLetterLength = (BYTE*)wcschr(driveLetter, L'\\') -
|
|
(BYTE*)driveLetter;
|
|
hr = StringCbCopyNW((wchar_t*)pData, cbDest, driveLetter,
|
|
driveLetterLength);
|
|
DC_QUIT_ON_FAIL(hr);
|
|
((wchar_t*)pData)[driveLetterLength] = L'\0' ;
|
|
hr = StringCbCatW((wchar_t*)pData, cbDest, L":");
|
|
DC_QUIT_ON_FAIL(hr);
|
|
|
|
filePath = wcschr(driveLetter, L'\\');
|
|
|
|
if (filePath != NULL) {
|
|
hr = StringCbCatW((wchar_t*)pData, cbDest, filePath);
|
|
DC_QUIT_ON_FAIL(hr);
|
|
}
|
|
}
|
|
// otherwise, we just got a regular UNC path.
|
|
else
|
|
{
|
|
// Stary by prepending the new file path with the temp directory
|
|
hr = StringCbCopyW((wchar_t*) pData, cbDest, CBM.tempDirW) ;
|
|
DC_QUIT_ON_FAIL(hr);
|
|
// Now, we start from the beginning of the original path,
|
|
// find the last '\\' so we have just "\filename"
|
|
filePath = wcsrchr((wchar_t*)pOldData, L'\\');
|
|
|
|
if (filePath != NULL) {
|
|
hr = StringCbCatW((wchar_t*) pData, cbDest, filePath) ;
|
|
DC_QUIT_ON_FAIL(hr);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Bad path"))) ;
|
|
hr = E_FAIL; ;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB,_T("returning failure; hr=0x%x"), hr));
|
|
}
|
|
|
|
DC_END_FN() ;
|
|
return hr ;
|
|
}
|
|
|
|
HRESULT CBMConvertToClientPathA(PVOID pOldData, PVOID pData, size_t cbDest)
|
|
{
|
|
char* filePath ;
|
|
char* driveLetter ;
|
|
char* tempPath ;
|
|
size_t driveLetterLength ;
|
|
HRESULT hr;
|
|
|
|
DC_BEGIN_FN("CBMConvertToClientPathA") ;
|
|
|
|
// if this is a filepath with a drive letter, we strip the colon, and
|
|
// prefix the path with the temp Directory
|
|
filePath = strchr((char*)pOldData, ':') ;
|
|
if (filePath)
|
|
{
|
|
hr = StringCbCopyA( (char*)pData, cbDest, CBM.tempDirA) ;
|
|
DC_QUIT_ON_FAIL(hr);
|
|
// Now, we start from after the colon in the drive path, and
|
|
// find the last '\\' so we have just "\filename"
|
|
filePath = strrchr(filePath + 1, '\\');
|
|
|
|
// Add the leftover "\filename"
|
|
if (filePath != NULL) {
|
|
hr = StringCbCatA((char*)pData, cbDest, filePath) ;
|
|
DC_QUIT_ON_FAIL(hr);
|
|
}
|
|
}
|
|
// else if this is a UNC path beginning with a "\\"
|
|
else if (((char*) pOldData)[0] == '\\' &&
|
|
((char*) pOldData)[1] == '\\')
|
|
{
|
|
// if this we receive a path beginning with the TS_PREPEND_STRING then
|
|
// we should be smart and convert it back to a path with drive letter
|
|
if (0 == _strnicmp ((char*)pOldData,
|
|
TS_PREPEND_STRING, TS_PREPEND_LENGTH))
|
|
{
|
|
// Skip TS_PREPEND_STRING
|
|
driveLetter = ((char*) pOldData) + TS_PREPEND_LENGTH ;
|
|
driveLetterLength = (BYTE*)strchr(driveLetter, '\\') -
|
|
(BYTE*)driveLetter;
|
|
|
|
hr = StringCbCopyNA((char*)pData, cbDest, driveLetter,
|
|
driveLetterLength) ;
|
|
DC_QUIT_ON_FAIL(hr);
|
|
((char*)pData)[driveLetterLength] = '\0' ;
|
|
|
|
hr = StringCbCatA((char*)pData, cbDest, ":") ;
|
|
DC_QUIT_ON_FAIL(hr);
|
|
|
|
filePath = strchr(driveLetter, '\\');
|
|
|
|
if (filePath != NULL) {
|
|
hr = StringCbCatA((char*)pData, cbDest, filePath) ;
|
|
DC_QUIT_ON_FAIL(hr);
|
|
}
|
|
}
|
|
// otherwise, we just got a regular UNC path.
|
|
else
|
|
{
|
|
// Stary by prepending the new file path with the temp directory
|
|
hr = StringCbCopyA((char*) pData, cbDest, CBM.tempDirA) ;
|
|
DC_QUIT_ON_FAIL(hr);
|
|
|
|
// Now, we start from the beginning of the original path,
|
|
// find the last '\\' so we have just "\filename"
|
|
filePath = strrchr((char*)pOldData, L'\\');
|
|
|
|
if (filePath != NULL) {
|
|
hr = StringCbCatA((char*) pData, cbDest, filePath) ;
|
|
DC_QUIT_ON_FAIL(hr);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Bad path"))) ;
|
|
hr = E_FAIL ;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB,_T("returning failure; hr=0x%x"), hr));
|
|
}
|
|
|
|
DC_END_FN() ;
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CBMGetNewFilePathLengthForClient
|
|
// - Arguments:
|
|
// pData = Buffer containing a filepath
|
|
// fWide = Wide or Ansi (TRUE if wide, FALSE if ansi)
|
|
// - Returns the size (in bytes) required to convert the path to a client path
|
|
// 0 if it fails
|
|
//
|
|
|
|
UINT CBMGetNewFilePathLengthForClient(PVOID pData, BOOL fWide)
|
|
{
|
|
UINT result ;
|
|
DC_BEGIN_FN("CBMGetNewFilePathLengthForClient") ;
|
|
if (!pData)
|
|
{
|
|
TRC_ERR((TB, _T("Filename is NULL"))) ;
|
|
result = 0 ;
|
|
}
|
|
if (fWide)
|
|
result = CBMGetNewFilePathLengthForClientW((WCHAR*)pData) ;
|
|
else
|
|
result = CBMGetNewFilePathLengthForClientA((char*)pData) ;
|
|
DC_EXIT_POINT:
|
|
DC_END_FN() ;
|
|
|
|
return result ;
|
|
}
|
|
|
|
UINT CBMGetNewFilePathLengthForClientW(WCHAR* wszOldFilepath)
|
|
{
|
|
UINT oldLength = wcslen(wszOldFilepath) ;
|
|
UINT newLength ;
|
|
UINT remainingLength = oldLength ;
|
|
byte charSize = sizeof(WCHAR) ;
|
|
DC_BEGIN_FN("CBMGetNewFilePathLengthForClientW") ;
|
|
|
|
// if the old filename didn't even have space for "c:\" (with NULL),
|
|
// then its probably invalid
|
|
if (4 > oldLength)
|
|
{
|
|
newLength = 0 ;
|
|
DC_QUIT ;
|
|
}
|
|
// We check to see if the filepath is prefixed by the TS_PREPEND_STRING
|
|
// If so, we should be smart, and return the size of it with the prepend
|
|
// string removed, and the colon added.
|
|
if (0 == _wcsnicmp(wszOldFilepath,
|
|
LTS_PREPEND_STRING, TS_PREPEND_LENGTH))
|
|
{
|
|
newLength = oldLength - TS_PREPEND_LENGTH + 1 ; // +1 is for the colon
|
|
DC_QUIT ;
|
|
}
|
|
|
|
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(CBM.tempDirW) + 1;
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN() ;
|
|
return newLength * charSize ;
|
|
}
|
|
|
|
UINT CBMGetNewFilePathLengthForClientA(char* szOldFilepath)
|
|
{
|
|
UINT oldLength = strlen(szOldFilepath) ;
|
|
UINT newLength ;
|
|
UINT remainingLength = oldLength ;
|
|
byte charSize = sizeof(char) ;
|
|
DC_BEGIN_FN("CBMGetNewFilePathLengthForClientA") ;
|
|
|
|
// if the old filename didn't even have space for "c:\" (with NULL),
|
|
// then its probably invalid
|
|
if (4 > oldLength)
|
|
{
|
|
newLength = 0 ;
|
|
DC_QUIT ;
|
|
}
|
|
// We check to see if the filepath is prefixed by the TS_PREPEND_STRING
|
|
// If so, we should be smart, and return the size of it with the prepend
|
|
// string removed, and the colon added.
|
|
if (0 == _strnicmp(szOldFilepath,
|
|
TS_PREPEND_STRING, TS_PREPEND_LENGTH))
|
|
{
|
|
newLength = oldLength - TS_PREPEND_LENGTH + 1 ; // +1 is for the colon
|
|
DC_QUIT ;
|
|
}
|
|
|
|
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(CBM.tempDirA) + 1;
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN() ;
|
|
return newLength * charSize ;
|
|
}
|
|
|
|
//
|
|
// CBMGetNewDropfilesSizeForClientSize
|
|
// - 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 the size required for a conversion of the paths to client paths
|
|
// 0 if it fails
|
|
//
|
|
|
|
ULONG CBMGetNewDropfilesSizeForClient(PVOID pData, ULONG oldSize, BOOL fWide)
|
|
{
|
|
DC_BEGIN_FN("CBMGetNewDropfilesSizeForClientSize") ;
|
|
if (fWide)
|
|
return CBMGetNewDropfilesSizeForClientW(pData, oldSize) ;
|
|
else
|
|
return CBMGetNewDropfilesSizeForClientA(pData, oldSize) ;
|
|
DC_END_FN() ;
|
|
}
|
|
|
|
ULONG CBMGetNewDropfilesSizeForClientW(PVOID pData, ULONG oldSize)
|
|
{
|
|
ULONG newSize = oldSize ;
|
|
wchar_t* filenameW ;
|
|
wchar_t* filePathW ;
|
|
wchar_t* fullFilePathW ;
|
|
byte charSize ;
|
|
|
|
DC_BEGIN_FN("CBMGetNewDropfilesSizeForClientSizeW") ;
|
|
charSize = sizeof(wchar_t) ;
|
|
if (!pData)
|
|
{
|
|
TRC_ERR((TB,_T("Pointer to dropfile is NULL"))) ;
|
|
return 0 ;
|
|
}
|
|
|
|
// The start of the first filename
|
|
fullFilePathW = (wchar_t*) ((byte*) pData + ((DROPFILES*) pData)->pFiles) ;
|
|
|
|
while (L'\0' != fullFilePathW[0])
|
|
{
|
|
filePathW = wcschr(fullFilePathW, L':') ;
|
|
// If the file path has a colon in it, then it's a valid drive path
|
|
if (filePathW)
|
|
{
|
|
// we add space for (strlen(tempDir)-1+1) characters because
|
|
// although we are adding strlen(tempDir) characters, we are
|
|
// stripping out the colon from the filepath; however, we add
|
|
// an extra "\" to the string because the tempDir does not have
|
|
// a trailing "\"
|
|
filenameW = wcsrchr(filePathW, 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(CBM.tempDirW) + (filenameW - fullFilePathW))
|
|
* charSize ;
|
|
}
|
|
// Otherwise, it is a UNC path
|
|
else if (fullFilePathW[0] == L'\\' &&
|
|
fullFilePathW[1] == L'\\')
|
|
{
|
|
// if we receive a path beginning with the TS_PREPEND_STRING then
|
|
// we should be smart and convert it back to a path with drive letter
|
|
if (0 == _wcsnicmp(fullFilePathW,
|
|
LTS_PREPEND_STRING, TS_PREPEND_LENGTH))
|
|
{
|
|
newSize = newSize - (TS_PREPEND_LENGTH - 1) * charSize ;
|
|
}
|
|
else
|
|
{
|
|
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(CBM.tempDirW) - (filenameW - fullFilePathW))
|
|
* charSize ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB,_T("Bad path"))) ;
|
|
return 0 ;
|
|
}
|
|
fullFilePathW = fullFilePathW + (wcslen(fullFilePathW) + 1) ;
|
|
}
|
|
// Add space for extra null character
|
|
newSize += charSize ;
|
|
|
|
DC_END_FN() ;
|
|
return newSize ;
|
|
}
|
|
|
|
ULONG CBMGetNewDropfilesSizeForClientA(PVOID pData, ULONG oldSize)
|
|
{
|
|
ULONG newSize = oldSize ;
|
|
char* filename ;
|
|
char* filePath ;
|
|
char* fullFilePath ;
|
|
byte charSize ;
|
|
|
|
DC_BEGIN_FN("CBMGetNewDropfilesSizeForClientSizeW") ;
|
|
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])
|
|
{
|
|
filePath = strchr(fullFilePath, ':') ;
|
|
// If the file path has a colon in it, then its a valid drive path
|
|
if (filePath)
|
|
{
|
|
// we add space for (strlen(tempDir)-1+1) characters because
|
|
// although we are adding strlen(tempDir) characters, we are
|
|
// stripping out the colon from the filepath; however, we add
|
|
// an extra "\" to the string because the tempDir does not have
|
|
// a trailing "\"
|
|
filename = strrchr(filePath, '\\');
|
|
|
|
// Add the length of the temp directory path, and subtract the
|
|
// path preceeding the filename ("path\filename" -> "\filename")
|
|
// (\\server\sharename\path\morepath\filename
|
|
newSize += (strlen(CBM.tempDirA) + (filename - fullFilePath))
|
|
* charSize ;
|
|
}
|
|
// Otherwise, it is a UNC path
|
|
else if (fullFilePath[0] == '\\' &&
|
|
fullFilePath[1] == '\\')
|
|
{
|
|
// if we receive a path beginning with the TS_PREPEND_STRING then
|
|
// we should be smart and convert it back to a path with drive letter
|
|
if (0 == _strnicmp(fullFilePath,
|
|
TS_PREPEND_STRING, TS_PREPEND_LENGTH))
|
|
{
|
|
newSize = newSize - (TS_PREPEND_LENGTH - 1) * charSize ;
|
|
}
|
|
else
|
|
{
|
|
filename = strrchr(fullFilePath, '\\');
|
|
|
|
// Add the length of the temp directory path, and subtract the
|
|
// path preceeding the filename ("path\filename" -> "\filename")
|
|
// (\\server\sharename\path\morepath\filename
|
|
newSize += (strlen(CBM.tempDirA) + (filename - fullFilePath))
|
|
* charSize ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB,_T("Bad path"))) ;
|
|
return 0 ;
|
|
}
|
|
|
|
fullFilePath = fullFilePath + (strlen(fullFilePath) + 1) ;
|
|
}
|
|
// Add space for extra null character
|
|
newSize += charSize ;
|
|
|
|
DC_END_FN() ;
|
|
return newSize ;
|
|
}
|
|
|
|
//
|
|
// ClipConvertToTempPath, ClipConvertToTempPathA, ClipConvertToTempPathW
|
|
// - 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 CBMCopyToTempDirectory(PVOID pSrcFiles, BOOL fWide)
|
|
{
|
|
int result ;
|
|
if (fWide)
|
|
result = CBMCopyToTempDirectoryW(pSrcFiles) ;
|
|
else
|
|
result = CBMCopyToTempDirectoryA(pSrcFiles) ;
|
|
|
|
return result ;
|
|
|
|
}
|
|
|
|
int CBMCopyToTempDirectoryW(PVOID pSrcFiles)
|
|
{
|
|
DC_BEGIN_FN("CBMCopyToTempDirectoryW") ;
|
|
|
|
SHFILEOPSTRUCTW fileOpStructW ;
|
|
int result ;
|
|
HRESULT hr;
|
|
// these are the temp, "temp directories"; they are used because we cannot
|
|
// directly perform a conversion to client path CBM.tempDir*, because they
|
|
// are used within the conversion routines!
|
|
wchar_t tempDirW[MAX_PATH] ;
|
|
|
|
hr = CBMConvertToServerPath(CBM.tempDirW, tempDirW, sizeof(tempDirW), 1) ;
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB,_T("CBMConvertToServerPath failed hr=0x%x"), hr));
|
|
result = hr;
|
|
DC_QUIT;
|
|
}
|
|
fileOpStructW.pFrom = (WCHAR*) pSrcFiles ;
|
|
fileOpStructW.pTo = tempDirW ;
|
|
fileOpStructW.hwnd = NULL ;
|
|
fileOpStructW.wFunc = CBM.dropEffect ;
|
|
fileOpStructW.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR |
|
|
FOF_SIMPLEPROGRESS | FOF_ALLOWUNDO ;
|
|
fileOpStructW.hNameMappings = NULL ;
|
|
fileOpStructW.lpszProgressTitle = CBM.szPasteInfoStringW;
|
|
|
|
result = SHFileOperationW(&fileOpStructW) ;
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return result ;
|
|
}
|
|
|
|
int CBMCopyToTempDirectoryA(PVOID pSrcFiles)
|
|
{
|
|
DC_BEGIN_FN("CBMCopyToTempDirectoryA") ;
|
|
|
|
SHFILEOPSTRUCTA fileOpStructA ;
|
|
int result ;
|
|
HRESULT hr;
|
|
char tempDirA[MAX_PATH] ;
|
|
|
|
hr = CBMConvertToServerPath(CBM.tempDirA, tempDirA, sizeof(tempDirA), 0) ;
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB,_T("CBMConvertToServerPath failed hr=0x%x"), hr));
|
|
result = hr;
|
|
DC_QUIT;
|
|
}
|
|
|
|
fileOpStructA.pFrom = (char*) pSrcFiles ;
|
|
fileOpStructA.pTo = tempDirA ;
|
|
fileOpStructA.hwnd = NULL ;
|
|
fileOpStructA.wFunc = CBM.dropEffect ;
|
|
fileOpStructA.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR |
|
|
FOF_SIMPLEPROGRESS | FOF_ALLOWUNDO ;
|
|
fileOpStructA.hNameMappings = NULL ;
|
|
fileOpStructA.lpszProgressTitle = CBM.szPasteInfoStringA;
|
|
|
|
result = SHFileOperationA(&fileOpStructA) ;
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return result ;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* CBMOnDataReceived - handle incoming data */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CBMOnDataReceived(PDCUINT8 pBuffer, DCUINT cbBytes)
|
|
{
|
|
PCHANNEL_PDU_HEADER pHdr;
|
|
PDCUINT8 pData;
|
|
DCUINT copyLen;
|
|
DCBOOL freeTheBuffer = FALSE;
|
|
PTS_CLIP_PDU pClipPDU;
|
|
|
|
DC_BEGIN_FN("CBMOnDataReceived");
|
|
SetEvent(CBM.GetDataSync[TS_BLOCK_RECEIVED]) ;
|
|
pHdr = (PCHANNEL_PDU_HEADER)pBuffer;
|
|
pData = (PDCUINT8)(pHdr + 1);
|
|
TRC_DBG((TB, _T("Header at %p: flags %#x, length %d"),
|
|
pHdr, pHdr->flags, pHdr->length));
|
|
|
|
// Check to be sure we have at least a header worth of data
|
|
if (sizeof(CHANNEL_PDU_HEADER) > cbBytes) {
|
|
TRC_ERR((TB,_T("Packet not large enough to contain header; cbBytes=%u"),
|
|
cbBytes));
|
|
freeTheBuffer = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* First chunk - allocate memory to hold the entire message */
|
|
/************************************************************************/
|
|
if (pHdr->flags & CHANNEL_FLAG_FIRST)
|
|
{
|
|
TRC_NRM((TB, _T("First chunk - %d of %d"), cbBytes, pHdr->length));
|
|
CBM.rxpBuffer = (PDCUINT8) LocalAlloc(LMEM_FIXED, pHdr->length);
|
|
if (CBM.rxpBuffer)
|
|
{
|
|
CBM.rxpNext = CBM.rxpBuffer;
|
|
CBM.rxSize = pHdr->length;
|
|
CBM.rxLeft = pHdr->length;
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Failed to alloc %d bytes for rx buffer"),
|
|
pHdr->length));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Check that we have a buffer available */
|
|
/************************************************************************/
|
|
if (!CBM.rxpBuffer)
|
|
{
|
|
TRC_ERR((TB, _T("No rx buffer")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Check there's enough space left */
|
|
/************************************************************************/
|
|
copyLen = cbBytes - sizeof(*pHdr);
|
|
if (CBM.rxLeft < copyLen)
|
|
{
|
|
TRC_ERR((TB, _T("Not enough space in rx buffer: need/got %d/%d"),
|
|
copyLen, CBM.rxLeft));
|
|
freeTheBuffer = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Copy the data */
|
|
/************************************************************************/
|
|
TRC_DBG((TB, _T("Copy %d bytes to %p"), copyLen, CBM.rxpNext));
|
|
DC_MEMCPY(CBM.rxpNext, pData, copyLen);
|
|
CBM.rxpNext += copyLen;
|
|
CBM.rxLeft -= copyLen;
|
|
|
|
/************************************************************************/
|
|
/* If we have a complete buffer, tell the main thread */
|
|
/************************************************************************/
|
|
if (pHdr->flags & CHANNEL_FLAG_LAST)
|
|
{
|
|
/********************************************************************/
|
|
/* Check that we received all the data */
|
|
/********************************************************************/
|
|
if (CBM.rxLeft != 0)
|
|
{
|
|
TRC_ERR((TB, _T("Didn't receive all the data: expect/got: %d/%d"),
|
|
CBM.rxSize, CBM.rxSize - CBM.rxLeft));
|
|
freeTheBuffer = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Check that we received at least a TS_CLIP_PDU in length
|
|
if (FIELDOFFSET(TS_CLIP_PDU, data) > CBM.rxSize) {
|
|
TRC_ERR((TB,_T("Assembled buffer to short for TS_CLIP_PDU ")
|
|
_T(" [need=%u got=%u]"), FIELDOFFSET(TS_CLIP_PDU, data),
|
|
CBM.rxSize));
|
|
freeTheBuffer = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/********************************************************************/
|
|
// If this message contains a response to our format list, a request
|
|
// for clipboard data or the clipboard data that we requested
|
|
// handle it in this thread (it will not block us for long)
|
|
// Otherwise,
|
|
// Tell the main thread. The main thread will free the buffer when
|
|
// it's done with this message.
|
|
/********************************************************************/
|
|
pClipPDU = (PTS_CLIP_PDU) CBM.rxpBuffer ;
|
|
|
|
// Validate that there is enough data to read whatever is advertised
|
|
// in the pClipPDU->dataLen
|
|
if (pClipPDU->dataLen + FIELDOFFSET(TS_CLIP_PDU, data) > CBM.rxSize) {
|
|
TRC_ERR((TB,_T("TS_CLIP_PDU.dataLen field too large")));
|
|
freeTheBuffer = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
switch (pClipPDU->msgType)
|
|
{
|
|
case TS_CB_FORMAT_LIST_RESPONSE:
|
|
{
|
|
TRC_NRM((TB, _T("TS_CB_FORMAT_LIST_RESPONSE received")));
|
|
CBMOnFormatListResponse(pClipPDU);
|
|
LocalFree(pClipPDU);
|
|
}
|
|
break;
|
|
|
|
case TS_CB_FORMAT_DATA_REQUEST:
|
|
{
|
|
TRC_NRM((TB, _T("TS_CB_FORMAT_DATA_REQUEST received")));
|
|
CBMOnFormatDataRequest(pClipPDU);
|
|
LocalFree(pClipPDU);
|
|
}
|
|
break;
|
|
|
|
case TS_CB_FORMAT_DATA_RESPONSE:
|
|
{
|
|
TRC_NRM((TB, _T("TS_CB_FORMAT_DATA_RESPONSE received")));
|
|
CBMOnFormatDataResponse(pClipPDU);
|
|
LocalFree(pClipPDU);
|
|
}
|
|
break;
|
|
|
|
case TS_CB_TEMP_DIRECTORY:
|
|
{
|
|
TRC_NRM((TB, _T("TS_CB_TEMP_DIRECTORY received")));
|
|
CBM.fFileCutCopyOn = CBMOnReceivedTempDirectory(pClipPDU);
|
|
LocalFree(pClipPDU);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
// Free the Clipboard thread, if locked
|
|
if (TS_CB_FORMAT_LIST == pClipPDU->msgType)
|
|
{
|
|
SetEvent(CBM.GetDataSync[TS_RESET_EVENT]) ;
|
|
TRC_NRM((TB,_T("Reset state; free clipboard if locked"))) ;
|
|
}
|
|
|
|
TRC_NRM((TB, _T("Pass %d bytes to main thread"), CBM.rxSize));
|
|
PostMessage(CBM.viewerWindow,
|
|
CBM.regMsg,
|
|
CBM.rxSize,
|
|
(LPARAM)CBM.rxpBuffer);
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* Free the buffer if necessary */
|
|
/************************************************************************/
|
|
if (freeTheBuffer && CBM.rxpBuffer)
|
|
{
|
|
TRC_DBG((TB, _T("Free rx buffer")));
|
|
LocalFree(CBM.rxpBuffer);
|
|
CBM.rxpBuffer = NULL;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return;
|
|
} /* CBMOnDataReceived */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* the second thread procedure */
|
|
/****************************************************************************/
|
|
DWORD WINAPI CBMDataThreadProc( LPVOID pParam )
|
|
{
|
|
DWORD waitRc = 0;
|
|
BOOL fSuccess;
|
|
DWORD dwResult;
|
|
DCUINT8 readBuffer[CHANNEL_PDU_LENGTH];
|
|
DWORD cbBytes = 0;
|
|
DCBOOL dataRead;
|
|
DCBOOL tryToDoRead;
|
|
|
|
DC_BEGIN_FN("CBMDataThreadProc");
|
|
|
|
DC_IGNORE_PARAMETER(pParam);
|
|
|
|
/************************************************************************/
|
|
/* loop until we're told to stop */
|
|
/************************************************************************/
|
|
while (CBM.runThread)
|
|
{
|
|
dataRead = FALSE;
|
|
tryToDoRead = (CBM.vcHandle != NULL) ? TRUE : FALSE;
|
|
|
|
if (tryToDoRead)
|
|
{
|
|
/****************************************************************/
|
|
/* Issue a read */
|
|
/****************************************************************/
|
|
cbBytes = sizeof(readBuffer);
|
|
TRC_DBG((TB, _T("Issue the read")));
|
|
fSuccess = ReadFile(CBM.vcHandle,
|
|
readBuffer,
|
|
sizeof(readBuffer),
|
|
&cbBytes,
|
|
&CBM.readOL);
|
|
if (fSuccess)
|
|
{
|
|
TRC_NRM((TB, _T("Data read instantly")));
|
|
dataRead = TRUE;
|
|
}
|
|
else
|
|
{
|
|
dwResult = GetLastError();
|
|
TRC_DBG((TB, _T("Read failed, %d"), dwResult));
|
|
if (dwResult != ERROR_IO_PENDING)
|
|
{
|
|
/********************************************************/
|
|
/* The read failed. Treat this like a disconnection - */
|
|
/* stick around and wait to be reconnected. */
|
|
/********************************************************/
|
|
TRC_ERR((TB, _T("Read failed, %d"), dwResult));
|
|
tryToDoRead = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* If we haven't read any data, wait for something to happen now */
|
|
/********************************************************************/
|
|
if (!dataRead)
|
|
{
|
|
waitRc = WaitForMultipleObjects(CLIP_EVENT_COUNT,
|
|
CBM.hEvent,
|
|
FALSE,
|
|
INFINITE);
|
|
switch (waitRc)
|
|
{
|
|
/************************************************************/
|
|
/* Handle Disconnect and Reconnect synchronously, so that */
|
|
/* all state changes are complete on return. */
|
|
/************************************************************/
|
|
case WAIT_OBJECT_0 + CLIP_EVENT_DISCONNECT:
|
|
{
|
|
TRC_NRM((TB, _T("Session disconnected")));
|
|
|
|
// Make sure that if the other rdpclip thread is waiting
|
|
// for a response in GetData() it is notified of the
|
|
// disconnection.
|
|
|
|
if (CBM.GetDataSync[TS_DISCONNECT_EVENT]) {
|
|
SetEvent(CBM.GetDataSync[TS_DISCONNECT_EVENT]);
|
|
}
|
|
|
|
ResetEvent(CBM.hEvent[CLIP_EVENT_DISCONNECT]);
|
|
SendMessage(CBM.viewerWindow,
|
|
CBM.regMsg,
|
|
0,
|
|
CLIP_EVENT_DISCONNECT);
|
|
tryToDoRead = FALSE;
|
|
}
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + CLIP_EVENT_RECONNECT:
|
|
{
|
|
TRC_NRM((TB, _T("Session reconnected")));
|
|
ResetEvent(CBM.hEvent[CLIP_EVENT_RECONNECT]);
|
|
SendMessage(CBM.viewerWindow,
|
|
CBM.regMsg,
|
|
0,
|
|
CLIP_EVENT_RECONNECT);
|
|
tryToDoRead = TRUE;
|
|
}
|
|
break;
|
|
|
|
/************************************************************/
|
|
/* Data received */
|
|
/************************************************************/
|
|
case WAIT_OBJECT_0 + CLIP_EVENT_READ:
|
|
{
|
|
TRC_DBG((TB, _T("Read complete")));
|
|
fSuccess = GetOverlappedResult(CBM.vcHandle,
|
|
&CBM.readOL,
|
|
&cbBytes,
|
|
FALSE);
|
|
if (fSuccess)
|
|
{
|
|
dataRead = TRUE;
|
|
}
|
|
else
|
|
{
|
|
dwResult = GetLastError();
|
|
TRC_ERR((TB, _T("GetOverlappedResult failed %d"),
|
|
dwResult));
|
|
tryToDoRead = FALSE;
|
|
}
|
|
|
|
/********************************************************/
|
|
/* Reset the event, otherwise we can come straight back */
|
|
/* in here if we don't retry the read. */
|
|
/********************************************************/
|
|
ResetEvent(CBM.hEvent[CLIP_EVENT_READ]);
|
|
}
|
|
break;
|
|
|
|
/************************************************************/
|
|
/* Error occurred - treat as disconnect */
|
|
/************************************************************/
|
|
case WAIT_FAILED:
|
|
default:
|
|
{
|
|
dwResult = GetLastError();
|
|
TRC_ERR((TB, _T("Wait failed, result %d"), dwResult));
|
|
tryToDoRead = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Once we get here, the read is complete - see what we got */
|
|
/********************************************************************/
|
|
if (dataRead && CBM.runThread)
|
|
{
|
|
TRC_NRM((TB, _T("%d bytes of data received"), cbBytes));
|
|
TRC_DATA_DBG("Data received", readBuffer, cbBytes);
|
|
CBMOnDataReceived(readBuffer, cbBytes);
|
|
}
|
|
|
|
} /* while */
|
|
|
|
TRC_NRM((TB, _T("Thread ending")));
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
ExitThread(waitRc);
|
|
return(waitRc);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* CBMOnReceivedTempDirectory */
|
|
/* Caller must have validated that the PDU contained enough data for the */
|
|
/* length specified in pClipPDU->dataLen */
|
|
/****************************************************************************/
|
|
DCBOOL DCINTERNAL CBMOnReceivedTempDirectory(PTS_CLIP_PDU pClipPDU)
|
|
{
|
|
DCBOOL fSuccess = FALSE ;
|
|
WCHAR tempDirW[MAX_PATH] ;
|
|
UINT pathLength = pClipPDU->dataLen / sizeof(WCHAR) ;
|
|
HRESULT hr;
|
|
wchar_t *pDummy;
|
|
size_t cbDummy;
|
|
DC_BEGIN_FN("CBMOnReceivedTempDirectory");
|
|
|
|
if (pathLength > MAX_PATH)
|
|
{
|
|
TRC_ERR((TB, _T("Path too big for us. Failing"))) ;
|
|
fSuccess = FALSE ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
if (sizeof(WCHAR) > pClipPDU->dataLen) {
|
|
TRC_ERR((TB,_T("Not enough data to read anything from buffer")));
|
|
fSuccess = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// The incoming data is not necessarily NULL terminated
|
|
hr = StringCbCopyNExW(tempDirW, sizeof(tempDirW), (WCHAR*) pClipPDU->data,
|
|
pClipPDU->dataLen, &pDummy, &cbDummy, 0 );
|
|
if (FAILED(hr)) {
|
|
fSuccess = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Check that the string is NULL terminated
|
|
hr = StringCbLengthW(tempDirW, sizeof(tempDirW), &cbDummy);
|
|
if (FAILED(hr)) {
|
|
fSuccess = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
hr = CBMConvertToServerPath(tempDirW, CBM.baseTempDirW,
|
|
sizeof(CBM.baseTempDirW), 1) ;
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB,_T("CBMConvertToServerPath failed hr=0x%x"), hr ));
|
|
fSuccess = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
fSuccess = TRUE ;
|
|
DC_EXIT_POINT:
|
|
DC_END_FN() ;
|
|
return fSuccess ;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* CBMOnFormatListResponse */
|
|
/* Caller must have validated that the PDU contained enough data for the */
|
|
/* length specified in pClipPDU->dataLen */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CBMOnFormatListResponse(PTS_CLIP_PDU pClipPDU)
|
|
{
|
|
DC_BEGIN_FN("CBMOnFormatListResponse");
|
|
|
|
/************************************************************************/
|
|
/* The client has received our format list */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Received FORMAT_LIST_REPSONSE")));
|
|
CBM_CHECK_STATE(CBM_EVENT_FORMAT_LIST_RSP);
|
|
|
|
/************************************************************************/
|
|
/* This may arrive just after we've sent the client a format list - */
|
|
/* since the client always wins, we must accept the list */
|
|
/************************************************************************/
|
|
if (CBM.state != CBM_STATE_PENDING_FORMAT_LIST_RSP)
|
|
{
|
|
TRC_ALT((TB, _T("Got unexpected list response")));
|
|
CBM.formatResponseCount = 0;
|
|
}
|
|
else
|
|
{
|
|
/********************************************************************/
|
|
/* update our state according to the result */
|
|
/********************************************************************/
|
|
CBM.formatResponseCount--;
|
|
TRC_NRM((TB, _T("Waiting for %d format response(s)"),
|
|
CBM.formatResponseCount));
|
|
if (CBM.formatResponseCount <= 0)
|
|
{
|
|
if (pClipPDU->msgFlags == TS_CB_RESPONSE_OK)
|
|
{
|
|
TRC_DBG((TB, _T("Fmt list response OK")));
|
|
CBM_SET_STATE(CBM_STATE_SHARED_CB_OWNER, CBM_EVENT_FORMAT_LIST_RSP);
|
|
}
|
|
else
|
|
{
|
|
TRC_ALT((TB, _T("Fmt list rsp failed")));
|
|
CBM_SET_STATE(CBM_STATE_CONNECTED, CBM_EVENT_FORMAT_LIST_RSP);
|
|
}
|
|
CBM.formatResponseCount = 0;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* close the local CB - if it's open - and tell the next viewer */
|
|
/* about the updated list */
|
|
/********************************************************************/
|
|
if (CBM.open)
|
|
{
|
|
TRC_NRM((TB, _T("Close clipboard - didn't expect that")));
|
|
if (!CloseClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseClipboard");
|
|
}
|
|
CBM.open = FALSE;
|
|
}
|
|
|
|
if (CBM.nextViewer != NULL)
|
|
{
|
|
PostMessage(CBM.nextViewer, WM_DRAWCLIPBOARD,0,0);
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return;
|
|
} /* CBMOnFormatListResponse */
|
|
|
|
|
|
//
|
|
// CBMOnFormatDataRequest
|
|
// - Sends client format data it requested
|
|
/* Caller must have validated that the PDU contained enough data for the */
|
|
/* length specified in pClipPDU->dataLen */
|
|
//
|
|
|
|
DCVOID DCINTERNAL CBMOnFormatDataRequest(PTS_CLIP_PDU pClipPDU)
|
|
{
|
|
DCUINT16 response = TS_CB_RESPONSE_OK;
|
|
HANDLE hData = NULL;
|
|
PDCVOID pData;
|
|
PDCVOID pNewData;
|
|
HANDLE hNewData = NULL;
|
|
HANDLE hDropData = NULL;
|
|
HANDLE hTempData = NULL;
|
|
DCINT32 numEntries;
|
|
DCUINT32 dataLen = 0;
|
|
DCUINT32 pduLen;
|
|
DCUINT formatID;
|
|
LOGPALETTE * pLogPalette = NULL;
|
|
PTS_CLIP_PDU pClipRsp;
|
|
TS_CLIP_PDU clipRsp;
|
|
DCBOOL fSuccess = TRUE ;
|
|
BOOL fWide ;
|
|
byte charSize ;
|
|
DROPFILES* pDropFiles ;
|
|
BOOL fDrivePath ;
|
|
ULONG newSize, oldSize ;
|
|
HPDCVOID pFileList ;
|
|
HPDCVOID pFilename ;
|
|
HPDCVOID pOldFilename ;
|
|
SHFILEOPSTRUCTA fileOpStructA ;
|
|
SHFILEOPSTRUCTW fileOpStructW ;
|
|
wchar_t tempDirW[MAX_PATH] ;
|
|
char tempDir[MAX_PATH] ;
|
|
DCTCHAR formatName[TS_FORMAT_NAME_LEN] ;
|
|
|
|
DC_BEGIN_FN("CBMOnFormatDataRequest");
|
|
|
|
// The client wants a format from us
|
|
TRC_NRM((TB, _T("Received FORMAT_DATA_REQUEST")));
|
|
|
|
// This may arrive just after we've sent the client a format list -
|
|
// since the client has not confirmed our list, this request is out-of-
|
|
// date. Fail it.
|
|
if (CBMCheckState(CBM_EVENT_FORMAT_DATA_RQ) != CBM_TABLE_OK)
|
|
{
|
|
TRC_ALT((TB, _T("Unexpected format data rq")));
|
|
|
|
// close the local CB - if it's open - and tell the next viewer
|
|
// about the updated list
|
|
if (CBM.open)
|
|
{
|
|
TRC_NRM((TB, _T("Close clipboard")));
|
|
if (!CloseClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseClipboard");
|
|
}
|
|
CBM.open = FALSE;
|
|
}
|
|
|
|
if (CBM.nextViewer != NULL)
|
|
{
|
|
PostMessage(CBM.nextViewer, WM_DRAWCLIPBOARD,0,0);
|
|
}
|
|
|
|
//
|
|
// Fail the data request
|
|
//
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
|
|
if (sizeof(DCUINT) > pClipPDU->dataLen) {
|
|
TRC_ERR((TB,_T("Not enough data to read format [need=%u got=%u]"),
|
|
sizeof(DCUINT), pClipPDU->dataLen ));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
|
|
formatID = *((PDCUINT)(pClipPDU->data));
|
|
TRC_NRM((TB, _T("format ID %d"), formatID));
|
|
|
|
/************************************************************************/
|
|
/* Open the local clipboard */
|
|
/************************************************************************/
|
|
if (!CBM.open)
|
|
{
|
|
if (!OpenClipboard(CBM.viewerWindow))
|
|
{
|
|
TRC_SYSTEM_ERROR("OpenCB");
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* It was/is open */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("CB opened")));
|
|
CBM.open = TRUE;
|
|
|
|
/************************************************************************/
|
|
/* Get a handle to the data */
|
|
/************************************************************************/
|
|
hData = GetClipboardData(formatID);
|
|
if (hData == NULL)
|
|
{
|
|
/********************************************************************/
|
|
/* Oops! */
|
|
/********************************************************************/
|
|
TRC_ERR((TB, _T("Failed to get format %d"),formatID));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Got handle, now what happens next depends on the flavour of data */
|
|
/* we're looking at... */
|
|
/************************************************************************/
|
|
if (formatID == CF_PALETTE)
|
|
{
|
|
DCUINT16 entries;
|
|
|
|
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), &entries) == 0)
|
|
{
|
|
TRC_DBG((TB, _T("Failed count of palette entries")));
|
|
entries = 256;
|
|
}
|
|
|
|
numEntries = entries;
|
|
|
|
TRC_DBG((TB, _T("Need mem for %d palette entries"), numEntries));
|
|
|
|
dataLen = sizeof(LOGPALETTE) +
|
|
((numEntries - 1) * sizeof(PALETTEENTRY));
|
|
|
|
hNewData = GlobalAlloc(GHND, dataLen);
|
|
if (hNewData == 0)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get %d bytes for palette"), dataLen));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
}
|
|
else
|
|
{
|
|
hDropData = hNewData;
|
|
/****************************************************************/
|
|
/* now get the palette entries into the new buffer */
|
|
/****************************************************************/
|
|
pData = GlobalLock(hNewData);
|
|
numEntries = GetPaletteEntries((HPALETTE)hData,
|
|
0,
|
|
numEntries,
|
|
(PALETTEENTRY*)pData);
|
|
GlobalUnlock(hNewData);
|
|
TRC_DBG((TB, _T("Got %d pal entries"), numEntries));
|
|
if (numEntries == 0)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get any pal entries")));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
}
|
|
dataLen = numEntries * sizeof(PALETTEENTRY);
|
|
|
|
/****************************************************************/
|
|
/* all ok - set up hData to point to the new data */
|
|
/****************************************************************/
|
|
//GlobalFree(hData);
|
|
hData = hNewData;
|
|
}
|
|
}
|
|
else if (formatID == CF_METAFILEPICT)
|
|
{
|
|
TRC_NRM((TB, _T("Metafile data to get")));
|
|
/********************************************************************/
|
|
/* Metafiles are copied as Handles - we need to send across the */
|
|
/* actual bits */
|
|
/********************************************************************/
|
|
hNewData = CBMGetMFData(hData, &dataLen);
|
|
if (!hNewData)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to set MF data")));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
}
|
|
else
|
|
{
|
|
hDropData = hNewData;
|
|
/****************************************************************/
|
|
/* all ok - set up hData to point to the new data */
|
|
/****************************************************************/
|
|
hData = hNewData;
|
|
}
|
|
}
|
|
else if (formatID == CF_HDROP)
|
|
{
|
|
TRC_NRM((TB,_T("HDROP requested"))) ;
|
|
|
|
pDropFiles = (DROPFILES*) GlobalLock(hData) ;
|
|
fWide = pDropFiles->fWide ;
|
|
charSize = fWide ? sizeof(wchar_t) : sizeof(char) ;
|
|
|
|
if (!CBM.fAlreadyCopied)
|
|
{
|
|
// if its not a drive path, then copy to a temp directory
|
|
pFileList = (byte*) pDropFiles + pDropFiles->pFiles ;
|
|
// CBMCopyToTempDirectory returns 0 if successful
|
|
if (0 != CBMCopyToTempDirectory(pFileList, fWide))
|
|
{
|
|
TRC_ERR((TB,_T("Copy to tmp directory failed"))) ;
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
CBM.fAlreadyCopied = TRUE ;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
CBM.fAlreadyCopied = TRUE ;
|
|
}
|
|
// Now that we copied the files, we want to convert the file
|
|
// paths to something the client will understand
|
|
|
|
// Allocate space for new filepaths
|
|
oldSize = (ULONG) GlobalSize(hData) ;
|
|
newSize = CBMGetNewDropfilesSizeForClient(pDropFiles, oldSize, fWide) ;
|
|
hNewData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE, newSize) ;
|
|
if (hNewData == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get %ld bytes for HDROP"),
|
|
newSize));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
hDropData = hNewData;
|
|
|
|
pNewData = GlobalLock(hNewData) ;
|
|
|
|
if (pNewData == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get lock %p for HDROP"),
|
|
hNewData));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
|
|
// 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 + pDropFiles->pFiles ;
|
|
pFilename = (byte*) pNewData + ((DROPFILES*) pNewData)->pFiles ;
|
|
while (fWide ? (L'\0' != ((wchar_t*) pOldFilename)[0]) : ('\0' != ((char*) pOldFilename)[0]))
|
|
{
|
|
if ((ULONG)((BYTE*)pFilename-(BYTE*)pNewData) > newSize) {
|
|
TRC_ERR((TB,_T("Failed filename conversion, not enough data")));
|
|
}
|
|
else {
|
|
if (!SUCCEEDED(CBMConvertToClientPath(pOldFilename, pFilename,
|
|
newSize - ((BYTE*)pFilename-(BYTE*)pNewData), fWide)))
|
|
{
|
|
TRC_ERR((TB, _T("Failed conversion"))) ;
|
|
}
|
|
else
|
|
{
|
|
if (fWide)
|
|
{
|
|
TRC_NRM((TB,_T("oldname %ls; newname %ls"), (wchar_t*)pOldFilename, (wchar_t*)pFilename)) ;
|
|
}
|
|
else
|
|
{
|
|
TRC_NRM((TB,_T("oldname %hs; newname %hs"), (char*)pOldFilename, (char*)pFilename)) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fWide)
|
|
{
|
|
pOldFilename = (byte*) pOldFilename + (wcslen((wchar_t*)pOldFilename) + 1) * sizeof(wchar_t) ;
|
|
pFilename = (byte*) pFilename + (wcslen((wchar_t*)pFilename) + 1) * sizeof(wchar_t) ;
|
|
}
|
|
else
|
|
{
|
|
pOldFilename = (byte*) pOldFilename + (strlen((char*)pOldFilename) + 1) * sizeof(char) ;
|
|
pFilename = (byte*) pFilename + (strlen((char*)pFilename) + 1) * sizeof(char) ;
|
|
}
|
|
}
|
|
if (fWide)
|
|
((wchar_t*)pFilename)[0] = L'\0' ;
|
|
else
|
|
((char*)pFilename)[0] = '\0' ;
|
|
|
|
GlobalUnlock(hNewData) ;
|
|
hData = hNewData ;
|
|
dataLen = (DWORD) GlobalSize(hData) ;
|
|
}
|
|
else
|
|
{
|
|
// Check to see if we are processing the FileName/FileNameW
|
|
// OLE 1 formats; if so, we convert the filenames
|
|
if (0 != GetClipboardFormatName(formatID, formatName, TS_FORMAT_NAME_LEN))
|
|
{
|
|
if ((0 == _tcscmp(formatName, TEXT("FileName"))) ||
|
|
(0 == _tcscmp(formatName, TEXT("FileNameW"))))
|
|
{
|
|
if (0 == _tcscmp(formatName, TEXT("FileNameW")))
|
|
{
|
|
fWide = TRUE ;
|
|
charSize = sizeof(WCHAR) ;
|
|
}
|
|
else
|
|
{
|
|
fWide = FALSE ;
|
|
charSize = 1 ;
|
|
}
|
|
pOldFilename = GlobalLock(hData) ;
|
|
if (!pOldFilename)
|
|
{
|
|
TRC_ERR((TB, _T("No filename/Unable to lock %p"),
|
|
hData));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
|
|
oldSize = (ULONG)GlobalSize(hData) ;
|
|
if (!CBM.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
|
|
pFileList = (char*) LocalAlloc(LPTR, oldSize + charSize) ;
|
|
if (fWide)
|
|
{
|
|
wcscpy((WCHAR*)pFileList, (WCHAR*)pOldFilename) ;
|
|
fDrivePath = (0 != wcschr((WCHAR*) pFileList, L':')) ;
|
|
}
|
|
else
|
|
{
|
|
strcpy((char*)pFileList, (char*)pOldFilename) ;
|
|
fDrivePath = (0 != strchr((char*) pFileList, ':')) ;
|
|
}
|
|
|
|
// CBMCopyToTempDirectory returns 0 if successful
|
|
if (0 != CBMCopyToTempDirectory(pFileList, fWide))
|
|
{
|
|
TRC_ERR((TB,_T("Copy to tmp directory failed"))) ;
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
CBM.fAlreadyCopied = TRUE ;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
CBM.fAlreadyCopied = TRUE ;
|
|
}
|
|
newSize = CBMGetNewFilePathLengthForClient(pOldFilename, fWide) ;
|
|
hNewData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE, newSize) ;
|
|
if (!hNewData)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get %ld bytes for FileName(W)"),
|
|
newSize));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
hDropData = hNewData;
|
|
pFilename= GlobalLock(hNewData) ;
|
|
if (!pFilename)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get lock %p for FileName(W)"),
|
|
hNewData));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
if (FAILED(CBMConvertToClientPath(pOldFilename, pFilename,
|
|
newSize, fWide)))
|
|
{
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
goto CB_SEND_RESPONSE;
|
|
}
|
|
GlobalUnlock(hNewData) ;
|
|
response = TS_CB_RESPONSE_OK;
|
|
hData = hNewData ;
|
|
dataLen = newSize ;
|
|
goto CB_SEND_RESPONSE ;
|
|
}
|
|
}
|
|
/********************************************************************/
|
|
/* just get the length of the block */
|
|
/********************************************************************/
|
|
dataLen = (DCUINT32)GlobalSize(hData);
|
|
TRC_DBG((TB, _T("Got data len %d"), dataLen));
|
|
}
|
|
|
|
CB_SEND_RESPONSE:
|
|
|
|
/************************************************************************/
|
|
/* Get some memory for a message to send to the Client if necessary */
|
|
/************************************************************************/
|
|
if (hData && (dataLen != 0))
|
|
{
|
|
pduLen = dataLen + sizeof(TS_CLIP_PDU);
|
|
pClipRsp = (PTS_CLIP_PDU) LocalAlloc(LMEM_FIXED, pduLen);
|
|
if (pClipRsp == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to alloc %d bytes"), pduLen));
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
dataLen = 0;
|
|
pClipRsp = &clipRsp;
|
|
pduLen = sizeof(clipRsp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_DBG((TB, _T("No data to send")));
|
|
pClipRsp = &clipRsp;
|
|
pduLen = sizeof(clipRsp);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Build the PDU */
|
|
/************************************************************************/
|
|
pClipRsp->msgType = TS_CB_FORMAT_DATA_RESPONSE;
|
|
pClipRsp->msgFlags = response;
|
|
pClipRsp->dataLen = dataLen;
|
|
|
|
/************************************************************************/
|
|
/* copy the data if necessary */
|
|
/************************************************************************/
|
|
if (dataLen != 0)
|
|
{
|
|
TRC_DBG((TB, _T("Copy %d bytes of data"), dataLen));
|
|
pData = GlobalLock(hData);
|
|
DC_MEMCPY(pClipRsp->data, pData, dataLen);
|
|
GlobalUnlock(hData);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Close the CB if open */
|
|
/************************************************************************/
|
|
TRC_DBG((TB, _T("Closing CB")));
|
|
if (!CloseClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseClipboard");
|
|
}
|
|
CBM.open = FALSE;
|
|
|
|
/************************************************************************/
|
|
/* Send the data to the Client */
|
|
/************************************************************************/
|
|
CBMSendToClient(pClipRsp, pduLen);
|
|
|
|
/************************************************************************/
|
|
/* Free the PDU, if any */
|
|
/************************************************************************/
|
|
if (pClipRsp != &clipRsp)
|
|
{
|
|
TRC_DBG((TB, _T("Free the clip PDU")));
|
|
LocalFree(pClipRsp);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
if ( NULL != hDropData )
|
|
{
|
|
GlobalFree( hDropData );
|
|
}
|
|
DC_END_FN();
|
|
return;
|
|
} /* CBMOnFormatDataRequest */
|
|
|
|
|
|
//
|
|
// CBMOnFormatDataRespons
|
|
// - Client response to our request for data
|
|
/* Caller must have validated that the PDU contained enough data for the */
|
|
/* length specified in pClipPDU->dataLen */
|
|
//
|
|
DCVOID DCINTERNAL CBMOnFormatDataResponse(PTS_CLIP_PDU pClipPDU)
|
|
{
|
|
HANDLE hData = NULL;
|
|
HPDCVOID pData;
|
|
LOGPALETTE * pLogPalette = NULL;
|
|
DCUINT32 numEntries;
|
|
DCUINT32 memLen;
|
|
|
|
HRESULT hr ;
|
|
|
|
DC_BEGIN_FN("CBMOnFormatDataResponse");
|
|
|
|
/************************************************************************/
|
|
/* check the response */
|
|
/************************************************************************/
|
|
if (!(pClipPDU->msgFlags & TS_CB_RESPONSE_OK))
|
|
{
|
|
TRC_ALT((TB, _T("Got fmt data rsp failed for %d"), CBM.pendingClientID));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Got the data */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Got OK fmt data rsp for %d"), CBM.pendingClientID));
|
|
|
|
/************************************************************************/
|
|
/* For some formats we still need to do some work */
|
|
/************************************************************************/
|
|
if (CBM.pendingClientID == CF_METAFILEPICT)
|
|
{
|
|
/********************************************************************/
|
|
/* Metafile format - create a metafile from the data */
|
|
/********************************************************************/
|
|
TRC_NRM((TB, _T("Rx data is for metafile")));
|
|
hData = CBMSetMFData(pClipPDU->dataLen, pClipPDU->data);
|
|
if (hData == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to set MF data")));
|
|
}
|
|
|
|
}
|
|
else if (CBM.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*) GlobalAlloc(GPTR, memLen);
|
|
if (pLogPalette != NULL)
|
|
{
|
|
pLogPalette->palVersion = 0x300;
|
|
pLogPalette->palNumEntries = (WORD)numEntries;
|
|
|
|
DC_MEMCPY(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));
|
|
DC_MEMCPY(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:
|
|
/************************************************************************/
|
|
/* Set the state, and we're done. Note that this is done when we get a */
|
|
/* failure response too. */
|
|
/************************************************************************/
|
|
CBM_SET_STATE(CBM_STATE_LOCAL_CB_OWNER, CBM_EVENT_FORMAT_DATA_RSP);
|
|
CBM.pClipData->SetClipData(hData, CBM.pendingClientID ) ;
|
|
SetEvent(CBM.GetDataSync[TS_RECEIVE_COMPLETED]) ;
|
|
|
|
/************************************************************************/
|
|
/* tidy up */
|
|
/************************************************************************/
|
|
if (pLogPalette)
|
|
{
|
|
TRC_NRM((TB, _T("Free pLogPalette %p"), pLogPalette));
|
|
GlobalFree(pLogPalette);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return;
|
|
} /* CBMOnFormatDataResponse */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* CBMSendToClient */
|
|
/****************************************************************************/
|
|
DCBOOL DCINTERNAL CBMSendToClient(PTS_CLIP_PDU pClipRsp, DCUINT size)
|
|
{
|
|
BOOL fSuccess;
|
|
DWORD dwResult;
|
|
DWORD cbBytes;
|
|
|
|
DC_BEGIN_FN("CBMSendToClient");
|
|
|
|
cbBytes = size;
|
|
fSuccess = WriteFile(CBM.vcHandle,
|
|
pClipRsp,
|
|
size,
|
|
&cbBytes,
|
|
&CBM.writeOL);
|
|
if (!fSuccess)
|
|
{
|
|
dwResult = GetLastError();
|
|
if (ERROR_IO_PENDING == dwResult)
|
|
{
|
|
TRC_DBG((TB, _T("Asynchronous write")));
|
|
fSuccess = GetOverlappedResult(CBM.vcHandle,
|
|
&CBM.writeOL,
|
|
&cbBytes,
|
|
TRUE);
|
|
if (fSuccess)
|
|
{
|
|
TRC_DATA_DBG("Data sent", pClipRsp, size);
|
|
}
|
|
else
|
|
{
|
|
TRC_SYSTEM_ERROR("GetOverlappedResult failed");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Write failed, %#x"), dwResult));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_DATA_DBG("Data sent immediately", pClipRsp, size);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(fSuccess);
|
|
} /* CBMSendToClient */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* CBMGetMFData */
|
|
/****************************************************************************/
|
|
HANDLE DCINTERNAL CBMGetMFData(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("CBMGetMFData");
|
|
|
|
TRC_NRM((TB, _T("Getting MF data")));
|
|
/************************************************************************/
|
|
/* Lock the memory to get a pointer to a METAFILEPICT header structure */
|
|
/* and create a METAFILEPICT DC. */
|
|
/************************************************************************/
|
|
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);
|
|
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 %d bits of MF data"), lenMFBits));
|
|
TRC_DATA_DBG("MF bits", (pNewData + sizeof(TS_CLIP_MFPICT)), 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);
|
|
|
|
} /* CBMGetMFData */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* CBMSetMFData */
|
|
/****************************************************************************/
|
|
HANDLE DCINTERNAL CBMSetMFData(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("CBMSetMFData");
|
|
|
|
TRC_DATA_DBG("Received MF data", pData, dataLen);
|
|
|
|
/************************************************************************/
|
|
/* Allocate memory to hold the MF bits (we need the handle to pass to */
|
|
/* SetMetaFileBits). */
|
|
/************************************************************************/
|
|
hMFBits = (PDCVOID)GlobalAlloc(GHND, dataLen - 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;
|
|
}
|
|
|
|
TRC_DBG((TB, _T("copying %d MF bits"), dataLen - sizeof(TS_CLIP_MFPICT) ));
|
|
DC_MEMCPY(pMFMem,
|
|
(PDCVOID)((PDCUINT8)pData + sizeof(TS_CLIP_MFPICT)),
|
|
dataLen - sizeof(TS_CLIP_MFPICT) );
|
|
|
|
GlobalUnlock(pMFMem);
|
|
|
|
/************************************************************************/
|
|
/* 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), (byte *) 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;
|
|
|
|
TRC_DBG((TB, _T("Created MF size %d, %d"), pMFPict->xExt, pMFPict->yExt ));
|
|
|
|
GlobalUnlock(hMFPict);
|
|
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* tidy up */
|
|
/************************************************************************/
|
|
if (rc == FALSE)
|
|
{
|
|
if (hMFPict)
|
|
{
|
|
GlobalFree(hMFPict);
|
|
}
|
|
}
|
|
|
|
if (hMFBits)
|
|
{
|
|
GlobalFree(hMFBits);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(hMFPict);
|
|
|
|
} /* CBMSetMFData */
|
|
|