Creating a Remote Desktop Plugin Using Delphi - Part 5

The last thing that I need to do to my remote desktop plugin is add the code required to send data from the client to the server.

Actually sending the data from the client to the server is quite simple, a single function call (VirtualChannelWrite), but there is a complicating factor.  The write happens asynchronously.  This means that we need to keep the data buffer around until the write is finished.  This is handled by adding code to the VirtualChannelOpenEvent handler that detects when the write is complete and frees the buffer.

Code to write the packet:


var
  stat: UINT;
begin
  StatusForm.SetStatus(Format('OpenHandle = %d Writing %d bytes',
                  [Handle, OutputBufferSize]));
  stat := gEntryPoints.pVirtualChannelWrite(Handle,
                  OutputBuffer,
                  OutputBufferSize,
                  nil);
  if (TChannelReturnCodes(stat) <> crOk) then
      StatusForm.SetStatus(Format('Write failed! Error = %d.', [stat]));
end;

Changes to VirtualChannelOpenEvent handler:


  case e of
    ceDataReceived:
       ...
    ceWriteComplete:
    begin
      StatusForm.SetStatus(Format('Open Handle = %d Write complete', [openHandle]));
      FreeMem(gChannels[i].OutputBuffer);
      gChannels[i].OutputBuffer := nil;
    end;

    ceWriteCancelled:
    begin
      StatusForm.SetStatus(Format('Open Handle = %d Write cancelled', [openHandle]));
      FreeMem(gChannels[i].OutputBuffer);
      gChannels[i].OutputBuffer := nil;
    end;
  end;


On the server side we need to add code to read the response from the client. This is also quite simple, involving a single function  (WTSVirtualChannelRead), but again there is a complicating factor.  The read function only reads CHANNEL_CHUNK_LENGTH bytes, maximum, in a single call.  So we need to know how many bytes to expect and we need to loop until we have read that many bytes.

Code to read the packet:


var
  bytesRead: Cardinal;
  totalRead: Cardinal;
  err: Integer;
begin
  totalRead := 0;
  while (totalRead < SizeOf(pkt)) do
  begin
    if (not WTSVirtualChannelRead(FChannelHandle,
              INFINITE,
              PByte(@pkt) + totalRead,
              SizeOf(pkt) - totalRead,
              bytesRead)) then
    begin
      err := GetLastError;
      raise Exception.CreateFmt('Read failed! Error = %d.', [err]);
    end;
    Inc(totalRead, bytesRead);
  end;
  ShowMessageFmt('Read %d bytes - %20.20s', [totalRead, String(AnsiString(pkt.LBData))]);
end;

The source code for the plugin can be found here.

The first article in the series can be found here.

3 Responses to “Creating a Remote Desktop Plugin Using Delphi - Part 5”

  1. Ted Says:

    I have also implemented a plugin such as this a few years ago using Delphi5 and some Project JEDI wrappers named CChannel.pas and PChannel.pas for the definitions of the different structures. This worked flawlessly until I needed to develop a 64-bit Virtual Channel. I dutifully upgraded to Delphi XE3 and upgraded my code so that it would compile without error.

    The problem that I am now having, both with my code and yours, is that when I close the RDP/Terminal Service session, the plugin crashes mstsc.exe. Have you run into this at all? I am currently running RDP version 6.2.9200.16398 which I believe is the latest and greatest version.

  2. admin Says:

    Mine is working perfectly. I’m not sure why mstsc.exe would be crashing for you. I have tried this on several different versions of the RDP client without any problems.

  3. hamid Says:

    hello
    I compile code i can succesfully sending string from remote desktop session to dll an show it on status form but how can i send message from dll to remote session ?
    can u please help me?
    regards

Leave a Reply