1

I'm currently writing a Windows service, which also does something when a user logs on. There was the idea to do nothing if the logon comes from a remote computer (e.g. Remote desktop), and tried to find a way to dermine this. But following does not work - it always returns true (Windows 10 64 bit V1809) - is there something I doing wrong here?

DWORD SvcHandlerEx(DWORD controlCode, DWORD eventType, ... )
{
    ...

    switch(controlCode)
    {
        case SERVICE_CONTROL_SESSIONCHANGE:
        {
            WTSSESSION_NOTIFICATION *pSessInfo = (WTSSESSION_NOTIFICATION *)pEvtData;
            // invoke SessionChangeHandler(eventId, pSessInfo->dwSessionId)
        }

        ...
    }

    ...
}

...

VOID SessionChangeHandler(DWORD reason, DWORD sessionId)
{
    LPWSTR *pSessionInfo = nullptr;
    DWORD dataLen = 0;
    BOOL isRDP = false;

    if (!WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, 
                                     WTSIsRemoteSession, &pSessionInfo, &dataLen))
    {
        // Do some error handling...
        return;
    }

    if (dataLen)
    {
        if (dataLen)
        {
            isRDP = (bool)pSessionInfo;    // Always 1 (TRUE) !!!
        }

        WTSFreeMemory(pSessionInfo);
    }

    ...

}
Willy K.
  • 397
  • 2
  • 14
  • I also tried with WTSClientProtocolType as stated in https://stackoverflow.com/questions/33001915/how-to-determine-whether-c-sharp-app-running-under-citrix - but in this case I do not get "2" (RDP protocol) when connecting from a remote computer...strange. – Willy K. Jul 11 '19 at 13:54

1 Answers1

5

Per the documentation for WTSIsRemoteSession:

WTSIsRemoteSession

Determines whether the current session is a remote session.

The WTSQuerySessionInformation function returns a value of TRUE to indicate that the current session is a remote session, and FALSE to indicate that the current session is a local session. This value can only be used for the local machine, so the hServer parameter of the WTSQuerySessionInformation function must contain WTS_CURRENT_SERVER_HANDLE.

Windows Server 2008 and Windows Vista:  This value is not supported.

This implies that the return value of WTSQuerySessionInformation() holds the value you are looking for, and whatever memory the function may allocate, if any, is secondary and should be ignored when querying WTSIsRemoteSession, eg:

VOID SessionChangeHandler(DWORD reason, DWORD sessionId)
{
    LPWSTR *pSessionInfo = nullptr;
    DWORD dataLen = 0;

    bool isRDP = WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSIsRemoteSession, &pSessionInfo, &dataLen);
    if ((!isRDP) && (GetLastError() != 0))
    {
        // Do some error handling...
        return;
    }

    if (pSessionInfo)
        WTSFreeMemory(pSessionInfo);

    // use isRDP as needed...
    ...
}

However, if you find that isRDP is always true in this case, then the documentation is misleading and you should check the contents of the pSessionInfo buffer instead. You are setting your isRDP variable based on whether WTSQuerySessionInformation() allocates any memory at all, you are not looking at what is actually inside the data.

For instance, assuming dataLen is being set to sizeof(BOOL) on output then cast your pSessionInfo pointer to a BOOL* pointer and dereference it, eg:

VOID SessionChangeHandler(DWORD reason, DWORD sessionId)
{
    LPWSTR *pSessionInfo = nullptr;
    DWORD dataLen = 0;

    if (!WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSIsRemoteSession, &pSessionInfo, &dataLen))
    {
        // Do some error handling...
        return;
    }

    bool isRDP = * (BOOL*) pSessionInfo;
    WTSFreeMemory(pSessionInfo);

    // use isRDP as needed...
    ...
}

Alternatively:

VOID SessionChangeHandler(DWORD reason, DWORD sessionId)
{
    BOOL *isRDP = nullptr;
    DWORD dataLen = 0;

    if (!WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSIsRemoteSession, (LPWSTR*)&isRDP, &dataLen))
    {
        // Do some error handling...
        return;
    }

    // use *isRDP as needed...

    WTSFreeMemory(isRDP);
    ...
}

