private void HandleClientDisconnect(ISServerSocket client) { if (inputController.CurrentInputClient == client) { inputController.SetInputClient(ISServerSocket.Localhost); } clientMan.RemoveClient(client); ISLogger.Write("Client {0} lost connection", client.ClientName); //Remove the clients hotkey if it exists if (inputMan.GetClientHotkey(client.ClientId) != null) { inputMan.RemoveClientHotkey(client.ClientId); } ClientDisconnected?.Invoke(this, new ISClientInfoModel(client, clientMan)); client.Dispose(); }
private void LoadAndStart() { try { clientInstance = new ISClient(); clientInstance.Start(new StartOptions(new System.Collections.Generic.List <string>(new string[] { "Verbose" })), WindowsDependencies.GetServiceDependencies(spMainHandle, spClipboardHandle)); clientInstance.ConnectionError += ClientInstance_ConnectionError; clientInstance.ConnectionFailed += ClientInstance_ConnectionFailed; clientInstance.Connected += ClientInstance_Connected; clientInstance.SasRequested += (object a, EventArgs b) => InputshareLibWindows.Native.Sas.SendSAS(false); clientInstance.Disconnected += ClientInstance_Disconnected; clientInstance.AutoReconnect = true; try { appHost = new NetIpcHost(clientInstance, "App connection"); }catch (Exception ex) { ISLogger.Write("Failed to create NetIPC host: " + ex.Message); } if (ConfigFile.TryReadProperty(ServiceConfigProperties.LastConnectedAddress, out string addrStr)) { if (IPEndPoint.TryParse(addrStr, out IPEndPoint addr)) { clientInstance.Connect(addr); } else { ISLogger.Write("Invalid address in config"); } } ISLogger.Write("Service started..."); } catch (Exception ex) { ISLogger.Write("LAUNCH ERROR - " + ex.Message); ISLogger.Write(ex.StackTrace); Stop(); } }
private async void Host_RequestedReadStream(object sender, AnonIpcHost.StreamReadRequestArgs args) { try { if (!callbacks.TryGetValue(args.Token, out CallbackHolder cbHolder)) { return; } byte[] data = await cbHolder.RequestPart(args.Token, args.FileId, args.ReadLen); clipboardHost.host.SendReadReply(args.MessageId, data); } catch (Exception ex) { ISLogger.Write("ServiceDragDropManager: Failed to send file data: " + ex.Message); ISLogger.Write(ex.StackTrace); } }
public void DeleteToken(Guid token) { if (currentAccessTokens.ContainsKey(token)) { currentAccessTokens.TryGetValue(token, out IFileAccessToken access); if (access == null) { ISLogger.Write("FileAccessController: Could not delete access token: Access token was null"); return; } access.CloseAllStreams(); } else { ISLogger.Write("FileAccessController: Could not delete access token: Key {0} not found", token); } }
private void TcpListener_ClientConnected(object sender, ClientConnectedArgs e) { ConnectedClient c = new ConnectedClient(e.ClientSocket, e.ClientName, e.ClientId); try { clientMan.AddClient(c); } catch (ClientManager.DuplicateNameException) { ISLogger.Write("Declining client {0}: Name already in use", c.ClientName); c.SendMessage(InputshareLib.Net.Messages.MessageType.ClientDuplicateName); c.Dispose(); return; } catch (ClientManager.DuplicateGuidException) { ISLogger.Write("Declining client {0}: Guid already in use", c.ClientName); c.SendMessage(InputshareLib.Net.Messages.MessageType.ClientDuplicateGuid); c.Dispose(); return; } catch (ClientManager.MaxClientsReachedException) { ISLogger.Write("Declining client {0}: Max clients reached", c.ClientName); c.SendMessage(InputshareLib.Net.Messages.MessageType.ClientLimitReached); c.Dispose(); return; } ApplyClientConfig(c.ClientGuid); c.SendMessage(InputshareLib.Net.Messages.MessageType.ServerOK); ISLogger.Write("{0} connected from {1}", e.ClientName, e.ClientSocket.RemoteEndPoint); ConnectedClientInfo info = CreateClientInfo(c, true); ClientConnected?.Invoke(this, CreateClientInfo(c, true)); c.ClipboardTextCopied += C_ClipboardTextCopied; c.ConnectionError += C_ConnectionError; c.ClientEdgeHit += OnAnyEdgeHit; }
private MemoryStream GetFileDescriptor(List <ClipboardVirtualFileData.FileAttributes> files) { try { MemoryStream FileDescriptorMemoryStream = new MemoryStream(); // Write out the FILEGROUPDESCRIPTOR.cItems value FileDescriptorMemoryStream.Write(BitConverter.GetBytes(files.Count), 0, sizeof(UInt32)); FILEDESCRIPTOR FileDescriptor = new FILEDESCRIPTOR(); foreach (var si in files) { string n = si.RelativePath; //If the file is in the root folder of the operation, the relative path will not be set! if (string.IsNullOrEmpty(si.RelativePath)) { n = si.FileName; } FileDescriptor.cFileName = n; Int64 FileWriteTimeUtc = si.LastChangeTime.ToFileTimeUtc(); FileDescriptor.ftLastWriteTime.dwHighDateTime = (Int32)(FileWriteTimeUtc >> 32); FileDescriptor.ftLastWriteTime.dwLowDateTime = (Int32)(FileWriteTimeUtc & 0xFFFFFFFF); FileDescriptor.nFileSizeHigh = (UInt32)(si.FileSize >> 32); FileDescriptor.nFileSizeLow = (UInt32)(si.FileSize & 0xFFFFFFFF); FileDescriptor.dwFlags = NativeMethods.FD_WRITESTIME | NativeMethods.FD_FILESIZE | NativeMethods.FD_PROGRESSUI; Int32 FileDescriptorSize = Marshal.SizeOf(FileDescriptor); IntPtr FileDescriptorPointer = Marshal.AllocHGlobal(FileDescriptorSize); Marshal.StructureToPtr(FileDescriptor, FileDescriptorPointer, true); Byte[] FileDescriptorByteArray = new Byte[FileDescriptorSize]; Marshal.Copy(FileDescriptorPointer, FileDescriptorByteArray, 0, FileDescriptorSize); Marshal.FreeHGlobal(FileDescriptorPointer); FileDescriptorMemoryStream.Write(FileDescriptorByteArray, 0, FileDescriptorByteArray.Length); } return(FileDescriptorMemoryStream); }catch (Exception ex) { ISLogger.Write("Get file descriptor failed: " + ex.Message); return(null); } }
private void HandleTargetRequest(XSelectionRequestEvent evt) { if (copiedData == null) { ISLogger.Write("Ignoring target request... no data is copied"); return; } byte[] raw = new byte[0]; if (copiedData.DataType == ClipboardDataType.Text) { raw = GetAtomsBytesText(); } else if (copiedData.DataType == ClipboardDataType.Image) { raw = GetAtomsBytesImage(); } XChangeProperty(xConnection.XDisplay, evt.requestor, evt.property, new IntPtr(4), 32, 0, raw, 1); }
/// <summary> /// We want to use scan codes instead of virtual codes as they are inserted lower into the stack, /// but some virtual keys cannot be mapped into scan codes so we need to decide whether to use /// the scan or virtual key /// </summary> /// <param name="input"></param> /// <param name="useScan"></param> /// <returns></returns> private short CheckKey(ISInputData input, out bool useScan) { useScan = false; if (input.Param2 == 0 || input.Param1 == 91) { return(input.Param1); } uint mappedScanb = MapVirtualKeyA((uint)input.Param1, MAPVKTYPE.MAPVK_VK_TO_VSC); if (mappedScanb != input.Param2) { ISLogger.Write("Invalid key virtual key:{0} scan code:{1} mapped: {2}", input.Param1, input.Param2, mappedScanb); return(input.Param2); } useScan = true; return(input.Param2); }
/// <summary> /// We want to use scan codes instead of virtual codes as they are inserted lower into the stack, /// but some virtual keys cannot be mapped into scan codes so we need to decide whether to use /// the scan or virtual key /// </summary> /// <param name="input"></param> /// <param name="useScan"></param> /// <returns></returns> private short CheckKey(ISInputData input, out bool useScan) { useScan = false; if (input.Param2 == 0 || input.Param1 == 91) { return(input.Param1); } uint mappedScanb = MapVirtualKeyA((uint)input.Param1, MAPVKTYPE.MAPVK_VK_TO_VSC); if (mappedScanb != input.Param2) { ISLogger.Write("Cannot translate key '{0}' to a scan code!", (WindowsVirtualKey)input.Param1); return(input.Param1); } useScan = true; return(input.Param2); }
protected override void OnStart(string[] args) { ISLogger.Write("----------------------------------------------"); ISLogger.Write("Inputshare service starting..."); ISLogger.Write("Console session state: " + Session.ConsoleSessionState); ISLogger.Write("Loaded config"); SetPriority(); Task.Run(() => { SpDragDropTaskLoop(); }); Task.Run(() => { SpMainTaskLoop(); }); //todo Thread.Sleep(1500); Task.Run(() => { LoadAndStart(); }); base.OnStart(args); }
internal void Local_DataDropped(object sender, ClipboardDataBase cbData) { if (!Server.IsConnected) { return; } if (CurrentOperation != null && !previousOperations.ContainsKey(CurrentOperation.OperationId)) { previousOperations.Add(CurrentOperation.OperationId, CurrentOperation); } if (Server.IsConnected) { Guid opId = Guid.NewGuid(); CurrentOperation = new DragDropOperation(opId, cbData); ISLogger.Write("LocalDragDropController: Started dragdrop operation " + opId); Server.SendDragDropData(cbData.ToBytes(), opId); } }
private async void Host_RequestedFileToken(object sender, AnonIpcHost.FileTokenRequestArgs args) { try { if (!callbacks.TryGetValue(args.Operation, out CallbackHolder cbHolder)) { return; } Guid token = await cbHolder.RequestToken(args.Operation); callbacks.Add(token, cbHolder); clipboardHost.host.SendFileTokenResponse(args.MessageId, token); ISLogger.Write("Sent file token response " + token); }catch (Exception ex) { ISLogger.Write("ServiceDragDropManager: Failed to send file token: " + ex.Message); ISLogger.Write(ex.StackTrace); } }
/// <summary> /// Sends any message defined in InputshareLib.Net.Messages /// Larger messages are automatically split into smalller messages /// that can be read by the receiving socket /// </summary> /// <param name="message"></param> protected void SendMessage(NetworkMessage message) { try { byte[] data = message.ToBytes(); if (data.Length > Settings.NetworkMessageChunkSize) { SendLargeMessage(message); return; } if (tcpSocket != null && tcpSocket.Connected) { tcpSocket.BeginSend(data, 0, data.Length, SocketFlags.None, TcpSocket_SendCallback, null); } }catch (Exception ex) { ISLogger.Write("Sendmessage error: " + ex.Message); } }
private static ClipboardVirtualFileData ReadFileDrop(System.Windows.Forms.IDataObject data) { string[] files = (string[])data.GetData(DataFormats.FileDrop); DirectoryAttributes root = new DirectoryAttributes("", new List <FileAttributes>(), new List <DirectoryAttributes>(), ""); int fCount = 0; foreach (var file in files) { try { System.IO.FileAttributes fa = System.IO.File.GetAttributes(file); if (fa.HasFlag(System.IO.FileAttributes.Directory)) { DirectoryAttributes di = new DirectoryAttributes(new DirectoryInfo(file)); root.SubFolders.Add(di); foreach (var baseFile in Directory.GetFiles(file)) { FileAttributes df = new FileAttributes(new FileInfo(baseFile)); df.RelativePath = Path.Combine(di.Name, df.FileName); di.Files.Add(df); } } else { root.Files.Add(new FileAttributes(new FileInfo(file))); } } catch (ClipboardTranslationException ex) { ISLogger.Write("An error occurred while reading attributes for {0}. File not copied\n{1}", file, ex.Message); } } foreach (var folder in root.SubFolders) { AddDirectoriesRecursive(folder, folder.Name, ref fCount); } return(new ClipboardVirtualFileData(root)); }
private void SocketSendThreadLoop() { try { while (!cancelToken.IsCancellationRequested) { INetworkMessage msg; if (SendQueue.Count != 0) //if there are items waiting in the normal priority queue, send that item { msg = SendQueue.Take(); } else if (LowPrioritySendQueue.Count != 0) //else if normal queue is empty, and there is an item in the low priorty queue, send that item { msg = LowPrioritySendQueue.Take(); } else { BlockingCollection <INetworkMessage> .TakeFromAny(queueCollection, out msg, cancelToken.Token); //if both queues are empty, wait for either queue to receive an item } byte[] data = msg.ToBytes(); clientSocket.Send(data); if (msg.Type == MessageType.FileTransferPart) //tell the send file thread that a part has been sent { fileTransferPartSentEvent.Set(); } } } catch (ObjectDisposedException) { } catch (OperationCanceledException) { } catch (Exception ex) { ISLogger.Write($"{ClientName} send thread error: {ex.Message}"); OnConnectionError(); } }
private void CSocket_ConnectionFailed(object sender, EventArgs e) { ISLogger.Write($"Service->Failed to connect to {cSocket.serverAddress}"); if (namedIpc.Active) { namedIpc.SendObject(NIpcBasicMessage.ConnectionFailed); } if (keepRetryingConnection) { try { ISLogger.Write($"Service->Auto retry enabled... trying to reconnect in 2s"); DelayReconnect(2000); } catch (Exception ex) { ISLogger.Write($"Service->Auto retry error: {ex.Message}"); } } }
/// <summary> /// If localhost drops files, each virtualfile will be given this delegate to allow it to read data /// from the operation host. The virtual file will contain the token that it should use to access the files. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void VirtualFile_ReadComplete(object sender, EventArgs e) { ClipboardVirtualFileData.FileAttributes file = sender as ClipboardVirtualFileData.FileAttributes; ISLogger.Write("DragDropController: File {0} read", file.FileName); if (CurrentOperation.RemoteFileAccessToken != file.RemoteAccessToken) { if (previousOperationIds.TryGetValue(file.FileOperationId, out DragDropOperation operation)) { operation.Host.RequestCloseStream(file.RemoteAccessToken, file.FileRequestId); } else { ISLogger.Write("DragDropController: Error sending stream close for file {0}: cannot find dragdrop operation", file.FileName); } } else { CurrentOperation.Host.RequestCloseStream(file.RemoteAccessToken, file.FileRequestId); } }
public void HandleInputReceived(ISInputData input) { if (CurrentInputClient != ISServerSocket.Localhost) { if (CurrentInputClient.IsConnected) { if (CurrentInputClient.UdpEnabled && udpHost != null) { udpHost.SendInput(input, CurrentInputClient); } else { CurrentInputClient.SendInputData(input.ToBytes()); } } else { ISLogger.Write("TARGET NOT CONNECTED"); } } }
static void Main(string[] args) { AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; ISLogger.SetLogFileName("InputshareSP.log"); ISLogger.EnableConsole = true; ISLogger.EnableLogFile = true; if (args.Length != 3) { OnInvalidArgs(); return; } string mode = args[0]; string readPipe = args[1]; string writePipe = args[2]; AnonIpcClient client = new AnonIpcClient(readPipe, writePipe, "ServiceIPC"); if (!client.ConnectedEvent.WaitOne(2000)) { ISLogger.Write("Failed to connect to service... exiting"); return; } ISLogger.Write("Connected to service IPC!"); if (mode == "Default") { DefaultHost.SPDefaultHost.Init(client); } else if (mode == "Clipboard") { ClipboardHost.SPClipboardHost.Init(client); } else { OnInvalidArgs(); } }
private void monitorTimerCallback(object sync) { IntPtr window = GetForegroundWindow(); if (window == null) { ISLogger.Write("GetForegroundWindow() returned null"); return; } int w = GetSystemMetrics(0); //width of primay monitor int h = GetSystemMetrics(1); //height of primary monitor GetWindowRect(window, out RECT rect); int pw = Math.Abs(rect.left - rect.right); int ph = Math.Abs(rect.top - rect.bottom); //To detect a fullscreen application, we check to see if size of the foreground window is equal to the primary monitor //This only works for fullscreen applications that are displayed on primary monitor if (pw == w && ph == h) { if (!FullscreenApplicationRunning) { CurrentFullscreenProcess = GetForegroundWindowProcess(window); ProcessEnteredFullscreen?.Invoke(this, CurrentFullscreenProcess); FullscreenApplicationRunning = true; } } else { if (FullscreenApplicationRunning) { ProcessExitedFullscreen?.Invoke(this, CurrentFullscreenProcess); CurrentFullscreenProcess = null; FullscreenApplicationRunning = false; } } }
/// <summary> /// This method runs on a session change and launches a new instance of SP if required. /// </summary> /// <param name="changeDescription"></param> protected override void OnSessionChange(SessionChangeDescription changeDescription) { if (!launcher.SpRunning) { ISLogger.Write("SP not found... launching"); try { launcher.LaunchSp(ipcWrite.PipeHandleAsString, ipcRead.PipeHandleAsString); } catch (Exception ex) { if (ex is InvalidOperationException || ex is Win32Exception) { ISLogger.Write($"Failed to launch inputsharesp: {ex.Message}"); } } } base.OnSessionChange(changeDescription); }
public override object GetData(string format, bool autoConvert) { ISLogger.Write("Requesting format " + format); //Don't tell the shell that we have file contents if we only have text or an image if (objectType == ClipboardDataType.File) { if (String.Compare(format, NativeMethods.CFSTR_FILEDESCRIPTORW, StringComparison.OrdinalIgnoreCase) == 0 && operationFiles != null) { base.SetData(NativeMethods.CFSTR_FILEDESCRIPTORW, fileDescriptorStream); } else if (String.Compare(format, NativeMethods.CFSTR_FILECONTENTS, StringComparison.OrdinalIgnoreCase) == 0) { base.SetData(NativeMethods.CFSTR_FILECONTENTS, GetFileContents(m_lindex)); } else if (String.Compare(format, NativeMethods.CFSTR_PERFORMEDDROPEFFECT, StringComparison.OrdinalIgnoreCase) == 0) { base.SetData(NativeMethods.CFSTR_PREFERREDDROPEFFECT, DragDropEffects.All); } } return(base.GetData(format, autoConvert)); }
private Process LaunchSPMain() { IntPtr sysToken = IntPtr.Zero; try { iHostMain?.Dispose(); iHostMain = new AnonIpcHost("SP main"); ISLogger.Write("Launching SP default process"); if (Settings.DEBUG_SPECIFYSPSESSION != -1) { sysToken = unchecked (Token.GetSystemToken((uint)Settings.DEBUG_SPECIFYSPSESSION)); } else { sysToken = Token.GetSystemToken(Session.ConsoleSessionId); } Process proc = ProcessLauncher.LaunchSP(ProcessLauncher.SPMode.Default, WindowsDesktop.Default, Settings.DEBUG_SPCONSOLEENABLED, iHostMain, sysToken); Token.CloseToken(sysToken); spMainHandle.host = iHostMain; spMainHandle.NotifyHandleUpdate(); return(proc); } catch (Exception ex) { ISLogger.Write("Failed to launch inputshareSP main process: " + ex.Message); return(null); } finally { if (sysToken != IntPtr.Zero) { Token.CloseToken(sysToken); } } }
private void CSocket_ConnectionError(object sender, EventArgs e) { ISLogger.Write($"Service->Socket error. disconnected"); if (namedIpc.Active) { namedIpc.SendObject(NIpcBasicMessage.ConnectionError); } if (keepRetryingConnection) { try { ISLogger.Write($"Service->Auto retry enabled... trying to reconnect"); DelayReconnect(2000); } catch (Exception ex) { ISLogger.Write($"Service->Auto retry error: {ex.Message}"); } } }
void IStream.Read(Byte[] buffer, Int32 bufferSize, IntPtr bytesReadPtr) { try { //TODO - shell reads 16 bytes of data and discards it when the drop begins?? if (bufferSize == 16) { Marshal.WriteInt32(bytesReadPtr, -1); return; } //check if file is fully read if (readLen >= SourceVirtualFile.FileSize) { Marshal.WriteInt32(bytesReadPtr, 0); return; } //using await will break the dragdrop operation! byte[] data = sourceClipboardData.RequestPartMethod(accessToken, SourceVirtualFile.FileRequestId, bufferSize).Result; //check that close has not been called if (data.Length == 0) { Marshal.WriteInt32(bytesReadPtr, 0); return; } //copy the data to the buffer, and write the length of the data to BytesReadPtr Buffer.BlockCopy(data, 0, buffer, 0, data.Length); Marshal.WriteInt32(bytesReadPtr, data.Length); readLen += data.Length; } catch (Exception ex) { ISLogger.Write("ManagedRemoteIStream: Failed to read remote file: " + ex.Message); ISLogger.Write(ex.StackTrace); } }
private void C_ConnectionError(object sender, EventArgs e) { ConnectedClient c = sender as ConnectedClient; if (currentInputClient == c) { SwitchLocalInput(); } try { inputMan.RemoveClientHotkey(c.ClientGuid); } catch (InvalidOperationException ex) { ISLogger.Write("Could not remove hotkey for client {0}: {1}", c.ClientName, ex.Message); } ISLogger.Write("{0} disconnected: Connection error", c.ClientName); clientMan.RemoveClient(c); ClientDisconnected?.Invoke(this, new ClientDisconnectedArgs(CreateClientInfo(c, true), "Connection error")); c.Dispose(); }
private void DoDragDrop(ClipboardDataBase data) { InputshareDataObject nativeObject = null; try { nativeObject = ClipboardTranslatorWindows.ConvertToWindows(data); reportedSuccess = false; nativeObject.SetData("InputshareData", new bool()); dropQueue.Enqueue(nativeObject); nativeObject.DropComplete += NativeObject_DropComplete; nativeObject.DropSuccess += NativeObject_DropSuccess; } catch (Exception ex) { ISLogger.Write("Could not start drag drop operation: " + ex.Message); return; } this.Show(); this.BringToFront(); this.TopMost = true; GetCurrentCursorMonitorSize(out Size mSize, out Point mPos); this.SetDesktopLocation((int)mPos.X, (int)mPos.Y); this.Size = mSize; outMan.Send(new InputshareLib.Input.ISInputData(InputshareLib.Input.ISInputCode.IS_MOUSELDOWN, 0, 0)); outMan.Send(new InputshareLib.Input.ISInputData(InputshareLib.Input.ISInputCode.IS_MOUSERUP, 0, 0)); Task.Run(() => { Thread.Sleep(300); this.Invoke(new Action(() => { if (!registeredDrop) { DoDragDrop(data); } })); }); }
protected override void OnStop() { try { stopping = true; ISLogger.Write("Inputshare service stopping..."); ISLogger.Write("Killing child processes..."); iHostClipboard?.Dispose(); iHostMain?.Dispose(); if (ISLogger.BufferedMessages > 0) { Thread.Sleep(1000); } foreach (var proc in Process.GetProcessesByName("inputsharesp")) { proc.Kill(); } if (clientInstance.Running) { clientInstance.Stop(); } } catch (Exception ex) { ISLogger.Write("An error occurred while stopping service: " + ex.Message); ISLogger.Write(ex.StackTrace); Thread.Sleep(3000); } finally { base.OnStop(); } }
internal static void Init(AnonIpcClient client) { ISLogger.SetLogFileName("InputshareSP_ClipboardHost.log"); ISLogger.Write("Starting SP clipboard host"); clipMan = new WindowsClipboardManager(); clipMan.Start(); ddMan = new WindowsDragDropManager(); ddMan.Start(); iClient = client; iClient.ClipboardDataReceived += IClient_ClipboardDataReceived; iClient.CheckForDropReceived += IClient_CheckForDropReceived; iClient.DoDragDropReceived += IClient_DoDragDropReceived; iClient.Disconnected += IClient_Disconnected; clipMan.ClipboardContentChanged += ClipMan_ClipboardContentChanged; ddMan.DataDropped += DdMan_DataDropped; ddMan.DragDropCancelled += DdMan_DragDropCancelled; ddMan.DragDropSuccess += DdMan_DragDropSuccess; mouseStateTimer = new Timer(MouseStateTimerCallback, 0, 0, 100); }
private void BeginTextOrImageOperation(ClipboardDataBase cbData, Guid operationId) { //If the previous dragdrop operation is still transfering files, store it so that the files can keep being transfered if (CurrentOperation != null && CurrentOperation.State == DragDropState.TransferingFiles) { previousOperationIds.Add(CurrentOperation.OperationId, CurrentOperation); } //Create a new operation with the text/image data, we don't care about the ID or host //as text and image data is not streamed the same way as file data. Text/Image data are sent as a single block CurrentOperation = new DragDropOperation(cbData, null, operationId); if (currentInputClient.IsLocalhost) { ddManager.DoDragDrop(cbData, operationId); } else { ISLogger.Write("SDragDropController: Sending dragdrop type {0} to {1}", cbData.DataType, currentInputClient.ClientName); currentInputClient.SendDragDropData(cbData.ToBytes(), CurrentOperation.OperationId); } }