Creating a Remote Desktop Plugin Using Delphi - Part 2

In part 1 of this series I set about creating a remote desktop plugin using Delphi. It didn’t do anything except initialize itself and display a few message boxes indicating its progress.  Not very useful.

In order for this whole thing to work the way I want it to, the plugin is going to have to interact with the user on the remote workstation.  This means displaying a form that will have to be on top of the remote desktop window.  I had some vague misgivings that remote desktop in full screen mode would be an “always on top” window that would interfere with the interaction that I need. So, as the next step in the process, I changed the plugin to create a form, make it an “always on top” window and display my status messages in a TMemo contained in the form.  Surprisingly enough this actually works quite well.

To make the Delphi form “always on top” I just created a method named SetTopmost and called if from the form’s OnShow event.


procedure TStatusForm.SetTopmost;
begin
  SetWindowPos(Handle,
    HWND_TOPMOST,
    Left,
    Top,
    Width,
    Height,
    SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
end;


Since a useful plugin needs to be able to respond to events from the remote desktop client, I also implemented a VirtualChannelInitEvent handler in this version of the plugin.  This writes the event ID and remote server host name to the status form and destroys the status form when the remote desktop client terminates.


procedure  VirtualChannelInitEvent(pInitHandle: THandle;
               event: UINT;
               pData: Pointer;
               dataLength: UINT); stdcall;
const
  events: array [TChannelEvents] of String = (
    'Initialized',
    'Connected',
    'V1 Connected',
    'Disconnected',
    'Terminated',
    'Unknown 5',
    'Unknown 6',
    'Unknown 7',
    'Unknown 8',
    'Unknown 9',
    'Data Received',
    'Write Complete',
    'Write Cancelled'
);
var
  e: TChannelEvents;
begin
  e := TChannelEvents(event);
  if ((e >= Low(events)) and (e <= High(events))) then
    StatusForm.SetStatus(events[e])
  else
    StatusForm.SetStatus(Format('Unknown event id = %d', [event]));
  case e of
    ceConnected:
      StatusForm.SetStatus(Format('Server name = %s length = %d',
        [String(PChar(pData)),
         dataLength]));
    ceTerminated:
      FreeAndNil(StatusForm);
  end;
end;

You can get the source code for the plugin as it stands so far here.

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

  1. Peter Says:

    Hi

    Thanks for the great series of articles. However this code does not work on Windows Server 2012 or Windows 8 (or Higher) as the server operating system. The status form shows and it shows the Initialized line but does not show “Connected” or the server name. This means the examples later in the series of articles do not work. Any clues as to what has gone wrong?

    Thanks

  2. lnsgod Says:

    You are quite right. I just found this out recently myself. RDP was completely rewritten in 2012. I had to take a really big wrench to this to get it working again. I had to move all client side logic to a separate worker thread to get things moving again.

  3. Peter Says:

    Hi Insgod

    Thanks for the info. Are you able to give me some more pointers as to where the thread would be created? Is it in the VirtualChannelEntry function or the VirtualChannelInitEvent or somewhere else?

    Thanks
    Peter

  4. lnsgod Says:

    It actually got quite complicated. Easier to show you than try to explain it. I have put the source code for the entire plugin on my FTP site so you can look at it.

    ftp://ftp.lnssoftware.ca/tmp/AurScannerPlugin.zip

    I will leave it there for a few days and then delete it. So grab it quickly if you are interested.

  5. Peter Says:

    Hi Insgod

    Thanks for the files. It’s going to take me a bit to get my head around it but that is what weekends are for.

    Thanks again
    Peter

Leave a Reply