UPDATE:

On the other hand, if dataLen is instead being set to sizeof(UCHAR) on output then cast your pSessionInfo pointer to a UCHAR* pointer and dereference it, eg:

VOID SessionChangeHandler(DWORD reason, DWORD sessionId)
{
    LPWSTR *pSessionInfo = nullptr;
    DWORD dataLen = 0;

    if (!WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSIsRemoteSession, &pSessionInfo, &dataLen))
    {
        // Do some error handling...
        return;
    }

    bool isRDP = * (UCHAR*) pSessionInfo;
    WTSFreeMemory(pSessionInfo);

    // use isRDP as needed...
    ...
}

Alternatively:

VOID SessionChangeHandler(DWORD reason, DWORD sessionId)
{
    UCHAR *isRDP = nullptr;
    DWORD dataLen = 0;

    if (!WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSIsRemoteSession, (LPWSTR*)&isRDP, &dataLen))
    {
        // Do some error handling...
        return;
    }

    // use *isRDP as needed...

    WTSFreeMemory(isRDP);
    ...
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • It seems that I misunderstood the documentation (or worded badly - "The return value of WTSQuerySessionInformationW returns ..." might be more clear). However, I tried as you suggested: GetLastError() returns 0 (was called immediately after WTSQuerySessionInformationW), but "isRdp" is always "TRUE". To be sure, I rebooted and checked this without connecting via remote desktop - "isRdp" is "TRUE". I also tried to catch errors via "if (GetLastError() != 0)" - nothing changes. Are there other ways to find out if a remote desktop connection was established? – Willy K. Jul 12 '19 at 15:15
  • Have much thanks - you helped me a lot. I got it: My usage of WTSQuerySessionInformationW() is correct, but I did not derefernce the data properly (I did "isRdp = (bool)pSessionInfo" instead of "isRdp =*(bool *)pSessionInfo"). A little mistake - big impact.... – Willy K. Jul 13 '19 at 13:36
  • @WillyK. Note that Microsoft does not use `bool` (lowercase) in its APIs, it (typically) uses `BOOL` (uppercase) instead, for compatibility with C. See [BOOL vs. VARIANT_BOOL vs. BOOLEAN vs. bool](https://devblogs.microsoft.com/oldnewthing/20041222-00/?p=36923): "*Newest on the scene is `bool`, which is a C++ data type that has the value `true` or `false`. **You won’t see this used much (if at all) in Win32** because Win32 tries to remain C-compatible.*" – Remy Lebeau Jul 13 '19 at 15:52
  • In my own testing (on Windows 10 Pro 22H2), I find the value of the buffer is correct – if I have a remote RDP session 2, plus the standard 0=Services and 1=Console, then I find the buffer is 0x00 for sessions 0 and 1, but 0x01 for session 2. The statement in the docs that implies you look at the return value not the buffer is just wrong/misleading, so you could improve your answer by deleting your example code which makes that false assumption. – Simon Kissane Mar 04 '23 at 23:57
  • @SimonKissane What you describe is already covered by the 2nd half of my answer: "*However, if you find that `isRDP` is always true in this case, then the documentation is misleading and you should check the contents of the `pSessionInfo` buffer instead.*" – Remy Lebeau Mar 05 '23 at 01:55
  • @RemyLebeau, Yes, but my point is your answer is worded "If A then B, if not-A then C". Since A is false, the whole "If A then B, if not-A then..." bit is unnecessary and just confusing people. – Simon Kissane Mar 05 '23 at 04:46
  • @SimonKissane I have no idea what you are talking about. – Remy Lebeau Mar 05 '23 at 06:23
  • @RemyLebeau Well, consider your first block of example code – it is just wrong, it doesn't work. That might not be clear from the documentation, but if you try it you will see – whereas your second and third code block is correct. So your answer would be briefer and easier to understand if you deleted that first code block. – Simon Kissane Mar 05 '23 at 08:40
  • In fact datalLen == 1 so you have to cast pSessionInfo into a UCHAR* or bool* lowercase or it will almost always return true depending of the other bytes : bool isRDP = * (UCHAR*) pSessionInfo; – Nehluxhes Aug 21 '23 at 17:19