Delphi6: нужно знать, работает ли мое приложение в сеансе консоли или сеанса удаленного рабочего стола

Время от времени к моему приложению обращаются из клиентов удаленного рабочего стола, и я хочу знать, используется ли оно в данный момент в сеансе консоли или сеансе удаленного рабочего стола, если позже это происходит, и сеанс отключен (пользователь отключился, но не вышел из системы), оно должен перенаправить себя на консоль (как это делает "tscon.exe 0 /dest:console" в Windows XP). Теперь я запускаю сценарий оболочки для достижения этой цели (используя "query.exe user" и "tscon.exe"), но хотел бы, чтобы мое приложение Delhi6 сделало это вместо этого.

1 ответ

Решение

Я использую следующее

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

На странице MSDN для GetSystemMetrics:
SM_REMOTESESSION = 0x1000
Этот системный показатель используется в среде служб терминалов. Если вызывающий процесс связан с сеансом клиента служб терминалов, возвращаемое значение отлично от нуля. Если вызывающий процесс связан с сеансом консоли служб терминалов, возвращаемое значение равно 0. Windows Server 2003 и Windows XP: сеанс консоли не обязательно является физической консолью. Для получения дополнительной информации см. WTSGetActiveConsoleSessionId.

Я использую это в Delphi 2007, и функция определяется в модуле Windows, но мне нужно было определить константу самостоятельно. Я не знаю, имеет ли Delphi 6 определенную функцию. Минимальной поддерживаемой версией Windows была Windows 2000, так что вы должны иметь возможность использовать ее, если не вернетесь назад.

-

Чтобы узнать текущее состояние сеанса, вам нужна функция WTSQuerySessionInformation. Вы можете использовать эту функцию, чтобы узнать много информации о текущем сеансе, включая текущее состояние.

На дискуссионных форумах Embarcadero я нашел запись, которая дала мне стартовый код. Этот пост назывался вопросом удаленного рабочего стола.

Вот некоторые константы и прототипы функций, которые вам понадобятся:


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;

И вот код в использовании. Я поместил это в таймер и вывел состояние в список. Я мог бы отключиться, а затем снова подключиться и увидеть изменение состояния в списке.

Существуют разные способы обработки библиотеки загрузки и вызова отображения функций. Вероятно, не следует загружать библиотеку при каждом вызове, если вы закончите опрос таким образом. Я просто использовал пример, который нашел.


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

  LibHandle := LoadLibrary('wtsapi32.dll');
  if LibHandle <> 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;
Другие вопросы по тегам