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.
3183 lines
116 KiB
3183 lines
116 KiB
/**MOD+**********************************************************************/
|
|
/* Module: sclip.cpp */
|
|
/* */
|
|
/* Purpose: Server-side shared clipboard support */
|
|
/* */
|
|
/* Copyright(C) Microsoft Corporation 1998-1999 */
|
|
/* */
|
|
/**MOD-**********************************************************************/
|
|
|
|
#include <adcg.h>
|
|
|
|
#define TRC_GROUP TRC_GROUP_CORE
|
|
#define TRC_FILE "sclip"
|
|
#include <atrcapi.h>
|
|
|
|
#include <pclip.h>
|
|
#include <sclip.h>
|
|
#include <winsta.h>
|
|
#include <pchannel.h>
|
|
#include <shlobj.h>
|
|
#include <wtsapi32.h>
|
|
#include <sclipids.h>
|
|
|
|
BOOL TSSNDD_Init( VOID );
|
|
BOOL TSSNDD_Loop( HINSTANCE );
|
|
VOID TSSNDD_Term( VOID );
|
|
LRESULT TSSNDD_PowerMessage( WPARAM, LPARAM );
|
|
|
|
#ifdef CLIP_TRANSITION_RECORDING
|
|
|
|
UINT g_rguiDbgLastClipState[DBG_RECORD_SIZE];
|
|
UINT g_rguiDbgLastClipEvent[DBG_RECORD_SIZE];
|
|
LONG g_uiDbgPosition = -1;
|
|
|
|
#endif // CLIP_TRANSITION_RECORDING
|
|
|
|
/****************************************************************************/
|
|
/* Global data */
|
|
/****************************************************************************/
|
|
#define DC_DEFINE_GLOBAL_DATA
|
|
#include <sclipdat.h>
|
|
#undef DC_DEFINE_GLOBAL_DATA
|
|
|
|
/**PROC+*********************************************************************/
|
|
/* Name: WinMain */
|
|
/* */
|
|
/* Purpose: Main procedure */
|
|
/* */
|
|
/* Returns: See Windows documentation */
|
|
/* */
|
|
/**PROC-*********************************************************************/
|
|
int WINAPI WinMain(HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPSTR lpszCmdParam,
|
|
int unusedParam2)
|
|
{
|
|
int wParam = 0;
|
|
TRC_CONFIG trcConfig;
|
|
WINSTATIONCONFIG config;
|
|
BOOLEAN fSuccess;
|
|
ULONG returnedLength;
|
|
|
|
|
|
DC_BEGIN_FN("WinMain");
|
|
|
|
DC_IGNORE_PARAMETER(hPrevInstance);
|
|
DC_IGNORE_PARAMETER(lpszCmdParam);
|
|
DC_IGNORE_PARAMETER(unusedParam2);
|
|
|
|
/************************************************************************/
|
|
/* Exit immediately if more than one copy is running */
|
|
/************************************************************************/
|
|
CBM.hMutex = CreateMutex(NULL, FALSE, TEXT("RDPCLIP is already running"));
|
|
if (CBM.hMutex == NULL)
|
|
{
|
|
// An error (like out of memory) has occurred.
|
|
TRC_ERR((TB, _T("Unable to create already running mutex!")));
|
|
DC_QUIT;
|
|
}
|
|
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
|
{
|
|
TRC_ERR((TB, _T("Second copy!")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Initialize Random number generator
|
|
//
|
|
TSRNG_Initialize();
|
|
|
|
#ifdef DC_DEBUG
|
|
/************************************************************************/
|
|
/* Call trace's DLLMain, since it's directly built into RDPCLIP */
|
|
/************************************************************************/
|
|
DllMain(hInstance, DLL_PROCESS_ATTACH, NULL);
|
|
|
|
/************************************************************************/
|
|
/* Switch off trace to file */
|
|
/************************************************************************/
|
|
TRC_GetConfig(&trcConfig, sizeof(trcConfig));
|
|
CLEAR_FLAG(trcConfig.flags, TRC_OPT_FILE_OUTPUT);
|
|
TRC_SetConfig(&trcConfig, sizeof(trcConfig));
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* Get WinStation configuration */
|
|
/************************************************************************/
|
|
fSuccess = WinStationQueryInformation(NULL, LOGONID_CURRENT,
|
|
WinStationConfiguration, &config,
|
|
sizeof(config), &returnedLength);
|
|
if (!fSuccess)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to get WinStation config, %d"), GetLastError()));
|
|
goto exitme;
|
|
}
|
|
|
|
|
|
if ( !config.User.fDisableCam )
|
|
TSSNDD_Init();
|
|
|
|
if ( !config.User.fDisableClip )
|
|
wParam = CBM_Main(hInstance);
|
|
else
|
|
{
|
|
//
|
|
// we need some window to handle the close event
|
|
//
|
|
if ( !config.User.fDisableCam )
|
|
wParam = TSSNDD_Loop(hInstance);
|
|
}
|
|
|
|
if ( !config.User.fDisableCam )
|
|
TSSNDD_Term();
|
|
|
|
//
|
|
// Terminate Random number generator
|
|
//
|
|
TSRNG_Shutdown();
|
|
|
|
|
|
exitme:
|
|
|
|
#ifdef DC_DEBUG
|
|
/****************************************************************************/
|
|
/* Tell trace we're terminating */
|
|
/****************************************************************************/
|
|
DllMain(hInstance, DLL_PROCESS_DETACH, NULL);
|
|
#endif
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* Release the run once mutex */
|
|
/************************************************************************/
|
|
if (CBM.hMutex != NULL) {
|
|
TRC_NRM((TB, _T("Closing already running mutex")));
|
|
CloseHandle(CBM.hMutex);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(wParam);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* CBM_Main */
|
|
/****************************************************************************/
|
|
DCINT DCAPI CBM_Main(HINSTANCE hInstance)
|
|
{
|
|
ATOM registerClassRc = 0;
|
|
DCUINT32 threadID;
|
|
MSG msg;
|
|
HANDLE hEvent;
|
|
DCTCHAR eventName[64];
|
|
DWORD dwResult;
|
|
HRESULT hr ;
|
|
INT iRet;
|
|
INT cbWritten;
|
|
|
|
DC_BEGIN_FN("CBM_Main");
|
|
|
|
/************************************************************************/
|
|
// Clear global memory
|
|
/************************************************************************/
|
|
DC_MEMSET(&CBM, 0, sizeof(CBM));
|
|
|
|
//
|
|
// Load the paste information string.
|
|
//
|
|
|
|
iRet = LoadStringW(
|
|
hInstance,
|
|
IDS_PASTE_PROGRESS_STRING,
|
|
CBM.szPasteInfoStringW,
|
|
PASTE_PROGRESS_STRING_LENGTH);
|
|
|
|
if (iRet == 0) {
|
|
TRC_SYSTEM_ERROR("LoadString");
|
|
CBM.szPasteInfoStringW[0] = NULL;
|
|
}
|
|
|
|
cbWritten = WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
CBM.szPasteInfoStringW,
|
|
-1,
|
|
CBM.szPasteInfoStringA,
|
|
sizeof(CBM.szPasteInfoStringA),
|
|
NULL,
|
|
NULL);
|
|
|
|
if (cbWritten == 0) {
|
|
TRC_ERR((TB, _T("Failed to load ANSI paste progress string: %s")));
|
|
CBM.szPasteInfoStringA[0] = NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize the data transfer object that contains the IDataObject,
|
|
// and then initialize Ole
|
|
CBM.pClipData = new CClipData();
|
|
if (CBM.pClipData == NULL) {
|
|
TRC_ERR((TB, _T("Failed to allocate memory for CClipData")));
|
|
DC_QUIT;
|
|
}
|
|
else
|
|
{
|
|
CBM.pClipData->AddRef();
|
|
}
|
|
|
|
hr = OleInitialize(NULL);
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB, _T("Failed to initialize OLE")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
// Get session information
|
|
/************************************************************************/
|
|
if (!ProcessIdToSessionId(GetCurrentProcessId(), &CBM.logonId))
|
|
{
|
|
dwResult = GetLastError();
|
|
TRC_ERR((TB, _T("Failed to get Session Id info, %d"), dwResult));
|
|
DC_QUIT;
|
|
}
|
|
TRC_NRM((TB, _T("Logon ID %d"), CBM.logonId));
|
|
|
|
//
|
|
// Create DataSync events
|
|
//
|
|
CBM.GetDataSync[TS_BLOCK_RECEIVED] = CreateEvent(NULL, FALSE, FALSE, NULL) ;
|
|
CBM.GetDataSync[TS_RECEIVE_COMPLETED] = CreateEvent(NULL, FALSE, FALSE, NULL) ;
|
|
CBM.GetDataSync[TS_DISCONNECT_EVENT] = CreateEvent(NULL, TRUE, FALSE, NULL) ;
|
|
CBM.GetDataSync[TS_RESET_EVENT] = CreateEvent(NULL, TRUE, FALSE, NULL) ;
|
|
|
|
if (!CBM.GetDataSync[TS_BLOCK_RECEIVED] || !CBM.GetDataSync[TS_RECEIVE_COMPLETED] ||
|
|
!CBM.GetDataSync[TS_RESET_EVENT] || !CBM.GetDataSync[TS_DISCONNECT_EVENT]) {
|
|
TRC_ERR((TB, _T("CreateEvent Failed; a GetDataSync is NULL"))) ;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Create read and write completion events */
|
|
/************************************************************************/
|
|
CBM.readOL.hEvent = CreateEvent(NULL, // no security attribute
|
|
TRUE, // manual-reset event
|
|
FALSE, // initial state = not signaled
|
|
NULL); // unnamed event object
|
|
if (CBM.readOL.hEvent == NULL)
|
|
{
|
|
dwResult = GetLastError();
|
|
TRC_ERR((TB, _T("Failed to create read completion event, %d"),
|
|
dwResult));
|
|
DC_QUIT;
|
|
}
|
|
CBM.hEvent[CLIP_EVENT_READ] = CBM.readOL.hEvent;
|
|
|
|
CBM.writeOL.hEvent = CreateEvent(NULL, // no security attribute
|
|
TRUE, // manual-reset event
|
|
FALSE, // initial state = not signaled
|
|
NULL); // unnamed event object
|
|
if (CBM.writeOL.hEvent == NULL)
|
|
{
|
|
dwResult = GetLastError();
|
|
TRC_ERR((TB, _T("Failed to create write completion event, %d"),
|
|
dwResult));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Create disconnect & reconnect events */
|
|
/************************************************************************/
|
|
DC_TSTRCPY(eventName,
|
|
_T("RDPClip-Disconnect"));
|
|
|
|
hEvent = CreateEvent(NULL, FALSE, FALSE, eventName);
|
|
if (hEvent == NULL)
|
|
{
|
|
dwResult = GetLastError();
|
|
TRC_ERR((TB, _T("Failed to create event %s, %d"),
|
|
eventName, dwResult));
|
|
DC_QUIT;
|
|
}
|
|
CBM.hEvent[CLIP_EVENT_DISCONNECT] = hEvent;
|
|
TRC_NRM((TB, _T("Created event %s, %p"), eventName, hEvent));
|
|
|
|
DC_TSTRCPY(eventName,
|
|
_T("RDPClip-Reconnect"));
|
|
|
|
hEvent = CreateEvent(NULL, FALSE, FALSE, eventName);
|
|
if (hEvent == NULL)
|
|
{
|
|
dwResult = GetLastError();
|
|
TRC_ERR((TB, _T("Failed to create event %s, %d"),
|
|
eventName, dwResult));
|
|
DC_QUIT;
|
|
}
|
|
CBM.hEvent[CLIP_EVENT_RECONNECT] = hEvent;
|
|
TRC_NRM((TB, _T("Created event %s, %p"), eventName, hEvent));
|
|
|
|
TRC_NRM((TB, _T("Created events: Read %p, Write %p, Disc %p, Reco %p"),
|
|
CBM.hEvent[CLIP_EVENT_READ], CBM.writeOL.hEvent,
|
|
CBM.hEvent[CLIP_EVENT_DISCONNECT], CBM.hEvent[CLIP_EVENT_RECONNECT]));
|
|
|
|
CBM.fFileCutCopyOn = FALSE;
|
|
|
|
/************************************************************************/
|
|
/* Create our (invisible) window which we will register as a clipboard */
|
|
/* viewer */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Register Main Window class")));
|
|
CBM.viewerWindowClass.style = 0;
|
|
CBM.viewerWindowClass.lpfnWndProc = CBMWndProc;
|
|
CBM.viewerWindowClass.cbClsExtra = 0;
|
|
CBM.viewerWindowClass.cbWndExtra = 0;
|
|
CBM.viewerWindowClass.hInstance = hInstance;
|
|
CBM.viewerWindowClass.hIcon = NULL;
|
|
CBM.viewerWindowClass.hCursor = NULL;
|
|
CBM.viewerWindowClass.hbrBackground = (HBRUSH) GetStockObject(HOLLOW_BRUSH);
|
|
CBM.viewerWindowClass.lpszMenuName = NULL;
|
|
CBM.viewerWindowClass.lpszClassName = CBM_VIEWER_CLASS;
|
|
|
|
registerClassRc = RegisterClass (&(CBM.viewerWindowClass));
|
|
|
|
if (registerClassRc == 0)
|
|
{
|
|
/********************************************************************/
|
|
/* Failed to register CB viewer class */
|
|
/********************************************************************/
|
|
TRC_ERR((TB, _T("Failed to register Cb Viewer class")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRC_DBG((TB, _T("Create main window")));
|
|
CBM.viewerWindow =
|
|
CreateWindow(CBM_VIEWER_CLASS, /* window class name */
|
|
_T("CB Monitor Window"), /* window caption */
|
|
WS_OVERLAPPEDWINDOW, /* window style */
|
|
0, /* initial x position */
|
|
0, /* initial y position */
|
|
100, /* initial x size */
|
|
100, /* initial y size */
|
|
NULL, /* parent window */
|
|
NULL, /* window menu handle */
|
|
hInstance, /* program inst handle */
|
|
NULL); /* creation parameters */
|
|
|
|
/************************************************************************/
|
|
/* Check we created the window OK */
|
|
/************************************************************************/
|
|
if (CBM.viewerWindow == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to create CB Viewer Window")));
|
|
DC_QUIT;
|
|
}
|
|
TRC_DBG((TB, _T("Viewer Window handle %x"), CBM.viewerWindow));
|
|
|
|
/************************************************************************/
|
|
/* Register a message for communication between the two threads */
|
|
/************************************************************************/
|
|
CBM.regMsg = RegisterWindowMessage(_T("Clip Message"));
|
|
if (CBM.regMsg == 0)
|
|
{
|
|
/********************************************************************/
|
|
/* Failed to register a message - use a WM_USER message instead */
|
|
/********************************************************************/
|
|
TRC_ERR((TB, _T("Failed to register a window message")));
|
|
CBM.regMsg = WM_USER_DD_KICK;
|
|
}
|
|
TRC_NRM((TB, _T("Registered window message %x"), CBM.regMsg));
|
|
|
|
/************************************************************************/
|
|
/* we're now finished with creating things */
|
|
/************************************************************************/
|
|
CBM_SET_STATE(CBM_STATE_INITIALIZED, CBM_EVENT_CBM_MAIN);
|
|
|
|
/************************************************************************/
|
|
/* Do (re)connection stuff */
|
|
/************************************************************************/
|
|
CBMReconnect();
|
|
|
|
/************************************************************************/
|
|
/* set up the second thread */
|
|
/************************************************************************/
|
|
TRC_DBG((TB, _T("Start second thread")));
|
|
CBM.runThread = TRUE;
|
|
CBM.hDataThread = CreateThread
|
|
( NULL, // pointer to thread security attribs
|
|
0, // initial thread stack size, in bytes
|
|
CBMDataThreadProc, // pointer to thread function
|
|
NULL, // argument for new thread
|
|
0, // creation flags
|
|
&threadID); // pointer to returned thread id
|
|
|
|
if (CBM.hDataThread == NULL)
|
|
{
|
|
/********************************************************************/
|
|
/* thread creation failed - oh dear! */
|
|
/********************************************************************/
|
|
TRC_ERR((TB, _T("Failed to create second thread - quit")));
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRC_DBG((TB,_T("Entering message loop")));
|
|
while (GetMessage (&msg, NULL, 0, 0))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
TRC_DBG((TB,_T("Exiting message loop")));
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* Do the tidy up... */
|
|
/* */
|
|
/* Start by stopping the second thread (if any!) and removing the */
|
|
/* event. */
|
|
/************************************************************************/
|
|
CBMTerm();
|
|
|
|
/************************************************************************/
|
|
/* Destroy the window */
|
|
/************************************************************************/
|
|
if (CBM.viewerWindow)
|
|
{
|
|
TRC_DBG((TB, _T("destroying window %p"), CBM.viewerWindow));
|
|
if (!DestroyWindow(CBM.viewerWindow))
|
|
{
|
|
TRC_SYSTEM_ERROR("DestroyWindow");
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Unregister the class */
|
|
/************************************************************************/
|
|
if (registerClassRc != 0)
|
|
{
|
|
TRC_NRM((TB, _T("Unregister window class")));
|
|
if (!UnregisterClass(CBM_VIEWER_CLASS, hInstance))
|
|
{
|
|
TRC_SYSTEM_ERROR("UnregisterClass");
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(0);
|
|
} /* CBM_Main */
|
|
|
|
/****************************************************************************/
|
|
/* CBMRemoteFormatFromLocalID */
|
|
/****************************************************************************/
|
|
DCUINT DCINTERNAL CBMRemoteFormatFromLocalID(DCUINT id)
|
|
{
|
|
DCUINT i ;
|
|
DCUINT retID = 0;
|
|
|
|
DC_BEGIN_FN("CBMRemoteFormatFromLocalID");
|
|
|
|
for (i = 0; i < CB_MAX_FORMATS; i++)
|
|
{
|
|
if (CBM.idMap[i].serverID == id)
|
|
{
|
|
retID = CBM.idMap[i].clientID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//TRC_ASSERT((retID != 0), (TB, _T("0 client id for server id %d"), id));
|
|
DC_END_FN();
|
|
return(retID);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* CBMCheckState */
|
|
/****************************************************************************/
|
|
DCUINT DCINTERNAL CBMCheckState(DCUINT event)
|
|
{
|
|
DCUINT tableVal = cbmStateTable[event][CBM.state];
|
|
|
|
DC_BEGIN_FN("CBMCheckState");
|
|
|
|
TRC_DBG((TB, _T("Test event %d (%s) in state %d (%s), result = %d (%s)"),
|
|
event, cbmEvent[event],
|
|
CBM.state, cbmState[CBM.state],
|
|
tableVal, tableVal == CBM_TABLE_OK ? _T("OK") :
|
|
tableVal == CBM_TABLE_WARN ? _T("Warn") :
|
|
_T("Error") ));
|
|
|
|
if (tableVal != CBM_TABLE_OK)
|
|
{
|
|
if (tableVal == CBM_TABLE_WARN)
|
|
{
|
|
TRC_ALT((TB, _T("Unusual event %s in state %s"),
|
|
cbmEvent[event], cbmState[CBM.state]));
|
|
}
|
|
else
|
|
{
|
|
TRC_ABORT((TB, _T("Invalid event %s in state %s"),
|
|
cbmEvent[event], cbmState[CBM.state]));
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(tableVal);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* CB window proc */
|
|
/****************************************************************************/
|
|
LRESULT CALLBACK CBMWndProc(HWND hwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
LRESULT rc = 0;
|
|
DCBOOL drawRc;
|
|
PTS_CLIP_PDU pClipPDU;
|
|
ULONG_PTR size;
|
|
#ifdef DC_DEBUG
|
|
HDC hdc;
|
|
PAINTSTRUCT ps;
|
|
RECT rect;
|
|
#endif
|
|
|
|
DC_BEGIN_FN("CBMWndProc");
|
|
|
|
/************************************************************************/
|
|
/* First of all, handle messages from the 2nd thread */
|
|
/************************************************************************/
|
|
if (message == CBM.regMsg)
|
|
{
|
|
TRC_NRM((TB, _T("Message from second thread")));
|
|
|
|
/********************************************************************/
|
|
/* Handle 0-length messages (lParam = event) */
|
|
/********************************************************************/
|
|
if (wParam == 0)
|
|
{
|
|
TRC_NRM((TB, _T("0-length")));
|
|
if (lParam == CLIP_EVENT_DISCONNECT)
|
|
{
|
|
TRC_NRM((TB, _T("Disconnected indication")));
|
|
CBMDisconnect();
|
|
}
|
|
else if (lParam == CLIP_EVENT_RECONNECT)
|
|
{
|
|
TRC_NRM((TB, _T("Reconnected indication")));
|
|
CBMReconnect();
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Unknown event %d"), lParam));
|
|
}
|
|
DC_QUIT;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Handle real messages (lParam = PDU) */
|
|
/********************************************************************/
|
|
size = (ULONG_PTR)wParam;
|
|
pClipPDU = (PTS_CLIP_PDU)lParam;
|
|
|
|
switch (pClipPDU->msgType)
|
|
{
|
|
case TS_CB_FORMAT_LIST:
|
|
{
|
|
// Validate a full TS_CLIP_PDU can be read
|
|
if (FIELDOFFSET(TS_CLIP_PDU, data) > size) {
|
|
TRC_ERR((TB,_T("TS_CB_FORMAT_LIST Not enough header ")
|
|
_T("data [needed=%u got=%u]"),
|
|
FIELDOFFSET(TS_CLIP_PDU, data), size));
|
|
break;
|
|
}
|
|
|
|
// Validate there is as much data as the packet advertises
|
|
if (FIELDOFFSET(TS_CLIP_PDU,data) + pClipPDU->dataLen > size) {
|
|
TRC_ERR((TB,_T("TS_CB_FORMAT_LIST Not enough packet ")
|
|
_T("data [needed=%u got=%u]"),
|
|
FIELDOFFSET(TS_CLIP_PDU, data) + pClipPDU->dataLen,
|
|
size));
|
|
break;
|
|
}
|
|
|
|
TRC_NRM((TB, _T("TS_CB_FORMAT_LIST received")));
|
|
CBMOnFormatList(pClipPDU);
|
|
}
|
|
break;
|
|
|
|
case TS_CB_MONITOR_READY:
|
|
{
|
|
TRC_ERR((TB, _T("Unexpected Monitor ready event!")));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
TRC_ERR((TB, _T("Unknown event %d"), pClipPDU->msgType));
|
|
}
|
|
break;
|
|
}
|
|
|
|
TRC_NRM((TB, _T("Freeing processed PDU")));
|
|
LocalFree(pClipPDU);
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Now process constant messages */
|
|
/************************************************************************/
|
|
switch (message)
|
|
{
|
|
case WM_CREATE:
|
|
{
|
|
/****************************************************************/
|
|
/* We've been created - check the state */
|
|
/****************************************************************/
|
|
CBM_CHECK_STATE(CBM_EVENT_WM_CREATE);
|
|
|
|
TRC_NRM((TB, _T("Event CBM_EVENT_WM_CREATE OK")));
|
|
/****************************************************************/
|
|
/* Add the window to the clipboard viewer chain. */
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("SetClipboardViewer")));
|
|
CBM.nextViewer = SetClipboardViewer(hwnd);
|
|
CBM.fInClipboardChain = TRUE;
|
|
TRC_NRM((TB,_T("CBM.fInClipboardChain=%d"),
|
|
CBM.fInClipboardChain ? 1 : 0 ));
|
|
TRC_NRM((TB, _T("Back from SetClipboardViewer")));
|
|
|
|
/************************************************************************/
|
|
/* Register for TS session notifications */
|
|
/************************************************************************/
|
|
CBM.fRegisteredForSessNotif = WTSRegisterSessionNotification(hwnd, NOTIFY_FOR_THIS_SESSION);
|
|
if (0 == CBM.fRegisteredForSessNotif) {
|
|
TRC_ERR((TB,_T("Failed to register for session notifications")));
|
|
}
|
|
}
|
|
break;
|
|
|
|
#ifdef DC_DEBUG
|
|
case WM_PAINT:
|
|
{
|
|
/****************************************************************/
|
|
/* paint the window! */
|
|
/****************************************************************/
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
GetClientRect(hwnd, &rect);
|
|
FillRect(hdc, &rect, WHITE_BRUSH);
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
/****************************************************************/
|
|
/* We're being destroyed - check the state */
|
|
/****************************************************************/
|
|
CBM_CHECK_STATE(CBM_EVENT_WM_DESTROY);
|
|
TRC_NRM((TB, _T("WM_DESTROY")));
|
|
|
|
/****************************************************************/
|
|
/* Remove ourselves from the CB Chain */
|
|
/****************************************************************/
|
|
if (CBM.fInClipboardChain) {
|
|
if (!ChangeClipboardChain(hwnd, CBM.nextViewer))
|
|
{
|
|
TRC_SYSTEM_ERROR("ChangeClipboardChain");
|
|
}
|
|
CBM.nextViewer = NULL;
|
|
CBM.fInClipboardChain = FALSE;
|
|
TRC_NRM((TB,_T("CBM.fInClipboardChain=%d"),
|
|
CBM.fInClipboardChain ? 1 : 0 ));
|
|
}
|
|
|
|
if (CBM.fRegisteredForSessNotif) {
|
|
WTSUnRegisterSessionNotification(hwnd);
|
|
CBM.fRegisteredForSessNotif = FALSE;
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* and quit */
|
|
/****************************************************************/
|
|
PostQuitMessage(0);
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
{
|
|
/****************************************************************/
|
|
/* We're closing down. If this is not happening cleanly, then */
|
|
/* make sure we disconnect first */
|
|
/****************************************************************/
|
|
CBM_CHECK_STATE(CBM_EVENT_WM_CLOSE);
|
|
TRC_NRM((TB, _T("WM_CLOSE")));
|
|
if (CBM.state != CBM_STATE_INITIALIZED)
|
|
{
|
|
TRC_ALT((TB, _T("Close when not already back to state Init")));
|
|
CBMDisconnect();
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* and having done that, its safe to finish. */
|
|
/****************************************************************/
|
|
DestroyWindow(CBM.viewerWindow);
|
|
}
|
|
break;
|
|
|
|
case WM_CHANGECBCHAIN:
|
|
{
|
|
/****************************************************************/
|
|
/* The CB viewer chain is chainging - check the state */
|
|
/****************************************************************/
|
|
CBM_CHECK_STATE(CBM_EVENT_WM_CHANGECBCHAIN);
|
|
|
|
/****************************************************************/
|
|
/* If the next window is closing, repair the chain. */
|
|
/****************************************************************/
|
|
if ((HWND)wParam == CBM.nextViewer)
|
|
{
|
|
CBM.nextViewer = (HWND) lParam;
|
|
}
|
|
else if (CBM.nextViewer != NULL)
|
|
{
|
|
/************************************************************/
|
|
/* pass the message to the next link. */
|
|
/************************************************************/
|
|
PostMessage(CBM.nextViewer, message, wParam, lParam);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case WM_DRAWCLIPBOARD:
|
|
{
|
|
LPDATAOBJECT pIDataObject = NULL;
|
|
HRESULT hr ;
|
|
|
|
/****************************************************************/
|
|
/* The local clipboard contents have been changed. Check the */
|
|
/* state */
|
|
/****************************************************************/
|
|
if (CBMCheckState(CBM_EVENT_WM_DRAWCLIPBOARD) != CBM_TABLE_OK)
|
|
{
|
|
/************************************************************/
|
|
/* We're not interested at the moment - pass the message to */
|
|
/* the next link */
|
|
/************************************************************/
|
|
if (CBM.nextViewer != NULL)
|
|
{
|
|
TRC_NRM((TB, _T("Tell next viewer anyway")));
|
|
PostMessage(CBM.nextViewer, message, wParam, lParam);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* If it wasn't us that generated this change, then tell the */
|
|
/* client */
|
|
/****************************************************************/
|
|
drawRc = FALSE;
|
|
|
|
CBM.pClipData->QueryInterface(IID_IDataObject, (PPVOID) &pIDataObject) ;
|
|
hr = OleIsCurrentClipboard(pIDataObject) ;
|
|
|
|
if ((S_FALSE == hr))
|
|
{
|
|
TRC_NRM((TB, _T("...and it wasn't us"))) ;
|
|
drawRc = CBMDrawClipboard() ;
|
|
}
|
|
else
|
|
{
|
|
TRC_NRM((TB, _T("CB contents changed by us - ignoring")));
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* If the draw processing failed, or it was us that changed the */
|
|
/* CB, pass the message to the next window in the chain (if */
|
|
/* any) */
|
|
/****************************************************************/
|
|
if (!drawRc)
|
|
{
|
|
if (CBM.nextViewer != NULL)
|
|
{
|
|
/********************************************************/
|
|
/* pass the message to the next link. */
|
|
/********************************************************/
|
|
TRC_NRM((TB, _T("Tell next viewer")));
|
|
PostMessage(CBM.nextViewer, message, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
if (pIDataObject)
|
|
{
|
|
pIDataObject->Release();
|
|
pIDataObject = NULL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_POWERBROADCAST:
|
|
rc = TSSNDD_PowerMessage( wParam, lParam );
|
|
break;
|
|
|
|
case WM_ENDSESSION:
|
|
{
|
|
/****************************************************************/
|
|
/* The session is ending. Clean up here - we don't get a */
|
|
/* WM_QUIT, so we can't clean up in the normal place. We must */
|
|
/* clean up however, otherwise we generate a */
|
|
/* SESSION_HAS_VALID_PAGES fault. */
|
|
/****************************************************************/
|
|
/****************************************************************/
|
|
/* Remove ourselves from the CB Chain */
|
|
/****************************************************************/
|
|
if (CBM.fInClipboardChain) {
|
|
if (!ChangeClipboardChain(hwnd, CBM.nextViewer))
|
|
{
|
|
TRC_SYSTEM_ERROR("ChangeClipboardChain");
|
|
}
|
|
CBM.nextViewer = NULL;
|
|
CBM.fInClipboardChain = FALSE;
|
|
TRC_NRM((TB,_T("CBM.fInClipboardChain=%d"),
|
|
CBM.fInClipboardChain ? 1 : 0 ));
|
|
}
|
|
|
|
TRC_NRM((TB,_T("WM_ENDSESSION")));
|
|
|
|
CBMTerm();
|
|
TSSNDD_Term();
|
|
}
|
|
break;
|
|
|
|
case WM_WTSSESSION_CHANGE:
|
|
{
|
|
switch(wParam) {
|
|
case WTS_REMOTE_CONNECT: //A session was connected to the remote session.
|
|
{
|
|
TRC_NRM((TB,_T("WM_WTSSESSION_CHANGE WTS_REMOTE_CONNECT")));
|
|
|
|
if (FALSE == CBM.fInClipboardChain) {
|
|
|
|
/****************************************************************/
|
|
/* Add the window to the clipboard viewer chain. */
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("SetClipboardViewer")));
|
|
|
|
// Check to see that the first clipboard viewer in the chain is
|
|
// not this process. This helps to partially address RAID
|
|
// Bug #646295. A better fix would be to have a reliable means of
|
|
// removing ourselves from the clipboard viewer chain, but that
|
|
// does not currently exist with the current set of clipboard
|
|
// functions.
|
|
|
|
if (CBM.viewerWindow != GetClipboardViewer()) {
|
|
TRC_ERR((TB, _T("RDPClip already in clipboard chain.")));
|
|
CBM.nextViewer = SetClipboardViewer(hwnd);
|
|
}
|
|
|
|
CBM.fInClipboardChain = TRUE;
|
|
}
|
|
TRC_NRM((TB,_T("CBM.fInClipboardChain=%d"),
|
|
CBM.fInClipboardChain ? 1 : 0 ));
|
|
TRC_NRM((TB, _T("Back from SetClipboardViewer")));
|
|
break;
|
|
}
|
|
case WTS_REMOTE_DISCONNECT: //A session was disconnected from the remote session.
|
|
{
|
|
TRC_NRM((TB,_T("WM_WTSSESSION_CHANGE WTS_REMOTE_DISCONNECT")));
|
|
|
|
/****************************************************************/
|
|
/* Remove ourselves from the CB Chain */
|
|
/****************************************************************/
|
|
if (CBM.fInClipboardChain) {
|
|
if (!ChangeClipboardChain(hwnd, CBM.nextViewer))
|
|
{
|
|
TRC_SYSTEM_ERROR("ChangeClipboardChain");
|
|
}
|
|
CBM.nextViewer = NULL;
|
|
CBM.fInClipboardChain = FALSE;
|
|
TRC_NRM((TB,_T("CBM.fInClipboardChain=%d"),
|
|
CBM.fInClipboardChain ? 1 : 0 ));
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
TRC_NRM((TB,_T("WM_WTSSESSION_CHANGE wParam=0x%x"), wParam));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
/****************************************************************/
|
|
/* Ignore all other messages. */
|
|
/****************************************************************/
|
|
rc = DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
break;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
|
|
return(rc);
|
|
|
|
} /* CBMWndProc */
|
|
|
|
/****************************************************************************/
|
|
/* CBMDrawClipboard - send the local formats to the remote */
|
|
/****************************************************************************/
|
|
DCBOOL DCINTERNAL CBMDrawClipboard(DCVOID)
|
|
{
|
|
DCUINT numFormats;
|
|
DCUINT formatCount;
|
|
DCUINT formatID;
|
|
PTS_CLIP_FORMAT formatList;
|
|
PTS_CLIP_PDU pClipRsp = NULL;
|
|
TS_CLIP_PDU clipRsp;
|
|
DCUINT nameLen;
|
|
DCUINT pduLen;
|
|
DCUINT32 dataLen = 0;
|
|
DCINT rc1;
|
|
DCTCHAR formatName[TS_FORMAT_NAME_LEN + 1] = { 0 };
|
|
|
|
DCBOOL rc = TRUE;
|
|
DCBOOL fHdrop = FALSE ;
|
|
wchar_t tempDirW[MAX_PATH] ;
|
|
|
|
DC_BEGIN_FN("CBMDrawClipboard");
|
|
|
|
CBM.dropEffect = FO_COPY ;
|
|
CBM.fAlreadyCopied = FALSE ;
|
|
|
|
CBM_CHECK_STATE(CBM_EVENT_WM_DRAWCLIPBOARD);
|
|
|
|
/************************************************************************/
|
|
/* @@@ what tidy up is needed here if state is unusual? */
|
|
/************************************************************************/
|
|
|
|
/************************************************************************/
|
|
/* First we open the clipboard */
|
|
/************************************************************************/
|
|
if (!CBM.open)
|
|
{
|
|
if (!OpenClipboard(CBM.viewerWindow))
|
|
{
|
|
TRC_SYSTEM_ERROR("OpenCB");
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* It was/is open */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("CB opened")));
|
|
CBM.open = TRUE;
|
|
|
|
/************************************************************************/
|
|
/* Count the formats available, checking we don't blow our limit */
|
|
/************************************************************************/
|
|
numFormats = CountClipboardFormats();
|
|
if (numFormats == 0)
|
|
{
|
|
/********************************************************************/
|
|
/* clipboard has been emptied - send an empty list */
|
|
/********************************************************************/
|
|
pClipRsp = &clipRsp;
|
|
pduLen = sizeof(clipRsp);
|
|
dataLen = 0;
|
|
TRC_NRM((TB, _T("CB emptied")));
|
|
}
|
|
else
|
|
{
|
|
/********************************************************************/
|
|
/* build the format list */
|
|
/********************************************************************/
|
|
if (numFormats > CB_MAX_FORMATS)
|
|
{
|
|
TRC_ALT((TB, _T("Num formats %d too large - limit to %d"),
|
|
numFormats, CB_MAX_FORMATS));
|
|
numFormats = CB_MAX_FORMATS;
|
|
}
|
|
TRC_DBG((TB, _T("found %d formats"), numFormats));
|
|
|
|
/********************************************************************/
|
|
/* We need some memory for the format list - how much? */
|
|
/********************************************************************/
|
|
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 %d"), numFormats));
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* now get the buffer */
|
|
/********************************************************************/
|
|
pClipRsp = (PTS_CLIP_PDU)GlobalAlloc(GPTR, pduLen);
|
|
if (pClipRsp == NULL)
|
|
{
|
|
/****************************************************************/
|
|
/* If we supplied the last format list, we can no longer */
|
|
/* satisfy any requests, so empty the remote clipboard even */
|
|
/* though we can't send the new list */
|
|
/****************************************************************/
|
|
TRC_ERR((TB, _T("Failed to get format list mem - emptying remote")));
|
|
pClipRsp = &clipRsp;
|
|
pduLen = sizeof(clipRsp);
|
|
dataLen = 0;
|
|
}
|
|
else
|
|
{
|
|
/****************************************************************/
|
|
/* enumerate the formats */
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("Building list...")));
|
|
formatList = (PTS_CLIP_FORMAT)pClipRsp->data;
|
|
formatCount = 0;
|
|
formatID = EnumClipboardFormats(0); /* 0 starts enumeration */
|
|
|
|
while ((formatID != 0) && (formatCount < numFormats))
|
|
{
|
|
if ((CF_HDROP == formatID) && (CBM.fFileCutCopyOn))
|
|
{
|
|
fHdrop = TRUE ;
|
|
}
|
|
|
|
/************************************************************/
|
|
/* look for formats we don't send */
|
|
/************************************************************/
|
|
if ((formatID == CF_BITMAP) ||
|
|
(formatID == CF_DSPBITMAP) ||
|
|
(formatID == CF_ENHMETAFILE) ||
|
|
(formatID == CF_OWNERDISPLAY) ||
|
|
((formatID == CF_HDROP) && (!CBM.fFileCutCopyOn)))
|
|
{
|
|
// We send DIB not bitmap to avoid potential palette
|
|
// problems - this is a Windows recommendation. The
|
|
// remote Windows CB will provide the conversion to
|
|
// CF_BITMAP
|
|
//
|
|
// Similarly 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!
|
|
//
|
|
// And if File Cut/Copy is off, we can't do HDROP
|
|
TRC_NRM((TB, _T("Dropping format ID %d"), formatID));
|
|
goto CONTINUE_FORMAT_ENUM;
|
|
}
|
|
|
|
/************************************************************/
|
|
/* find the name for the format */
|
|
/************************************************************/
|
|
nameLen = GetClipboardFormatName(formatID,
|
|
formatName,
|
|
TS_FORMAT_NAME_LEN);
|
|
|
|
if (nameLen == 0)
|
|
{
|
|
/********************************************************/
|
|
/* predefined formats have no name */
|
|
/********************************************************/
|
|
TRC_NRM((TB, _T("predefined format %d - "), formatID));
|
|
formatList[formatCount].formatID = formatID;
|
|
*(formatList[formatCount].formatName) = '\0';
|
|
}
|
|
else if (CBMIsExcludedFormat(formatName))
|
|
{
|
|
/********************************************************/
|
|
/* We drop the various DDE formats, again because they */
|
|
/* just won't work where the apps are running on */
|
|
/* different machines */
|
|
/********************************************************/
|
|
TRC_NRM((TB, _T("Dropping format '%s'"), formatName));
|
|
goto CONTINUE_FORMAT_ENUM;
|
|
}
|
|
else
|
|
{
|
|
/********************************************************/
|
|
/* its got a name and its not excluded! */
|
|
/********************************************************/
|
|
formatList[formatCount].formatID = formatID;
|
|
|
|
/********************************************************/
|
|
/* Convert the name to Ascii? */
|
|
/********************************************************/
|
|
if (CBM.fUseAsciiNames)
|
|
{
|
|
/****************************************************/
|
|
/* Convert over to the ANSI codepage. Set no flags */
|
|
/* to maximise the speed of the conversion Set */
|
|
/* length to -1 since null-terminated. Set default */
|
|
/* chars to NULL to maximise speed. */
|
|
/****************************************************/
|
|
TRC_DBG((TB, _T("Converting to Ascii")));
|
|
rc1 = WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
(LPCWSTR)formatName,
|
|
-1,
|
|
(LPSTR)formatList[formatCount].formatName,
|
|
TS_FORMAT_NAME_LEN,
|
|
NULL,
|
|
NULL);
|
|
TRC_ASSERT((0 != rc1),
|
|
(TB, _T("Wide char conversion failed")));
|
|
|
|
TRC_DATA_DBG("Ascii name",
|
|
formatList[formatCount].formatName,
|
|
TS_FORMAT_NAME_LEN);
|
|
}
|
|
/********************************************************/
|
|
/* just copy the name */
|
|
/********************************************************/
|
|
else
|
|
{
|
|
//
|
|
// There is no explicit NULL termination at this
|
|
// point if the format name is more than 32 bytes.
|
|
// This will be rectified in Longhorn when we
|
|
// eliminate truncation of format names.
|
|
//
|
|
|
|
TRC_DBG((TB, _T("copying Unicode name")));
|
|
DC_TSTRNCPY(
|
|
(PDCTCHAR)(formatList[formatCount].formatName),
|
|
formatName,
|
|
TS_FORMAT_NAME_LEN / sizeof(WCHAR));
|
|
}
|
|
|
|
}
|
|
|
|
TRC_DBG((TB, _T("found format id %d, name '%s'"),
|
|
formatList[formatCount].formatID,
|
|
formatList[formatCount].formatName));
|
|
|
|
/************************************************************/
|
|
/* update the count and move on */
|
|
/************************************************************/
|
|
formatCount++;
|
|
|
|
CONTINUE_FORMAT_ENUM:
|
|
/************************************************************/
|
|
/* 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 %d"),
|
|
formatCount, dataLen));
|
|
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Close the Clipboard now */
|
|
/************************************************************************/
|
|
if (CBM.open)
|
|
{
|
|
TRC_NRM((TB, _T("Close clipboard")));
|
|
if (!CloseClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseClipboard");
|
|
}
|
|
CBM.open = FALSE;
|
|
}
|
|
// Only if we got an HDROP should we make a new temp directory
|
|
if (fHdrop)
|
|
{
|
|
if (GetTempFileNameW(CBM.baseTempDirW, L"_TS", 0, CBM.tempDirW)) {
|
|
DeleteFile(CBM.tempDirW) ;
|
|
CreateDirectoryW(CBM.tempDirW, NULL) ;
|
|
if (CBMConvertToClientPathW(CBM.tempDirW, tempDirW,
|
|
sizeof(tempDirW)) == S_OK) {
|
|
wcscpy(CBM.tempDirW, tempDirW) ;
|
|
WideCharToMultiByte(CP_ACP, NULL, CBM.tempDirW, -1,
|
|
CBM.tempDirA, wcslen(CBM.tempDirW), NULL, NULL) ;
|
|
}
|
|
else {
|
|
CBM.tempDirW[0] = L'\0';
|
|
CBM.tempDirA[0] = '\0';
|
|
}
|
|
|
|
}
|
|
else {
|
|
CBM.tempDirW[0] = L'\0';
|
|
CBM.tempDirA[0] = '\0';
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Update the state */
|
|
/************************************************************************/
|
|
CBM_SET_STATE(CBM_STATE_PENDING_FORMAT_LIST_RSP, CBM_EVENT_WM_DRAWCLIPBOARD);
|
|
|
|
/************************************************************************/
|
|
/* Complete the PDU */
|
|
/************************************************************************/
|
|
pClipRsp->msgType = TS_CB_FORMAT_LIST;
|
|
pClipRsp->msgFlags = 0;
|
|
pClipRsp->dataLen = dataLen;
|
|
|
|
/************************************************************************/
|
|
/* and send it to the Client */
|
|
/************************************************************************/
|
|
CBM.formatResponseCount++;
|
|
TRC_NRM((TB, _T("Pass format data to Client - %d response(s) pending"),
|
|
CBM.formatResponseCount));
|
|
CBMSendToClient(pClipRsp, pduLen);
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* free any memory we got */
|
|
/************************************************************************/
|
|
if ((pClipRsp != NULL) && (pClipRsp != &clipRsp))
|
|
{
|
|
GlobalFree(pClipRsp);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(rc);
|
|
} /* CBMDrawClipboard */
|
|
|
|
/****************************************************************************/
|
|
/* CBMOnFormatList */
|
|
/* Caller must have validated that the PDU contained enough data for the */
|
|
/* length specified in pClipPDU->dataLen */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CBMOnFormatList(PTS_CLIP_PDU pClipPDU)
|
|
{
|
|
DCUINT16 response = TS_CB_RESPONSE_OK;
|
|
DCUINT numFormats;
|
|
PTS_CLIP_FORMAT fmtList;
|
|
DCUINT i;
|
|
DCTCHAR formatName[TS_FORMAT_NAME_LEN + 1] = { 0 };
|
|
TS_CLIP_PDU clipRsp;
|
|
DCBOOL fSuccess;
|
|
LPDATAOBJECT pIDataObject = NULL;
|
|
LPFORMATETC pFormatEtc = NULL;
|
|
HRESULT hr ;
|
|
|
|
DC_BEGIN_FN("CBMOnFormatList");
|
|
|
|
/************************************************************************/
|
|
/* The client has sent us some new formats */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, _T("Received FORMAT_LIST")));
|
|
CBM_CHECK_STATE(CBM_EVENT_FORMAT_LIST);
|
|
|
|
/************************************************************************/
|
|
/* 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 list while pending list response")));
|
|
|
|
/********************************************************************/
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
CBM.formatResponseCount = 0;
|
|
|
|
/********************************************************************/
|
|
/* empty the CB and the client/server mapping table */
|
|
/********************************************************************/
|
|
//OleSetClipboard(NULL) ;
|
|
|
|
DC_MEMSET(CBM.idMap, 0, sizeof(CBM.idMap));
|
|
|
|
/********************************************************************/
|
|
/* See if we must use ASCII format names */
|
|
/********************************************************************/
|
|
CBM.fUseAsciiNames = (pClipPDU->msgFlags & TS_CB_ASCII_NAMES) ?
|
|
TRUE : FALSE;
|
|
|
|
/********************************************************************/
|
|
/* work out how many formats we got */
|
|
/********************************************************************/
|
|
numFormats = (pClipPDU->dataLen) / sizeof(TS_CLIP_FORMAT);
|
|
TRC_ASSERT(numFormats < CB_MAX_FORMATS,
|
|
(TB,_T("Too many formats recevied %d"),
|
|
numFormats));
|
|
TRC_NRM((TB, _T("PDU contains %d formats"), numFormats));
|
|
hr = CBM.pClipData->SetNumFormats(numFormats + 5) ; // Add 5 extra format slots
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
hr = CBM.pClipData->QueryInterface(IID_IDataObject, (PPVOID) &pIDataObject);
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB,_T("Error getting pointer to an IDataObject"))) ;
|
|
pIDataObject = NULL;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
/********************************************************************/
|
|
/* and add them to the clipboard */
|
|
/********************************************************************/
|
|
fmtList = (PTS_CLIP_FORMAT)pClipPDU->data;
|
|
for (i = 0; i < numFormats; i++)
|
|
{
|
|
TRC_DBG((TB, _T("format number %d, client id %d"),
|
|
i, fmtList[i].formatID));
|
|
/****************************************************************/
|
|
/* If we got a name... */
|
|
/****************************************************************/
|
|
if (fmtList[i].formatName[0] != 0)
|
|
{
|
|
/************************************************************/
|
|
/* clear out any garbage */
|
|
/************************************************************/
|
|
DC_MEMSET(formatName, 0, TS_FORMAT_NAME_LEN + 1);
|
|
|
|
/************************************************************/
|
|
/* Convert from Ascii? */
|
|
/************************************************************/
|
|
if (CBM.fUseAsciiNames)
|
|
{
|
|
TRC_NRM((TB, _T("Converting to Unicode")));
|
|
MultiByteToWideChar(
|
|
CP_ACP,
|
|
MB_ERR_INVALID_CHARS,
|
|
(LPCSTR)fmtList[i].formatName,
|
|
-1,
|
|
(LPWSTR)formatName,
|
|
TS_FORMAT_NAME_LEN);
|
|
}
|
|
else
|
|
{
|
|
/********************************************************/
|
|
/* just copy it */
|
|
/********************************************************/
|
|
//
|
|
// 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));
|
|
}
|
|
|
|
/************************************************************/
|
|
/* Check for excluded formats */
|
|
/************************************************************/
|
|
if (CBMIsExcludedFormat(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 client id */
|
|
/****************************************************************/
|
|
CBM.idMap[i].clientID = fmtList[i].formatID;
|
|
TRC_NRM((TB, _T("client id %d"), CBM.idMap[i].clientID));
|
|
|
|
/****************************************************************/
|
|
/* get local name (if needed) */
|
|
/****************************************************************/
|
|
if (formatName[0] != 0)
|
|
{
|
|
CBM.idMap[i].serverID = RegisterClipboardFormat(formatName);
|
|
}
|
|
else
|
|
{
|
|
/************************************************************/
|
|
/* it's a predefined format so we can just use the ID */
|
|
/************************************************************/
|
|
CBM.idMap[i].serverID = CBM.idMap[i].clientID;
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* and add the format to the local CB */
|
|
/****************************************************************/
|
|
TRC_DBG((TB, _T("Adding format '%s', client ID %d, server ID %d"),
|
|
formatName,
|
|
CBM.idMap[i].clientID,
|
|
CBM.idMap[i].serverID));
|
|
|
|
if (CBM.idMap[i].serverID != 0) {
|
|
pFormatEtc = new FORMATETC ;
|
|
if (pFormatEtc) {
|
|
pFormatEtc->cfFormat = (CLIPFORMAT) CBM.idMap[i].serverID ;
|
|
pFormatEtc->dwAspect = DVASPECT_CONTENT ;
|
|
pFormatEtc->ptd = NULL ;
|
|
pFormatEtc->lindex = -1 ;
|
|
pFormatEtc->tymed = TYMED_HGLOBAL ;
|
|
|
|
pIDataObject->SetData(pFormatEtc, NULL, TRUE) ;
|
|
delete pFormatEtc;
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB,_T("Invalid format dropped"))) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = OleSetClipboard(pIDataObject) ;
|
|
if (pIDataObject)
|
|
{
|
|
pIDataObject->Release();
|
|
pIDataObject = NULL ;
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
response = TS_CB_RESPONSE_FAIL;
|
|
}
|
|
CBM.open = FALSE ;
|
|
|
|
/************************************************************************/
|
|
// Now we can pass the response to the client
|
|
/************************************************************************/
|
|
clipRsp.msgType = TS_CB_FORMAT_LIST_RESPONSE;
|
|
clipRsp.msgFlags = response;
|
|
clipRsp.dataLen = 0;
|
|
fSuccess = CBMSendToClient(&clipRsp, sizeof(clipRsp));
|
|
TRC_NRM((TB, _T("Write to Client %s"), fSuccess ? _T("OK") : _T("failed")));
|
|
|
|
/************************************************************************/
|
|
/* Update the state according to how we got on */
|
|
/************************************************************************/
|
|
if (response == TS_CB_RESPONSE_OK)
|
|
{
|
|
CBM_SET_STATE(CBM_STATE_LOCAL_CB_OWNER, CBM_EVENT_FORMAT_LIST);
|
|
}
|
|
else
|
|
{
|
|
CBM_SET_STATE(CBM_STATE_CONNECTED, CBM_EVENT_FORMAT_LIST);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return;
|
|
} /* CBMOnFormatList */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* CBMDisconnect - either the client has disconnected, or we have been */
|
|
/* closed */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CBMDisconnect(DCVOID)
|
|
{
|
|
DC_BEGIN_FN("CBMDisconnect");
|
|
|
|
/************************************************************************/
|
|
/* 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 client */
|
|
/************************************************************************/
|
|
if ((CBM.state == CBM_STATE_LOCAL_CB_OWNER) ||
|
|
(CBM.state == CBM_STATE_PENDING_FORMAT_DATA_RSP))
|
|
{
|
|
TRC_NRM((TB, _T("Disable received while local CB owner")));
|
|
|
|
/********************************************************************/
|
|
/* Open the clipboard if needed */
|
|
/********************************************************************/
|
|
if (!CBM.open)
|
|
{
|
|
if (!OpenClipboard(CBM.viewerWindow))
|
|
{
|
|
TRC_SYSTEM_ERROR("OpenCB");
|
|
DC_QUIT;
|
|
}
|
|
CBM.open = TRUE;
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* It was/is open */
|
|
/****************************************************************/
|
|
TRC_NRM((TB, _T("CB opened")));
|
|
CBM.open = TRUE;
|
|
|
|
/****************************************************************/
|
|
/* Empty it */
|
|
/****************************************************************/
|
|
if (!EmptyClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("EmptyClipboard");
|
|
}
|
|
else
|
|
{
|
|
TRC_NRM((TB, _T("Clipboard emptied")));
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Ensure that we close the local CB */
|
|
/************************************************************************/
|
|
if (CBM.open)
|
|
{
|
|
if (!CloseClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseClipboard");
|
|
}
|
|
CBM.open = FALSE;
|
|
TRC_NRM((TB, _T("CB closed")));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Virtual channel has been closed */
|
|
/************************************************************************/
|
|
CloseHandle(CBM.vcHandle);
|
|
CBM.vcHandle = NULL;
|
|
|
|
//
|
|
// Switch off the file clipboard redirection flag, otherwise if a client
|
|
// with drive redirection disabled connects, we will still attempt to
|
|
// send file copy formats.
|
|
//
|
|
|
|
CBM.fFileCutCopyOn = FALSE;
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* Update our state */
|
|
/************************************************************************/
|
|
CBM_SET_STATE(CBM_STATE_INITIALIZED, CBM_EVENT_DISCONNECT);
|
|
|
|
DC_END_FN();
|
|
return;
|
|
} /* CBMDisconnect */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* CBMReconnect */
|
|
/****************************************************************************/
|
|
DCVOID DCINTERNAL CBMReconnect(DCVOID)
|
|
{
|
|
TS_CLIP_PDU clipRsp;
|
|
|
|
DC_BEGIN_FN("CBMReconnect");
|
|
|
|
SetEvent(CBM.GetDataSync[TS_RESET_EVENT]) ;
|
|
CBM_CHECK_STATE(CBM_EVENT_CONNECT);
|
|
|
|
CBM.vcHandle = NULL;
|
|
|
|
/************************************************************************/
|
|
/* Open our virtual channel */
|
|
/************************************************************************/
|
|
CBM.vcHandle = WinStationVirtualOpen(NULL, LOGONID_CURRENT, CLIP_CHANNEL);
|
|
if (CBM.vcHandle == NULL)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to open virtual channel %S"), CLIP_CHANNEL));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Send the Monitor Ready message to the Client */
|
|
/************************************************************************/
|
|
clipRsp.msgType = TS_CB_MONITOR_READY;
|
|
clipRsp.msgFlags = 0;
|
|
clipRsp.dataLen = 0;
|
|
if (!CBMSendToClient(&clipRsp, sizeof(clipRsp)))
|
|
{
|
|
/********************************************************************/
|
|
/* Failed to send the Monitor Ready message. Clip redirection is */
|
|
/* not available. */
|
|
/********************************************************************/
|
|
TRC_ERR((TB, _T("Failed to send MONITOR_READY to Client - Exit")));
|
|
CloseHandle(CBM.vcHandle);
|
|
CBM.vcHandle = NULL;
|
|
DC_QUIT;
|
|
}
|
|
TRC_NRM((TB, _T("Sent MONITOR_READY to Client")));
|
|
|
|
/************************************************************************/
|
|
/* Client support is enabled - we're all set */
|
|
/************************************************************************/
|
|
CBM_SET_STATE(CBM_STATE_CONNECTED, CBM_EVENT_CONNECT);
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return;
|
|
} /* CBMReconnect */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* CBMTerm - terminate the Clipboard Monitor */
|
|
/**PROC-*********************************************************************/
|
|
DCVOID DCINTERNAL CBMTerm(DCVOID)
|
|
{
|
|
HRESULT hr ;
|
|
DC_BEGIN_FN("CBMTerm");
|
|
|
|
/************************************************************************/
|
|
/* Tell the second thread to end */
|
|
/************************************************************************/
|
|
if (CBM.readOL.hEvent)
|
|
{
|
|
TRC_NRM((TB, _T("Signalling thread to stop")));
|
|
CBM.runThread = FALSE;
|
|
SetEvent(CBM.readOL.hEvent);
|
|
|
|
/********************************************************************/
|
|
/* Give the second thread a chance to finish */
|
|
/********************************************************************/
|
|
TRC_NRM((TB, _T("Wait a sec ...")));
|
|
if ( NULL != CBM.hDataThread )
|
|
{
|
|
WaitForSingleObject( CBM.hDataThread, INFINITE );
|
|
CloseHandle( CBM.hDataThread );
|
|
CBM.hDataThread = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Destroy the events */
|
|
/************************************************************************/
|
|
if (CBM.readOL.hEvent)
|
|
{
|
|
TRC_NRM((TB, _T("destroying read event %p"), CBM.readOL.hEvent));
|
|
if (!CloseHandle(CBM.readOL.hEvent))
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseHandle");
|
|
}
|
|
CBM.readOL.hEvent = NULL;
|
|
}
|
|
if (CBM.writeOL.hEvent)
|
|
{
|
|
TRC_NRM((TB, _T("destroying write event %p"), CBM.writeOL.hEvent));
|
|
if (!CloseHandle(CBM.writeOL.hEvent))
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseHandle");
|
|
}
|
|
CBM.writeOL.hEvent = NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Empty the clipboard if we own its contents */
|
|
/************************************************************************/
|
|
if (CBM.viewerWindow && (GetClipboardOwner() == CBM.viewerWindow))
|
|
{
|
|
TRC_NRM((TB, _T("We own the clipboard - empty it")));
|
|
|
|
hr = OleSetClipboard(NULL) ;
|
|
|
|
if (FAILED(hr)) {
|
|
TRC_SYSTEM_ERROR("Unable to clear clipboard") ;
|
|
}
|
|
}
|
|
if (CBM.pClipData)
|
|
{
|
|
CBM.pClipData->Release() ;
|
|
CBM.pClipData = NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Close thc clipboard if we have it open */
|
|
/************************************************************************/
|
|
if (CBM.open)
|
|
{
|
|
TRC_NRM((TB, _T("Close clipboard")));
|
|
if (!CloseClipboard())
|
|
{
|
|
TRC_SYSTEM_ERROR("CloseClipboard");
|
|
}
|
|
CBM.open = FALSE;
|
|
}
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return;
|
|
} /* CBMTerm */
|
|
|
|
/****************************************************************************/
|
|
// CBMIsExcludedFormat - test to see if the suplied format is on our
|
|
// "banned list"
|
|
/****************************************************************************/
|
|
DCBOOL DCINTERNAL CBMIsExcludedFormat(PDCTCHAR formatName)
|
|
{
|
|
DCBOOL rc = FALSE;
|
|
DCINT i;
|
|
|
|
DC_BEGIN_FN("CBMIsExcludedFormat");
|
|
|
|
/************************************************************************/
|
|
/* 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 (CBM.fFileCutCopyOn)
|
|
{
|
|
for (i = 0; i < CBM_EXCLUDED_FORMAT_COUNT; i++)
|
|
{
|
|
TRC_DBG((TB, _T("comparing with '%s'"), cbmExcludedFormatList[i]));
|
|
if (DC_WSTRCMP((PDCWCHAR)formatName,
|
|
(PDCWCHAR)cbmExcludedFormatList[i]) == 0)
|
|
{
|
|
TRC_NRM((TB, _T("Found excluded format '%s'"), formatName));
|
|
rc = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < CBM_EXCLUDED_FORMAT_COUNT_NO_RD; i++)
|
|
{
|
|
TRC_DBG((TB, _T("comparing with '%s'"), cbmExcludedFormatList_NO_RD[i]));
|
|
if (DC_WSTRCMP((PDCWCHAR)formatName,
|
|
(PDCWCHAR)cbmExcludedFormatList[i]) == 0)
|
|
{
|
|
TRC_NRM((TB, _T("Found excluded format '%s'"), formatName));
|
|
rc = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
|
|
return(rc);
|
|
} /* CBMIsExcludedFormat */
|
|
|
|
//
|
|
// CBMConvertToServerPath, CBMConvertToServerPathA, CBMConvertToServerPathW
|
|
// - 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 a colon, this function will strip out the old path,
|
|
// and prepend it with TS_PREPEND_STRING; otherwise, we just copy it over
|
|
// because we can already understand it
|
|
HRESULT CBMConvertToServerPath(PVOID pOldData, PVOID pData, size_t cbDest, BOOL wide)
|
|
{
|
|
HRESULT result ;
|
|
|
|
DC_BEGIN_FN("CBMConvertToServerPath") ;
|
|
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 (wide)
|
|
result = CBMConvertToServerPathW(pOldData, pData, cbDest) ;
|
|
else
|
|
result = CBMConvertToServerPathA(pOldData, pData, cbDest) ;
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN() ;
|
|
return result ;
|
|
|
|
}
|
|
|
|
HRESULT CBMConvertToServerPathW(PVOID pOldData, PVOID pData, size_t cbDest)
|
|
{
|
|
wchar_t* filePath ;
|
|
wchar_t* driveLetter ;
|
|
size_t driveLetterLength ;
|
|
HRESULT result = E_FAIL ;
|
|
|
|
DC_BEGIN_FN("CBMConvertToServerPathW") ;
|
|
|
|
// if this is a filepath with a drive letter
|
|
filePath = wcschr((wchar_t*)pOldData, L':') ;
|
|
if (filePath)
|
|
{
|
|
driveLetter = (wchar_t*)pOldData ;
|
|
result = StringCbCopyW( (wchar_t*)pData, cbDest, LTS_PREPEND_STRING) ;
|
|
DC_QUIT_ON_FAIL(result);
|
|
// Since there is actually a constant for the max
|
|
// drive letter length, we can't assume it will
|
|
// always be 1 character
|
|
driveLetterLength = (BYTE*)filePath - (BYTE*)driveLetter;
|
|
result = StringCbCatNW( (wchar_t*)pData, cbDest, driveLetter,
|
|
driveLetterLength);
|
|
DC_QUIT_ON_FAIL(result);
|
|
filePath = (wchar_t*) filePath + 1 ; // character after the ':'
|
|
result = StringCbCatW( (wchar_t*)pData, cbDest, filePath);
|
|
DC_QUIT_ON_FAIL(result);
|
|
TRC_NRM((TB,_T("New filename = %ls"), (wchar_t*)pData)) ;
|
|
result = S_OK ;
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Not a filepath with drive letter. Nothing converted"))) ;
|
|
result = StringCbCopyW((wchar_t*)pData, cbDest, (wchar_t*)pOldData);
|
|
DC_QUIT_ON_FAIL(result);
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
if (FAILED(result)) {
|
|
TRC_ERR((TB,_T("Returning failure; hr=0x%x"), result));
|
|
}
|
|
|
|
DC_END_FN() ;
|
|
return result ;
|
|
}
|
|
|
|
HRESULT CBMConvertToServerPathA(PVOID pOldData, PVOID pData, size_t cbDest)
|
|
{
|
|
char* filePath ;
|
|
char* driveLetter ;
|
|
size_t driveLetterLength ;
|
|
HRESULT result = E_FAIL ;
|
|
|
|
DC_BEGIN_FN("CBMConvertToServerPathW") ;
|
|
|
|
// if this is a filepath with a drive letter
|
|
filePath = strchr((char*)pOldData, ':') ;
|
|
if (filePath)
|
|
{
|
|
driveLetter = (char*)pOldData ;
|
|
result = StringCbCopyA( (char*)pData, cbDest, TS_PREPEND_STRING) ;
|
|
DC_QUIT_ON_FAIL(result);
|
|
// Since there is actually a constant for the max
|
|
// drive letter length, we can't assume it will
|
|
// always be 1 character
|
|
driveLetterLength = (BYTE*)filePath - (BYTE*)driveLetter;
|
|
result = StringCbCatNA( (char*)pData, cbDest, driveLetter,
|
|
driveLetterLength);
|
|
DC_QUIT_ON_FAIL(result);
|
|
filePath = (char*) filePath + 1 ; // character after the ':'
|
|
result = StringCbCatA( (char*)pData, cbDest, filePath);
|
|
DC_QUIT_ON_FAIL(result);
|
|
result = S_OK ;
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Not a filepath with drive letter. Nothing converted"))) ;
|
|
result = StringCbCopyA((char*)pData, cbDest, (char*)pOldData);
|
|
DC_QUIT_ON_FAIL(result);
|
|
result = E_FAIL ;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
if (FAILED(result)) {
|
|
TRC_ERR((TB,_T("Returning failure; 0x%x"), result));
|
|
}
|
|
|
|
DC_END_FN() ;
|
|
return result ;
|
|
}
|
|
|
|
//
|
|
// CBMGetNewDropfilesSizeForServer,
|
|
// CBMGetNewDropfilesSizeForServerW,
|
|
// CBMGetNewDropfilesSizeForServerA
|
|
// - Arguments:
|
|
// pData = Buffer containing a DROPFILES struct
|
|
// oldSize = The size of the DROPFILES struct
|
|
// wide = Wide or Ansi (TRUE if wide, FALSE if ansi)
|
|
// - Returns new size of the drop file
|
|
// 0 if it fails
|
|
// - Given a file path with drive letter, this function will calculate
|
|
// the needed space for the new string, when changed to \\tsclient
|
|
// format
|
|
//
|
|
//
|
|
// ***** NOTE *****
|
|
// - Currently, if the path is a network path, and not a drive path (C:\path)
|
|
// it simply fails
|
|
//
|
|
|
|
ULONG CBMGetNewDropfilesSizeForServer(PVOID pData, ULONG oldSize, BOOL wide)
|
|
{
|
|
DC_BEGIN_FN("CBMGetNewDropfilesSizeForServer") ;
|
|
if (wide)
|
|
return CBMGetNewDropfilesSizeForServerW(pData, oldSize) ;
|
|
else
|
|
return CBMGetNewDropfilesSizeForServerA(pData, oldSize) ;
|
|
DC_END_FN() ;
|
|
}
|
|
|
|
ULONG CBMGetNewDropfilesSizeForServerW(PVOID pData, ULONG oldSize)
|
|
{
|
|
ULONG newSize = oldSize ;
|
|
wchar_t* filenameW ;
|
|
wchar_t* filePathW ;
|
|
byte charSize ;
|
|
|
|
DC_BEGIN_FN("CBMGetNewDropfilesSizeForServerW") ;
|
|
charSize = sizeof(wchar_t) ;
|
|
if (!pData)
|
|
{
|
|
TRC_ERR((TB,_T("Pointer to dropfile is NULL"))) ;
|
|
return 0 ;
|
|
}
|
|
|
|
// The start of the first filename
|
|
filenameW = (wchar_t*) ((byte*) pData + ((DROPFILES*) pData)->pFiles) ;
|
|
|
|
while (L'\0' != filenameW[0])
|
|
{
|
|
TRC_NRM((TB,_T("First filename = %ls"), filenameW)) ;
|
|
filePathW = wcschr(filenameW, L':') ;
|
|
// If the file path has a colon in it, then it's a drive path
|
|
if (filePathW)
|
|
{
|
|
// we add space for (TS_PREPEND_LENGTH - 1) characters because
|
|
// although we are adding TS_PREPEND_LENGTH characters, we are
|
|
// stripping out the colon from the filepath
|
|
newSize = newSize + (TS_PREPEND_LENGTH - 1) * charSize ;
|
|
// going from c:\foo.txt -> \\tsclient\c\foo.txt adds
|
|
// \\tsclient\ and subtracts :
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB,_T("Bad path"))) ;
|
|
return 0 ;
|
|
}
|
|
filenameW = filenameW + (wcslen((wchar_t*)filenameW) + 1) ;
|
|
}
|
|
|
|
DC_END_FN() ;
|
|
return newSize ;
|
|
}
|
|
|
|
ULONG CBMGetNewDropfilesSizeForServerA(PVOID pData, ULONG oldSize)
|
|
{
|
|
ULONG newSize = oldSize ;
|
|
char* filename ;
|
|
char* filePath ;
|
|
byte charSize ;
|
|
|
|
DC_BEGIN_FN("CBMGetNewDropfilesSizeForServerW") ;
|
|
|
|
charSize = sizeof(wchar_t) ;
|
|
if (!pData)
|
|
{
|
|
TRC_ERR((TB,_T("Pointer to dropfile is NULL"))) ;
|
|
return 0 ;
|
|
}
|
|
|
|
// The start of the first filename
|
|
filename = (char*) ((byte*) pData + ((DROPFILES*) pData)->pFiles) ;
|
|
|
|
while ('\0' != filename[0])
|
|
{
|
|
filePath = strchr(filename, ':') ;
|
|
// If the file path has a colon in it, then its a drive path
|
|
if (filePath)
|
|
{
|
|
// we add space for (TS_PREPEND_LENGTH - 1) characters because
|
|
// although we are adding TS_PREPEND_LENGTH characters, we are
|
|
// stripping out the colon from the filepath
|
|
newSize = newSize + (TS_PREPEND_LENGTH - 1) * charSize ;
|
|
// going from c:\foo.txt -> \\tsclient\c\foo.txt adds
|
|
// \\tsclient\ and subtracts :
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB,_T("Bad path"))) ;
|
|
return 0 ;
|
|
}
|
|
filename = filename + (strlen(filename) + 1) ;
|
|
}
|
|
|
|
DC_END_FN() ;
|
|
return newSize ;
|
|
}
|
|
|
|
DCINT DCAPI CBMGetData (DCUINT cfFormat)
|
|
{
|
|
PTS_CLIP_PDU pClipPDU = NULL;
|
|
DCUINT32 pduLen;
|
|
DCUINT32 dataLen;
|
|
PDCUINT32 pFormatID;
|
|
DCUINT8 clipRsp[sizeof(TS_CLIP_PDU) + sizeof(DCUINT32)];
|
|
BOOL success = 0 ;
|
|
|
|
DC_BEGIN_FN("ClipGetData");
|
|
|
|
CBM_CHECK_STATE(CBM_EVENT_WM_RENDERFORMAT);
|
|
|
|
// Record the requested format
|
|
CBM.pendingServerID = cfFormat ;
|
|
CBM.pendingClientID = CBMRemoteFormatFromLocalID(CBM.pendingServerID);
|
|
|
|
// if we don't get a valid client ID, then fail
|
|
if (!CBM.pendingClientID)
|
|
{
|
|
TRC_NRM((TB, _T("Server format %d not supported/found. Failing"), CBM.pendingServerID)) ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
TRC_NRM((TB, _T("Render format received for %d (client ID %d)"),
|
|
CBM.pendingServerID, CBM.pendingClientID));
|
|
|
|
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 = (PTS_CLIP_PDU)(&clipRsp) ;
|
|
|
|
DC_MEMSET(pClipPDU, 0, sizeof(*pClipPDU)) ;
|
|
pClipPDU->msgType = TS_CB_FORMAT_DATA_REQUEST ;
|
|
pClipPDU->dataLen = dataLen ;
|
|
pFormatID = (PDCUINT32)(pClipPDU->data) ;
|
|
*pFormatID = (DCUINT32)CBM.pendingClientID ;
|
|
|
|
// Reset the TS_RECEIVE_COMPLETED event since we expect it to be signaled
|
|
// if any data is received from the client.
|
|
|
|
ResetEvent(CBM.GetDataSync[TS_RECEIVE_COMPLETED]);
|
|
|
|
// Send the PDU
|
|
TRC_NRM((TB, _T("Sending format data request"))) ;
|
|
success = CBMSendToClient(pClipPDU, sizeof(TS_CLIP_PDU) + sizeof(DCUINT32)) ;
|
|
|
|
DC_EXIT_POINT:
|
|
// Update the state if successful
|
|
if (success)
|
|
CBM_SET_STATE(CBM_STATE_PENDING_FORMAT_DATA_RSP, CBM_EVENT_WM_RENDERFORMAT) ;
|
|
|
|
DC_END_FN() ;
|
|
return success ;
|
|
}
|
|
|
|
CClipData::CClipData()
|
|
{
|
|
DC_BEGIN_FN("CClipData") ;
|
|
_cRef = 0 ;
|
|
_pImpIDataObject = NULL ;
|
|
DC_END_FN();
|
|
}
|
|
|
|
CClipData::~CClipData(void)
|
|
{
|
|
DC_BEGIN_FN("~CClipData");
|
|
|
|
if (_pImpIDataObject != NULL)
|
|
{
|
|
_pImpIDataObject->Release();
|
|
_pImpIDataObject = NULL;
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
HRESULT DCINTERNAL CClipData::SetNumFormats(ULONG numFormats)
|
|
{
|
|
DC_BEGIN_FN("SetNumFormats");
|
|
HRESULT hr = S_OK;
|
|
|
|
if (_pImpIDataObject)
|
|
{
|
|
_pImpIDataObject->Release();
|
|
_pImpIDataObject = NULL;
|
|
}
|
|
_pImpIDataObject = new CImpIDataObject(this) ;
|
|
if (_pImpIDataObject != NULL) {
|
|
_pImpIDataObject->AddRef() ;
|
|
|
|
hr = _pImpIDataObject->Init(numFormats) ;
|
|
DC_QUIT_ON_FAIL(hr);
|
|
}
|
|
else {
|
|
TRC_ERR((TB,_T("Unable to create IDataObject")));
|
|
hr = E_OUTOFMEMORY;
|
|
DC_QUIT;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return hr;
|
|
}
|
|
|
|
DCVOID CClipData::SetClipData(HGLOBAL hGlobal, DCUINT clipType)
|
|
{
|
|
DC_BEGIN_FN("SetClipData");
|
|
|
|
if (_pImpIDataObject != NULL) {
|
|
_pImpIDataObject->SetClipData(hGlobal, clipType) ;
|
|
}
|
|
DC_END_FN();
|
|
}
|
|
|
|
STDMETHODIMP CClipData::QueryInterface(REFIID riid, PPVOID ppv)
|
|
{
|
|
DC_BEGIN_FN("QueryInterface");
|
|
|
|
//set ppv to NULL just in case the interface isn't found
|
|
*ppv=NULL;
|
|
|
|
if (IID_IUnknown==riid)
|
|
*ppv=this;
|
|
|
|
if (IID_IDataObject==riid)
|
|
*ppv=_pImpIDataObject ;
|
|
|
|
if (NULL==*ppv)
|
|
return ResultFromScode(E_NOINTERFACE);
|
|
|
|
//AddRef any interface we'll return.
|
|
((LPUNKNOWN)*ppv)->AddRef();
|
|
DC_END_FN();
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CClipData::AddRef(void)
|
|
{
|
|
LONG cRef;
|
|
DC_BEGIN_FN("AddRef");
|
|
|
|
cRef = InterlockedIncrement(&_cRef) ;
|
|
|
|
DC_END_FN();
|
|
return 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)
|
|
{
|
|
DC_BEGIN_FN("CImplDataObject") ;
|
|
_numFormats = 0 ;
|
|
_maxNumFormats = 0 ;
|
|
_cRef = 0 ;
|
|
_pUnkOuter = lpUnk ;
|
|
if (_pUnkOuter)
|
|
{
|
|
_pUnkOuter->AddRef();
|
|
}
|
|
_pFormats = NULL ;
|
|
_pSTGMEDIUM = NULL ;
|
|
_lastFormatRequested = 0 ;
|
|
_dropEffect = FO_COPY ;
|
|
_cfDropEffect = (CLIPFORMAT) RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT) ;
|
|
_fAlreadyCopied = FALSE ;
|
|
DC_END_FN();
|
|
}
|
|
|
|
HRESULT CImpIDataObject::Init(ULONG numFormats)
|
|
{
|
|
DC_BEGIN_FN("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 == _pFormats) {
|
|
TRC_ERR((TB,_T("Failed to allocate STGMEDIUM")));
|
|
hr = E_OUTOFMEMORY;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (_pSTGMEDIUM != NULL) {
|
|
_pSTGMEDIUM->tymed = TYMED_HGLOBAL ;
|
|
_pSTGMEDIUM->pUnkForRelease = NULL ;
|
|
_pSTGMEDIUM->hGlobal = NULL ;
|
|
}
|
|
_uiSTGType = 0;
|
|
|
|
DC_EXIT_POINT:
|
|
if (FAILED(hr)) {
|
|
_maxNumFormats = 0;
|
|
}
|
|
|
|
DC_END_FN() ;
|
|
return hr;
|
|
}
|
|
|
|
DCVOID CImpIDataObject::SetClipData(HGLOBAL hGlobal, DCUINT clipType)
|
|
{
|
|
DC_BEGIN_FN("SetClipData");
|
|
if (!_pSTGMEDIUM)
|
|
_pSTGMEDIUM = (STGMEDIUM*) LocalAlloc(LPTR, sizeof(STGMEDIUM)) ;
|
|
|
|
if (_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;
|
|
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;
|
|
default:
|
|
GlobalFree( _pSTGMEDIUM->hGlobal );
|
|
}
|
|
_pSTGMEDIUM->hGlobal = NULL;
|
|
}
|
|
|
|
CImpIDataObject::~CImpIDataObject(void)
|
|
{
|
|
DC_BEGIN_FN("~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("QueryInterface");
|
|
DC_END_FN();
|
|
return _pUnkOuter->QueryInterface(riid, ppv);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CImpIDataObject::AddRef(void)
|
|
{
|
|
DC_BEGIN_FN("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 (cRef == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
DC_END_FN() ;
|
|
return 0;
|
|
}
|
|
|
|
// 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
|
|
TCHAR formatName[TS_FORMAT_NAME_LEN] ;
|
|
HGLOBAL hData = NULL ;
|
|
HPDCVOID pData ;
|
|
HPDCVOID pOldData ;
|
|
HPDCVOID pFilename ;
|
|
HPDCVOID pOldFilename ;
|
|
ULONG oldSize ;
|
|
ULONG newSize ;
|
|
byte charSize ;
|
|
DWORD eventSignaled ;
|
|
BOOL wide ;
|
|
DWORD* pDropEffect ;
|
|
DROPFILES tempDropfile ;
|
|
char* fileList ;
|
|
wchar_t* fileListW ;
|
|
HRESULT hr;
|
|
|
|
DC_BEGIN_FN("GetData");
|
|
|
|
if (!_pSTGMEDIUM)
|
|
{
|
|
TRC_ERR((TB, _T("Transfer medium (STGMEDIUM) is NULL"))) ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
if (!_pSTGMEDIUM->hGlobal || (pFE->cfFormat != _lastFormatRequested))
|
|
{
|
|
ResetEvent(CBM.GetDataSync[TS_RESET_EVENT]) ;
|
|
ResetEvent(CBM.GetDataSync[TS_DISCONNECT_EVENT]) ;
|
|
|
|
if (!CBMGetData(pFE->cfFormat))
|
|
DC_QUIT ;
|
|
|
|
do {
|
|
eventSignaled = WaitForMultipleObjects(
|
|
TS_NUM_EVENTS,
|
|
CBM.GetDataSync,
|
|
FALSE,
|
|
INFINITE);
|
|
} while (eventSignaled == (WAIT_OBJECT_0)) ;
|
|
|
|
TRC_NRM((TB, _T("EventSignaled = %d; GetLastError = %d"), eventSignaled, GetLastError())) ;
|
|
|
|
if ((WAIT_OBJECT_0 + TS_RESET_EVENT) == eventSignaled)
|
|
{
|
|
ResetEvent(CBM.GetDataSync[TS_RESET_EVENT]) ;
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
} else if ((WAIT_OBJECT_0 + TS_DISCONNECT_EVENT) == eventSignaled) {
|
|
ResetEvent(CBM.GetDataSync[TS_DISCONNECT_EVENT]) ;
|
|
CBM_SET_STATE(CBM_STATE_INITIALIZED, CBM_EVENT_DISCONNECT);
|
|
result = E_FAIL ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
// Make sure that we actually got data from the client.
|
|
|
|
if (_pSTGMEDIUM->hGlobal == NULL) {
|
|
TRC_ERR((TB, _T("No format data received from client!")));
|
|
result = E_FAIL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
// 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
|
|
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_ERR((TB, _T("Unable to lock %p"), _pSTGMEDIUM->hGlobal)) ;
|
|
_pSTGMEDIUM->hGlobal = NULL ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
// We strip shortcuts and moves this way
|
|
*pDropEffect = *pDropEffect ^ DROPEFFECT_LINK ;
|
|
*pDropEffect = *pDropEffect ^ DROPEFFECT_MOVE ;
|
|
CBM.dropEffect = *pDropEffect ;
|
|
|
|
GlobalUnlock(_pSTGMEDIUM->hGlobal) ;
|
|
|
|
pSTM->tymed = _pSTGMEDIUM->tymed ;
|
|
pSTM->hGlobal = _pSTGMEDIUM->hGlobal ;
|
|
// bugbug
|
|
_pSTGMEDIUM->hGlobal = NULL;
|
|
// bugbug: end
|
|
pSTM->pUnkForRelease = _pSTGMEDIUM->pUnkForRelease ;
|
|
|
|
result = S_OK ;
|
|
DC_QUIT ;
|
|
}
|
|
else if (CF_HDROP == pFE->cfFormat)
|
|
{
|
|
BYTE *pbLastByte, *pbStartByte, *pbLastPossibleNullStart, charSize;
|
|
BOOL fTrailingFileNamesValid;
|
|
SIZE_T cbDropFiles;
|
|
|
|
//
|
|
// Make sure that we have at least a DROPFILES structure in
|
|
// memory.
|
|
|
|
cbDropFiles = GlobalSize(_pSTGMEDIUM->hGlobal);
|
|
if (cbDropFiles < sizeof(DROPFILES)) {
|
|
TRC_ERR((TB, _T("Unexpected global memory size!")));
|
|
result = E_FAIL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
pOldData = GlobalLock(_pSTGMEDIUM->hGlobal) ;
|
|
|
|
if (!pOldData)
|
|
{
|
|
TRC_ERR((TB, _T("Unable to lock %p"), _pSTGMEDIUM->hGlobal)) ;
|
|
_pSTGMEDIUM->hGlobal = NULL ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
wide = ((DROPFILES*) pOldData)->fWide ;
|
|
|
|
//
|
|
// 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 (((DROPFILES*) pOldData)->pFiles < sizeof(DROPFILES)
|
|
|| ((DROPFILES*) pOldData)->pFiles > cbDropFiles) {
|
|
TRC_ERR((TB,_T("File name offset invalid!"))) ;
|
|
result = E_FAIL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
pbStartByte = (BYTE*) pOldData + ((DROPFILES*) pOldData)->pFiles;
|
|
pbLastByte = (BYTE*) pOldData + cbDropFiles - 1;
|
|
fTrailingFileNamesValid = FALSE;
|
|
charSize = wide ? sizeof(WCHAR) : sizeof(CHAR);
|
|
|
|
//
|
|
// 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 (wide) {
|
|
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!"))) ;
|
|
result = E_FAIL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// DROPFILES are valid so we can continue.
|
|
//
|
|
|
|
oldSize = (ULONG) GlobalSize(_pSTGMEDIUM->hGlobal) ;
|
|
newSize = CBMGetNewDropfilesSizeForServer(pOldData, oldSize, wide) ;
|
|
if (!newSize)
|
|
{
|
|
TRC_ERR((TB, _T("Unable to parse DROPFILES"))) ;
|
|
}
|
|
else
|
|
{
|
|
TRC_NRM((TB, _T("DROPFILES Old size= %d New size = %d"),
|
|
GlobalSize(_pSTGMEDIUM->hGlobal), newSize)) ;
|
|
}
|
|
|
|
hData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE,
|
|
newSize) ;
|
|
if (!hData)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to alloc %ld bytes"),
|
|
hData, newSize));
|
|
GlobalFree(hData);
|
|
hData = NULL;
|
|
return E_FAIL ;
|
|
}
|
|
pData = GlobalLock(hData) ;
|
|
if (!pData)
|
|
{
|
|
TRC_ERR((TB, _T("Failed to lock %p (%ld bytes)"),
|
|
pData, newSize));
|
|
return E_FAIL ;
|
|
}
|
|
((DROPFILES*) pData)->pFiles = ((DROPFILES*) pOldData)->pFiles ;
|
|
((DROPFILES*) pData)->pt = ((DROPFILES*) pOldData)->pt ;
|
|
((DROPFILES*) pData)->fNC = ((DROPFILES*) pOldData)->fNC ;
|
|
((DROPFILES*) pData)->fWide = ((DROPFILES*) pOldData)->fWide ;
|
|
|
|
pOldFilename = (byte*) pOldData + ((DROPFILES*) pOldData)->pFiles ;
|
|
pFilename = (byte*) pData + ((DROPFILES*) pData)->pFiles ;
|
|
// We keep looping until the current
|
|
if (wide)
|
|
{
|
|
while (L'\0' != ((wchar_t*) pOldFilename)[0])
|
|
{
|
|
if ( (ULONG)((BYTE*)pFilename - (BYTE*)pData) > newSize) {
|
|
TRC_ERR((TB,_T("Out of space, failed to convert")));
|
|
result = E_FAIL;
|
|
GlobalUnlock(hData) ;
|
|
GlobalUnlock(_pSTGMEDIUM->hGlobal) ;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (S_OK != CBMConvertToServerPath(pOldFilename, pFilename,
|
|
newSize - ((BYTE*)pFilename - (BYTE*)pData), wide))
|
|
{
|
|
TRC_ERR((TB, _T("Failed conversion"))) ;
|
|
result = E_FAIL ;
|
|
GlobalUnlock(hData) ;
|
|
GlobalUnlock(_pSTGMEDIUM->hGlobal) ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
TRC_NRM((TB,_T("oldname %ls; newname %ls"), (wchar_t*)pOldFilename, (wchar_t*)pFilename)) ;
|
|
pOldFilename = (byte*) pOldFilename + (wcslen((wchar_t*)pOldFilename) + 1) * sizeof(wchar_t) ;
|
|
pFilename = (byte*) pFilename + (wcslen((wchar_t*)pFilename) + 1) * sizeof(wchar_t) ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while ('\0' != ((char*) pOldFilename)[0])
|
|
{
|
|
if ( (ULONG)((BYTE*)pFilename - (BYTE*)pData) > newSize) {
|
|
TRC_ERR((TB,_T("Out of space, failed to convert")));
|
|
result = E_FAIL;
|
|
GlobalUnlock(hData) ;
|
|
GlobalUnlock(_pSTGMEDIUM->hGlobal) ;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (S_OK != CBMConvertToServerPath(pOldFilename, pFilename,
|
|
newSize - ((BYTE*)pFilename - (BYTE*)pData), wide))
|
|
{
|
|
TRC_ERR((TB, _T("Failed conversion"))) ;
|
|
result = E_FAIL ;
|
|
GlobalUnlock(hData) ;
|
|
GlobalUnlock(_pSTGMEDIUM->hGlobal) ;
|
|
DC_QUIT ;
|
|
}
|
|
|
|
TRC_NRM((TB,_T("oldname %hs; newname %hs"), (char*)pOldFilename, (char*)pFilename)) ;
|
|
pOldFilename = (byte*) pOldFilename + (strlen((char*)pOldFilename) + 1) * sizeof(char) ;
|
|
pFilename = (byte*) pFilename + (strlen((char*)pFilename) + 1) * sizeof(char) ;
|
|
}
|
|
}
|
|
|
|
if (wide)
|
|
{
|
|
(((wchar_t*) pFilename)[0]) = L'\0';
|
|
}
|
|
else
|
|
{
|
|
(((char*) pFilename)[0]) = '\0';
|
|
}
|
|
|
|
GlobalUnlock(hData) ;
|
|
GlobalUnlock(_pSTGMEDIUM->hGlobal) ;
|
|
|
|
pSTM->tymed = _pSTGMEDIUM->tymed ;
|
|
pSTM->hGlobal = hData ;
|
|
pSTM->pUnkForRelease = _pSTGMEDIUM->pUnkForRelease ;
|
|
|
|
result = S_OK ;
|
|
DC_QUIT ;
|
|
}
|
|
else
|
|
{
|
|
DC_MEMSET(formatName, 0, TS_FORMAT_NAME_LEN*sizeof(TCHAR));
|
|
|
|
if (0 != GetClipboardFormatName(pFE->cfFormat, formatName,
|
|
TS_FORMAT_NAME_LEN))
|
|
{
|
|
// if the remote system is requesting a filename, then we
|
|
// must translate the path from Driveletter:\path to
|
|
// \\tsclient\Driveletter\path before we hand this off
|
|
if ((0 == _tcscmp(formatName, TEXT("FileName"))) ||
|
|
(0 == _tcscmp(formatName, TEXT("FileNameW"))))
|
|
{
|
|
if (0 == _tcscmp(formatName, TEXT("FileNameW")))
|
|
{
|
|
wide = TRUE ;
|
|
charSize = sizeof(wchar_t);
|
|
}
|
|
else
|
|
{
|
|
wide = FALSE ;
|
|
charSize = sizeof(char);
|
|
}
|
|
|
|
pOldFilename = GlobalLock(_pSTGMEDIUM->hGlobal) ;
|
|
|
|
if (pOldFilename != NULL)
|
|
{
|
|
//
|
|
// Check that pOldFilename is NULL terminated.
|
|
//
|
|
|
|
size_t cbMaxOldFileName, cbOldFileName;
|
|
|
|
cbMaxOldFileName = (ULONG) GlobalSize(_pSTGMEDIUM->hGlobal);
|
|
|
|
if (wide) {
|
|
hr = StringCbLengthW((WCHAR*) pOldFilename,
|
|
cbMaxOldFileName,
|
|
&cbOldFileName);
|
|
} else {
|
|
hr = StringCbLengthA((CHAR*) pOldFilename,
|
|
cbMaxOldFileName,
|
|
&cbOldFileName);
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
TRC_ERR((TB, _T("File name not NULL terminated!")));
|
|
result = E_FAIL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We are now assured that pOldFilename is NULL terminated
|
|
// and can continue.
|
|
//
|
|
|
|
newSize = cbMaxOldFileName + (TS_PREPEND_LENGTH - 1) * charSize;
|
|
hData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE, newSize);
|
|
|
|
if (hData != NULL)
|
|
{
|
|
pFilename = GlobalLock(hData);
|
|
if (pFilename != NULL)
|
|
{
|
|
if (S_OK != CBMConvertToServerPath(pOldFilename,
|
|
pFilename, newSize, wide))
|
|
{
|
|
TRC_ERR((TB, _T("Failed filename conversion"))) ;
|
|
}
|
|
GlobalUnlock(hData) ;
|
|
GlobalFree(_pSTGMEDIUM->hGlobal) ;
|
|
_pSTGMEDIUM->hGlobal = hData ;
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Failed to lock %p (%ld bytes)"),
|
|
hData, newSize));
|
|
GlobalFree(hData);
|
|
hData = NULL;
|
|
return E_FAIL ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Failed to alloc %ld bytes"), newSize));
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, _T("Failed to lock %p"),
|
|
_pSTGMEDIUM->hGlobal)) ;
|
|
return E_FAIL ;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB,_T("Requested format %d"), pFE->cfFormat)) ;
|
|
}
|
|
|
|
pSTM->tymed = _pSTGMEDIUM->tymed ;
|
|
pSTM->hGlobal = _pSTGMEDIUM->hGlobal ;
|
|
// bugbug
|
|
_pSTGMEDIUM->hGlobal = NULL;
|
|
// bugbug: end
|
|
pSTM->pUnkForRelease = _pSTGMEDIUM->pUnkForRelease ;
|
|
result = S_OK ;
|
|
}
|
|
}
|
|
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 bugbug
|
|
if (!_pSTGMEDIUM->hGlobal)
|
|
#else
|
|
if (!pSTM->hGlobal)
|
|
#endif // bugbug
|
|
{
|
|
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("GetDataHere") ;
|
|
DC_END_FN();
|
|
return ResultFromScode(E_NOTIMPL) ;
|
|
}
|
|
|
|
STDMETHODIMP CImpIDataObject::QueryGetData(LPFORMATETC pFE)
|
|
{
|
|
ULONG i = 0 ;
|
|
HRESULT hr = DV_E_CLIPFORMAT ;
|
|
|
|
DC_BEGIN_FN("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("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 our _pSTGMEDIUM is generated using generic values
|
|
// ***************************************************************************
|
|
|
|
STDMETHODIMP CImpIDataObject::SetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM, BOOL fRelease)
|
|
{
|
|
TCHAR formatName[TS_FORMAT_NAME_LEN] = {0} ;
|
|
unsigned i ;
|
|
DC_BEGIN_FN("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;
|
|
|
|
DC_BEGIN_FN("CImpIDataObject::EnumFormatEtc");
|
|
|
|
*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 ;
|
|
DC_END_FN() ;
|
|
}
|
|
|
|
STDMETHODIMP CImpIDataObject::DAdvise(LPFORMATETC pFE, DWORD dwFlags,
|
|
LPADVISESINK pIAdviseSink, LPDWORD pdwConn)
|
|
{
|
|
DC_BEGIN_FN("CImpIDataObject::DAdvise");
|
|
DC_END_FN() ;
|
|
return ResultFromScode(E_NOTIMPL) ;
|
|
}
|
|
|
|
STDMETHODIMP CImpIDataObject::DUnadvise(DWORD dwConn)
|
|
{
|
|
DC_BEGIN_FN("CImpIDataObject::DUnadvise");
|
|
DC_END_FN() ;
|
|
return ResultFromScode(E_NOTIMPL) ;
|
|
}
|
|
|
|
STDMETHODIMP CImpIDataObject::EnumDAdvise(LPENUMSTATDATA *ppEnum)
|
|
{
|
|
DC_BEGIN_FN("CImpIDataObject::EnumDAdvise");
|
|
DC_END_FN() ;
|
|
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 (NULL != _pFormats)
|
|
LocalFree(_pFormats) ;
|
|
if (_pUnkRef)
|
|
{
|
|
_pUnkRef->Release();
|
|
_pUnkRef = NULL;
|
|
}
|
|
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) ;
|
|
// should return UnkRef's RefCount?
|
|
_pUnkRef->AddRef();
|
|
|
|
return cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CEnumFormatEtc::Release(void)
|
|
{
|
|
LONG cRef;
|
|
|
|
DC_BEGIN_FN("CEnumFormatEtc::Release");
|
|
|
|
_pUnkRef->Release();
|
|
|
|
cRef = InterlockedDecrement(&_cRef) ;
|
|
|
|
if (cRef == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
DC_END_FN() ;
|
|
return 0;
|
|
}
|
|
|
|
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)
|
|
{
|
|
PCEnumFormatEtc pNew = NULL;
|
|
LPMALLOC pIMalloc;
|
|
LPFORMATETC prgfe;
|
|
BOOL fRet=TRUE;
|
|
ULONG cb;
|
|
|
|
*ppEnum=NULL;
|
|
|
|
//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;
|
|
}
|
|
}
|
|
|
|
pIMalloc->Release();
|
|
|
|
*ppEnum=pNew;
|
|
return fRet ? NOERROR : ResultFromScode(E_OUTOFMEMORY);
|
|
}
|
|
|