2

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

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

我目前正在运行一个 shell 脚本来实现这一点(使用query.exe userand tscon.exe),但我希望我的 Delhi6 应用程序能做到这一点。

4

1 回答 1

4

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;
于 2013-07-11T21:24:50.573 回答