|
|
/**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); }
|