Remote Desktop Mobile (RDP Client) disconnects after 10 minutes of inactivity

[UPDATE 10/9/2009: Source-Code and CAB are now available at https://mobilerdpnotimeout.codeplex.com/ ]
[UPDATE 9/2/2010: v2 of the sample app is available at https://mobilerdpnotimeout.codeplex.com/ : it takes care of the battery-life impacted by application continuing to emulate mouse-move even after user closed RDP session – see details below]

  • Error Message: “The remote session was ended because the idle timeout limit was reached. This limit is set by the server administrator or by network policy.”
  • RDP\TSC Clients and Windows Mobile OEMs
  • What’s new in Windows Mobile 6.5 about Remote Desktop Mobile

This issue should be somehow well known by the Community, but I couldn’t easily find a solution so I had to think one by myself… :-) After 10 minutes of inactivity of a Terminal Server session on Windows Mobile devices, the Remote Desktop Mobile application needs you to logon again stating that a timeout “set by the server administrator or by network policy” occurred. Unfortunately it’s not really a good error message, since many times the server is correctly set and the problem is just client-side... Anyway, probably it was not very frustrating since in the most of the cases one is logged via RDP only to actively work on the server. But this is not always true, so I’d guess either I wasn’t able to find someone with a possible solution, either simply people accepted to live with it…

Remote Desktop Mobile on WM6.x (and, before this, the TSC client on WM5) is a component that OEMs are not required to include to have their OSs pass the Windows Mobile Logo Test Kit, and this is basically why you may find devices with it in-ROM and others where you need to find alternative solutions. In any case, regarding the inactivity timeout issue, luckily Windows Mobile 6.5 _should_ now have an updated version of the Remote Desktop Mobile application that OEMs can include in their images, easier to use on the field as it allows, for example, to finally customize an idle timeout!! Unfortunately it’s not included in the emulators coming with the Windows Mobile 6.5 Developer Tool Kit.

Back to the problem… after some researches I understood that the timer is reset only when the Remote Desktop Mobile’s application window receives mouse or keyboard input. Refreshing the window, or sending exotic WM_xx messages wasn’t enough. So, I came to thinking what was a possible keypress or mouseevent that can be programmatically simulated (through keybd_event() or mouse_event()) that wouldn’t impact application at all?? After some tests I’ve realized that MOUSEEVENTF_MOVE, which is the event of user “moving the mouse”, has no effect on current Windows Mobile-based devices: that event is maintained as codebase with Windows CE, where an OEM may also include a non-touch screen where you really have a mouse to move... but its only effect on Windows Mobile device is for example to modify display’s brightness, in case the display entered a sort of power-safe state (well, obviously the mouse-pointer is moved on the remote server)

So, the idea was to develop a Win32 Console application that simply launches the Remote Desktop Mobile application and every X minutes (5? 9?) simulates a mouse-movement of some pixels and after a short time another one to place the mouse on the previous position.

This is NOT elegant code at all, absolutely, but it does what was meant to run a specific task… Nerd

Obviously this watch-dog application would “waste” one of the 30 process slots… is it acceptable? As you can see the application doesn’t do anything special or unsupported: it’s simply a monitor-console application with no particularly elegant code. As usual, I would strongly encourage on adding some error-checking!!

 
int _tmain(int argc, _TCHAR* argv[])
{
    int const FIVEMINUTES = 1000*60*5;
    HWND hWndRDM = NULL;
    
    //Firstly launch RDP Client
    SHELLEXECUTEINFO sei = {0};
    sei.cbSize = sizeof(sei);
    sei.nShow = SW_SHOWNORMAL; 
    sei.lpFile = TEXT("\\Windows\\wpctsc.exe");
    sei.lpParameters = TEXT(" ");
    if (!ShellExecuteEx(&sei))
    {
        MessageBox(NULL, TEXT("Couldn't launch RDP Client"), TEXT("Remote Desktop Launcher"), MB_OK);
        goto Exit;
    }


    //Now every X minutes check if it's still running and if so "refresh" its window
    //if it's no longer running, exit
    do 
    {
        //check if RDP Client is running, otherwise exit
        hWndRDM = FindWindow(_T("TSSHELLWND"), NULL);
        if (NULL != hWndRDM)
        {
            ////Get foreground window -- this is not needed if RDM is launched Full-Screen as it was in this case
            //hWndForeground = GetForegroundWindow();
            //Sleep(500);

            //Give focus to the RDP Client window (even if the logon screen, in case user logged out in the meantime)
            SetForegroundWindow(hWndRDM);

            //The timer is reset when the rdp window receives mouse or keyboard input
            //with no MOUSEEVENTF_ABSOLUTE the move is relative

            mouse_event(MOUSEEVENTF_MOVE, 100, 0, 0, 0);
            Sleep(250);
            mouse_event(MOUSEEVENTF_MOVE, -100, 0, 0, 0);

            ////Give focus back to previous foreground window
            //SetForegroundWindow(hWndForeground);

            //Sleep 
            Sleep(FIVEMINUTES);
        }
        else 
        {
            //RDP Client is not running - let's exit
            goto Exit;
        }
    }
    while (TRUE); //The check (NULL != hWndRDM) is already done inside the loop


Exit:
    if (NULL != hWndRDM)
        CloseHandle(hWndRDM);
    
    return 0;
}
  

I can imagine that this problem affects more the WinMo Users rather than the WinMo “Devs”, therefore I had in mind to distribute directly the CAB of the application, if anyone else doesn’t want to do it for the Community. I’m wondering if CodePlex would be a good place, I’ll see and update this post. In any case, remember that it’s up to the OEMs to decide if including RDM (\Windows\wpctsc.exe) in their OSs based on Windows Mobile platform!

Cheers!

~raffaele

 

EDIT 9/2/2010:

After interacting with some users of the sample application and tested a v2 build on some scenarios, I’d like to share it now – note that the CodePlex project page will continue containing both versions. The issue was that MyRDM continued to emulate mouse-move even after user closed session, and this impacted on battery-life: this was not a bug, but simply a well known limitation of the tiny sample application… so we wanted a code that acted on the following possible conditions after every sleep-interval:

  • Wpstsc is no longer running (user “minimized” and OS closed it) –> in this case, MyRDM.exe exits
  • Wpstsc is running AND:
    • The “remote session” is foreground –> then MyRDM.exe emulates mouse-move and goes back to sleep
    • The “remote session” is not foreground for one of the possible causes:
      • User “minimized” wpstsc –> then MyRDM.exe kills wpstsc and exits
      • User logged off, and the foreground window is the RDM Logon window –> then MyRDM.exe does nothing and simply goes back to sleep

So the main portion of the code is the following (the same disclaimers apply as before):

         do 
        {
                //Sleep 
                Sleep(FIVEMINUTES);

                //check if RDP Client is still running, otherwise exit
                hWndRDM = FindWindow(_T("TSSHELLWND"), NULL);
                if (NULL != hWndRDM)
                {
                        hWndForeground = GetForegroundWindow();
                        if (hWndRDM == hWndForeground)
                        {
                                //The timer is reset when the rdp window receives mouse or keyboard input
                                //with no MOUSEEVENTF_ABSOLUTE the move is relative
                                mouse_event(MOUSEEVENTF_MOVE, 100, 0, 0, 0);
                                Sleep(250);
                                mouse_event(MOUSEEVENTF_MOVE, -100, 0, 0, 0);
                        }
                        else //this means that RDM main windows is no longer foreground, however it may be that the RDP logon page is active
                        {
                                pid = NULL;
                                threadId = GetWindowThreadProcessId(hWndForeground, &pid);
                                //only if the foreground window is not the RDP logon page, then kill wpctsc.exe and exit
                                if (pid != GetProcessIdByName(TEXT("wpctsc.exe")))
                                {
                                        KillProcessByName(TEXT("wpstsc.exe"));
                                        goto Exit;
                                }
                        }
                }
                else //hWndRDM == NULL: RDP Client is not running - let's exit
                {                       
                        goto Exit;
                }
        }
        while (TRUE);

 

HTH, ciao!

~raffaele