Delphi6: Need to know is my application running in console session or remote desktop session

StackOverflow https://stackoverflow.com/questions/17603520

  •  02-06-2022
  •  | 
  •  

Question

My application is accessed from Remote Desktop clients from time to time.

I want to know is it currently being used in console session or Remote Desktop session. If the latter is the case and the session is disconnected (user has disconnected but not logged off) it should redirect itself to console (like tscon.exe 0 /dest:console does on Windows XP).

I'm currently running a shell script to achieve that (using query.exe user and tscon.exe) but would like my Delhi6 app do that instead.

Was it helpful?

Solution

I use the following

const
  SM_REMOTESESSION = $1000;
if GetSystemMetrics(SM_REMOTESESSION) <> 0 then
begin
  // you are in a remote session
end

Per the MSDN page for GetSystemMetrics:

SM_REMOTESESSION = 0x1000
This system metric is used in a Terminal Services environment. If the calling process is associated with a Terminal Services client session, the return value is nonzero. If the calling process is associated with the Terminal Services console session, the return value is 0. Windows Server 2003 and Windows XP: The console session is not necessarily the physical console. For more information, see WTSGetActiveConsoleSessionId.

I am using this in Delphi 2007 and the function is defined in the Windows unit, but I did need to define the constant myself. I don't know if Delphi 6 has the function defined. The Minimum supported windows version was Windows 2000 so you should be able to use it unless you are going way back.

--

To find out the current state of the session you need the WTSQuerySessionInformation function. You can use this function to find out a lot of information on the current session including the current state.

I found an entry in the Embarcadero Discussion Forums that gave me the starting code. That post was called remote desktop question.

Here are some constants and function prototypes you will need:

const
  WTS_CURRENT_SERVER_HANDLE: THandle = 0;
  WTS_CURRENT_SESSION: DWORD = DWORD(-1);


type
  WTS_INFO_CLASS = (
    WTSInitialProgram,
    WTSApplicationName,
    WTSWorkingDirectory,
    WTSOEMId,
    WTSSessionId,
    WTSUserName,
    WTSWinStationName,
    WTSDomainName,
    WTSConnectState,
    WTSClientBuildNumber,
    WTSClientName,
    WTSClientDirectory,
    WTSClientProductId,
    WTSClientHardwareId,
    WTSClientAddress,
    WTSClientDisplay,
    WTSClientProtocolType,
    WTSIdleTime,
    WTSLogonTime,
    WTSIncomingBytes,
    WTSOutgoingBytes,
    WTSIncomingFrames,
    WTSOutgoingFrames,
    WTSClientInfo,
    WTSSessionInfo,
    WTSSessionInfoEx,
    WTSConfigInfo,
    WTSValidationInfo,
    WTSSessionAddressV4,
    WTSIsRemoteSession
  );

 WTS_CONNECTSTATE_CLASS = (
    WTSActive,              // User logged on to WinStation
    WTSConnected,           // WinStation connected to client
    WTSConnectQuery,        // In the process of connecting to client
    WTSShadow,              // Shadowing another WinStation
    WTSDisconnected,        // WinStation logged on without client
    WTSIdle,                // Waiting for client to connect
    WTSListen,              // WinStation is listening for connection
    WTSReset,               // WinStation is being reset
    WTSDown,                // WinStation is down due to error
    WTSInit);               // WinStation in initialization

  TWTSQuerySessionInformationFunction = function(hServer: THandle; SessionId:
                DWORD; WTSInfoClass: WTS_INFO_CLASS; var ppBuffer: Pointer; var pBytesReturned: DWORD): BOOL; stdcall;
  TWTSFreeMemoryProcedure = procedure(pMemory: Pointer); stdcall;

And here is the code in use. I put this in a timer and output the state to a list box. I could disconnect and then reconnect and see the state change in the list box.

There are different ways to handle the load library and function mapping call. Probably should not load library on every call if you end up polling like this. I just used the example I found.

function TForm3.GetTSClientState: WTS_CONNECTSTATE_CLASS;
var
  LibHandle: HMODULE;
  WTSQuerySessionInformation: TWTSQuerySessionInformationFunction;
  WTSFreeMemory: TWTSFreeMemoryProcedure;
  ClientState: Pointer;
  cBytesReturned: DWORD;
begin

  LibHandle := LoadLibrary('wtsapi32.dll');
  if LibHandle &lt;&gt; 0 then
  begin
    try
      @WTSQuerySessionInformation := GetProcAddress(LibHandle, 'WTSQuerySessionInformationA');
      @WTSFreeMemory := GetProcAddress(LibHandle, 'WTSFreeMemory');
      if Assigned(WTSQuerySessionInformation) and Assigned(WTSFreeMemory) 
      then
      begin
        if WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION,
                                      WTSConnectState, ClientState, cBytesReturned) then
        try
          result := WTS_CONNECTSTATE_CLASS(ClientState^);
        finally
          WTSFreeMemory(ClientState);
        end;

      end;
    finally
      FreeLibrary(LibHandle);
    end;
  end;
end;

procedure TForm3.Timer1Timer(Sender: TObject);
var
  State: WTS_CONNECTSTATE_CLASS;
begin
   ListBox1.AddItem(GetTSClientName, nil);

   State := GetTSClientState;

   case State of
     WTSActive: ListBox1.AddItem('WTSActive', nil);
     WTSConnected: ListBox1.AddItem('WTSConnected', nil);
     WTSConnectQuery: ListBox1.AddItem('WTSConnectQuery', nil);
     WTSShadow: ListBox1.AddItem('WTSShadow', nil);
     WTSDisconnected: ListBox1.AddItem('WTSDisconnected', nil);
     WTSIdle: ListBox1.AddItem('WTSIdle', nil);
     WTSListen: ListBox1.AddItem('WTSListen', nil);
     WTSReset: ListBox1.AddItem('WTSReset', nil);
     WTSDown: ListBox1.AddItem('WTSDown', nil);
     WTSInit: ListBox1.AddItem('WTSInit', nil);
   end;
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top