【问题标题】:Delphi6: Need to know is my application running in console session or remote desktop sessionDelphi6:需要知道我的应用程序是在控制台会话还是远程桌面会话中运行
【发布时间】:2013-07-11 21:06:11
【问题描述】:

不时从远程桌面客户端访问我的应用程序。

我想知道它当前是在控制台会话还是远程桌面会话中使用。如果是后者并且会话已断开连接(用户已断开连接但未注销),它应该将自身重定向到控制台(就像 Windows XP 上的 tscon.exe 0 /dest:console 一样)。

我目前正在运行一个 shell 脚本来实现这一点(使用 query.exe usertscon.exe),但我希望我的德里 6 应用能够这样做。

【问题讨论】:

    标签: delphi delphi-6


    【解决方案1】:

    我用下面的

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

    根据GetSystemMetrics 的 MSDN 页面:

    SM_REMOTESESSION = 0x1000
    此系统指标用于终端服务环境。如果调用进程与终端服务客户端会话相关联,则返回值非零。如果调用进程与终端服务控制台会话相关联,则返回值为 0。 Windows Server 2003 和 Windows XP:控制台会话不一定是物理控制台。如需更多信息,请参阅WTSGetActiveConsoleSessionId

    我在 Delphi 2007 中使用它,函数在 Windows 单元中定义,但我确实需要自己定义常量。我不知道 Delphi 6 是否定义了函数。受支持的最低 Windows 版本是 Windows 2000,因此您应该能够使用它,除非您要回去。

    --

    要找出会话的当前状态,您需要WTSQuerySessionInformation 函数。您可以使用此功能查找有关当前会话的大量信息,包括当前状态。

    我在 Embarcadero 论坛中找到了一个条目,该条目为我提供了起始代码。那个帖子被称为remote desktop question

    以下是您需要的一些常量和函数原型:

    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 &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;
    

    【讨论】:

    • 是的,它可以工作,但关键是要知道此会话是“断开连接”(用户已关闭查看器窗口并且 UI 无处显示)还是“活动”(用户正在远程工作)。
    • 更新了答案以包含获取会话状态的方法。
    • 所以现在我知道了会话状态,唯一需要发现的是如何将会话重定向到控制台,就像“tscon.exe 0 /dest:console”一样。有什么想法吗?
    猜你喜欢
    • 1970-01-01
    • 2011-05-02
    • 1970-01-01
    • 2011-02-23
    • 2010-10-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-02
    相关资源
    最近更新 更多