I have just commited an updated version of my Terminal Server unit (revision 842) into SVN. I have done some tests on Windows 7 (a fix for Windows 7 was done in Jwa revision 830 btw) and all seems to work well.

Internally the Connect method of TJwTerminalServer verifies the connection to Terminal Server by reading some property. This is done because of a change in the API’s by Microsoft: opening a handle with WTSOpenServer to a nonexisting server actually returns a valid handle. I reported this as a bug to Microsoft some time ago and the “fixed it” by changing the documentation.

For those interested in the code: it is a class function called IsValidServerHandle which can be used to verify a handle returned by WTSOpenServer:

class function TJwTerminalServer.IsValidServerHandle(const hServer: THandle): Boolean;
var
  dwSize: DWORD;
  WinStaInfo: WINSTATIONINFORMATIONW;
begin
  { All Windows < Vista return 0 on Invalid handle }
  Result := hServer <> 0;

  { No need to continue if it's already invalid... }
  if not Result then
    Exit;

  { For Windows >= Vista we need to make a query to be sure
    A non valid connection will fail on WTSQuerySessionInformation and will
    return LastError RPC_S_SERVER_UNAVAILABLE }
  if (TJwWindowsVersion.IsWindowsVista(True)) or
    (TJwWindowsVersion.IsWindows2008(True)) then
  begin
    Result := WinStationQueryInformationW(hServer, 65536, WinStationInformation,
      @WinStaInfo, sizeof(WinStaInfo), dwSize) or (GetLastError() <> RPC_S_SERVER_UNAVAILABLE);
  end;
end;

A more visible change is the introduction of the Session function of TJwTerminalServer, with this function you can get all details for a specific session. It is meant for those cases where you need only one specific session and is of course far more efficient than enumerating all sessions, retrieving all sessions details and then finding the particular session in the SessionList.

The signature of the function is:

function TJwTerminalServer.Session(const SessionId: TJwSessionId = WTS_CURRENT_SESSION): TJwWTSSession;

Note that the parameter is default so you can call both:

Session := TS.Session;  // Retrieve the current Session

and

Session := TS.Session(1); // Retrieve the session with SessionId 1

It is important to understand the difference between Session and Sessions: where Session returns an instance of TJwWTSSession, SessionS returns a SessionList. This is especially important because Session works with a SessionId parameters and Sessions (being a TObjectList descendant) is accessed with by Index which is just the position in the ObjectList.

If a Session is not found an EJwsclWinCallFailedException will be raised so you’d better wrap the call into a try..except handler!

The last remark is that you, the caller, are responsible for freeing the object so a more complete sample would be like this:

var
  TS: TJwTerminalServer;
  Session: TJwWTSSession;
begin
  TS := TJwTerminalServer.Create;
  try
    try
      Session := TS.Session;
      try
        // Work with the session object
        ShowMessageFmt('Username=%s', [Session.Username]);
        Session.PostMessage('Hello from the Jedi guys!', 'Message', MB_OK);
      finally
        FreeAndNil(Session);
      end;
    except
      // Handle Exception here!
    end;
  finally
    FreeAndNil(TS);
  end;
end;