Click here to Skip to main content
12,503,334 members (68,960 online)
Click here to Skip to main content
Add your own
alternative version

Stats

121.6K views
2.1K downloads
103 bookmarked
Posted

Windows Mobile Remote Controller

, 28 Mar 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
Control your Windows Mobile device from your desktop.

Introduction

This article describes the implementation of a Windows Mobile desktop remote controller. With this application, you will be able to remotely control your Windows Mobile device by using the mouse and keyboard.

Background

The code in this article builds on my previous article: A Remote Windows Mobile Screen Grabber. Instead of blocking RAPI calls, this application implements a streamed RAPI server that allows the desktop application to have a permanent connection to the device. Also, in this code, I have dropped the GAPI and DirectDraw screen grabbing techniques, and used a simpler GDI based screen grabbing technique. To improve communications performance, the code uses the ZLIB compression library on both ends.

Desktop code

Please refer to the CeRemoteClient directory on the distribution Zip file for the desktop project.

The bulk of the desktop code is on the CeRemoteClientView.h file. This is in fact a WTL 8.0 frame child window that implements all the desktop client features.

Device connection is performed via the public Connect() and Disconnect() methods. These are called by the menu and toolbar handlers in the frame window class implementation (MainFrm.h). When the desktop successfully connects to the device, a 200 millisecond timer is created to poll the device for the compressed screen bitmap.

The device screen is retrieved by the private GetScreen() method. It first kills the timer, and sends a message to the device server requesting the current screen:

KillTimer(SCREEN_TIMER);
hr = Write(RCM_GETSCREEN);

The device server returns a message containing the same message code, the compressed size of the screen buffer, its expanded size, and the compressed byte stream. After reading the three first DWORDs, the code makes sure there is enough room on both the compressed and expanded buffers, and then reads the compressed byte stream:

// Read the compressed buffer
hr = m_pStream->Read(m_pZipBuf, cbZipBuf, &ulRead);

If all is well, the compressed buffer is decompressed and the resulting DIB is queried for the bitmap dimensions. If the dimensions are different than the last time, then it is very likely that the device screen was rotated, so the whole window is invalidated to erase any garbage:

zr = uncompress(m_pScrBuf, &nDestLen, m_pZipBuf, cbZipBuf);
if(zr == Z_OK)
{
    DIBINFO* pDibInfo = (DIBINFO*)m_pScrBuf;
    BYTE*    pBmpData = (BYTE*)(m_pScrBuf + sizeof(DIBINFO));
    BOOL     bErase   = FALSE;

    if(m_xDevScr != pDibInfo->bmiHeader.biWidth || 
       m_yDevScr != pDibInfo->bmiHeader.biHeight)
    {
        m_xDevScr = pDibInfo->bmiHeader.biWidth;
        m_yDevScr = pDibInfo->bmiHeader.biHeight;

        SetScrollSize(m_xDevScr, m_yDevScr);

        bErase = TRUE;
    }

    m_dib.SetBitmap((BITMAPINFO*)pDibInfo, pBmpData);

    InvalidateRect(NULL, bErase);
    UpdateWindow();
}

After forcing the window to update, the timer is restarted so we can get the next screen.

Sending input

Sending keyboard and mouse input to the device is pretty simple: handle the corresponding window messages, convert their data content to INPUT structures, and send them to the server for processing. Here's the WM_KEYDOWN handler:

LRESULT OnKeyDown(TCHAR vk, UINT cRepeat, UINT flags)
{
    HRESULT hr;
    INPUT   input;

    if(!m_bConnected)
        return 0;

    input.type           = INPUT_KEYBOARD;
    input.ki.wVk         = MapKey(vk);
    input.ki.wScan       = 0;
    input.ki.dwFlags     = 0;
    input.ki.time        = 0;
    input.ki.dwExtraInfo = 0;

    hr = Write(RCM_SETINPUT);
    if(SUCCEEDED(hr))
    {
        hr = Write(&input, sizeof(input));
        if(SUCCEEDED(hr))
            GetScreen();
    }

    return 0;
}

The MapKey() function performs basic key mappings between the desktop and the device keyboards. Use the F1 key for the left function button, and F2 for the right. The F3 and F4 keys naturally map to the phone keys.

Sending mouse actions is similar:

LRESULT OnLButtonDown(UINT Flags, CPoint pt)
{
    HRESULT    hr;
    INPUT    input;

    if(!m_bConnected)
        return 0;

    m_bLeftBtn           = true;
    input.type           = INPUT_MOUSE;
    input.mi.dwFlags     = MOUSEEVENTF_ABSOLUTE | 
                           MOUSEEVENTF_LEFTDOWN;
    input.mi.dx          = pt.x * 65536 / m_xDevScr;
    input.mi.dy          = pt.y * 65536 / m_yDevScr;
    input.mi.mouseData   = 0;
    input.mi.time        = 0;
    input.mi.dwExtraInfo = 0;

    hr = Write(RCM_SETINPUT);
    if(SUCCEEDED(hr))
    {
        hr = Write(&input, sizeof(input));
        if(SUCCEEDED(hr))
            hr = GetScreen();
    }

    return 0;
}

Note how the mouse screen coordinates are normalized for the device screen. This is a requirement of the SendInput API used on the device server.

Now that I mentioned it, let's take a closer look at the device server code.

Device code

Please refer to the CeRemSrv directory on the distribution Zip file for the device project.

The bulk of the device code is implemented in the CRemoteControl class. All messages sent by the desktop client are processed and dispatched on the executive loop implemented in the Run method.

The device screen is captured by the SendScreen method which has a very similar structure to its desktop counterpart. Note how the device screen is captured so easily:

hDC = GetWindowDC(NULL);

After getting the HDC of the device screen, you can very easily copy it into a bitmap and serialize it to the desktop. There's no need for fancy GAPI or DirectDraw techniques like I used before.

After getting the device screen copied into a DIB, the whole thing is compressed and sent back to the desktop client:

memcpy(m_pScrBuf + i, m_dib.GetBitmapInfo(), sizeof(DIBINFO));
i += sizeof(DIBINFO);

memcpy(m_pScrBuf + i, m_dib.GetDIBits(), m_dib.GetImageSize());
i += m_dib.GetImageSize();

ULONG len = m_cbZipBuf;
int   zr  = compress(m_pZipBuf, &len, m_pScrBuf, cbNew);

if(zr != Z_OK)
    len = 0;

hr = m_pStream->Write(&dwMsg,    sizeof(DWORD), &ulWritten);
hr = m_pStream->Write(&len,      sizeof(ULONG), &ulWritten);
hr = m_pStream->Write(&cbNew,    sizeof(DWORD), &ulWritten);
hr = m_pStream->Write(m_pZipBuf, len,           &ulWritten);

Handling input from the desktop is even simpler:

HRESULT CRemoteControl::GetInput()
{
    INPUT   input;
    HRESULT hr;
    DWORD   dwRead;

    hr = m_pStream->Read(&input, sizeof(input), &dwRead);
    if(FAILED(hr))
        return hr;

    if(dwRead != sizeof(input))
        return E_FAIL;

    SendInput(1, &input, sizeof(input));

    return S_OK;
}

A very simple implementation indeed.

Points of interest

There are two interesting things you may like to know: how I simulated the double-click mouse event, and why this code will not work out of the box on WM5 and WM6 devices.

Double-clicks had to be simulated by sending four messages to the device. This happens because the desktop window manager will merge the four mouse events (down - up - down - up) into a single message - WM_LBUTTONDBLCLK. If you look at my code, you will see how this was undone...

On WM5 and WM6 devices, you may have to enable RAPI connectivity in order for the device server DLL to respond to the client. I once wrote a simple device tool to help you with this nasty chore. You can get it here. Copy the EXE to the device and execute it.

History

  • 2008-03-28 - Corrected flickering.
  • 2008-03-17 - First release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

João Paulo Figueira
Software Developer (Senior) Frotcom International
Portugal Portugal
I work on R&D for Frotcom International, a company that develops web-based fleet management solutions.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionBom Trabalho - nice work Pin
twisterat5730-Jun-15 23:50
membertwisterat5730-Jun-15 23:50 
AnswerRe: Bom Trabalho - nice work Pin
João Paulo Figueira1-Jul-15 0:15
professionalJoão Paulo Figueira1-Jul-15 0:15 
QuestionMy vote of 5 Pin
altinoga19-Oct-14 23:20
memberaltinoga19-Oct-14 23:20 
Questionhow to install server on wince? Pin
aliahmadi13671-Sep-14 22:24
memberaliahmadi13671-Sep-14 22:24 
GeneralMy vote of 5 Pin
Paul Heil25-May-11 6:10
memberPaul Heil25-May-11 6:10 
GeneralMy vote of 3 Pin
maq_rohit28-Feb-11 20:14
membermaq_rohit28-Feb-11 20:14 
GeneralGreat Work Sir ! ! Pin
g2gayan14-Jun-10 4:00
memberg2gayan14-Jun-10 4:00 
QuestionC# with sockets, possible? through internet? Pin
LucianoTres19-Jan-10 5:08
memberLucianoTres19-Jan-10 5:08 
GeneralWindows CE .Net 4.20 & 5.0 Pin
jregino13-Jan-10 23:31
memberjregino13-Jan-10 23:31 
GeneralRe: Windows CE .Net 4.20 & 5.0 Pin
Joao Paulo Figueira14-Jan-10 0:10
memberJoao Paulo Figueira14-Jan-10 0:10 
GeneralCannot compile server for WM6 Pin
Gianluca Monaco16-Sep-09 0:42
memberGianluca Monaco16-Sep-09 0:42 
GeneralRe: Cannot compile server for WM6 Pin
Gianluca Monaco16-Sep-09 14:09
memberGianluca Monaco16-Sep-09 14:09 
GeneralMy vote of 1 Pin
khansameer5-Dec-08 22:54
memberkhansameer5-Dec-08 22:54 
GeneralRe: My vote of 1 Pin
Joel Ivory Johnson7-Dec-08 4:35
memberJoel Ivory Johnson7-Dec-08 4:35 
GeneralRe: My vote of 1 Pin
Joao Paulo Figueira7-Dec-08 5:54
memberJoao Paulo Figueira7-Dec-08 5:54 
QuestionAbout CeRemSrv.dll Pin
andy zheng13-Nov-08 18:21
memberandy zheng13-Nov-08 18:21 
AnswerRe: About CeRemSrv.dll [modified] Pin
Gianluca Monaco16-Sep-09 14:14
memberGianluca Monaco16-Sep-09 14:14 
GeneralZLIB Pin
amnesty222-Sep-08 8:23
memberamnesty222-Sep-08 8:23 
GeneralRe: ZLIB Pin
Joao Paulo Figueira22-Sep-08 10:54
memberJoao Paulo Figueira22-Sep-08 10:54 
GeneralRe: ZLIB Pin
amnesty222-Sep-08 23:08
memberamnesty222-Sep-08 23:08 
QuestionAwesome Pin
Member 108861212-Sep-08 22:14
memberMember 108861212-Sep-08 22:14 
AnswerRe: Awesome Pin
Joao Paulo Figueira15-Sep-08 6:05
memberJoao Paulo Figueira15-Sep-08 6:05 
QuestionRe: Awesome Pin
Member 108861226-Sep-08 0:03
memberMember 108861226-Sep-08 0:03 
AnswerRe: Awesome Pin
Joao Paulo Figueira26-Sep-08 1:13
memberJoao Paulo Figueira26-Sep-08 1:13 
GeneralRe: Awesome Pin
Noisey1-Dec-09 13:14
memberNoisey1-Dec-09 13:14 
GeneralRe: Awesome Pin
Joao Paulo Figueira2-Dec-09 0:22
memberJoao Paulo Figueira2-Dec-09 0:22 
GeneralRe: Awesome Pin
Member 905997418-Nov-14 22:17
memberMember 905997418-Nov-14 22:17 
Questiondoes it have to be c++, what about c# Pin
Thanks for all the fish28-Mar-08 8:18
memberThanks for all the fish28-Mar-08 8:18 
GeneralRe: does it have to be c++, what about c# Pin
Joao Paulo Figueira28-Mar-08 8:26
memberJoao Paulo Figueira28-Mar-08 8:26 
GeneralArticle updated Pin
Joao Paulo Figueira28-Mar-08 0:48
memberJoao Paulo Figueira28-Mar-08 0:48 
Generalflickering isssues [modified] Pin
Vincent_RICHOMME27-Mar-08 23:21
memberVincent_RICHOMME27-Mar-08 23:21 
GeneralRe: flickering isssues Pin
Joao Paulo Figueira28-Mar-08 0:19
memberJoao Paulo Figueira28-Mar-08 0:19 
GeneralRe: flickering isssues Pin
Joao Paulo Figueira28-Mar-08 1:08
memberJoao Paulo Figueira28-Mar-08 1:08 
GeneralRe: flickering isssues Pin
Vincent_RICHOMME28-Mar-08 1:26
memberVincent_RICHOMME28-Mar-08 1:26 
GeneralMuito Bom! Pin
adauto25-Mar-08 5:04
memberadauto25-Mar-08 5:04 
GeneralRe: Muito Bom! Pin
Joao Paulo Figueira28-Mar-08 0:21
memberJoao Paulo Figueira28-Mar-08 0:21 
GeneralRAPI invoke error Pin
tigerharry17-Mar-08 23:56
membertigerharry17-Mar-08 23:56 
GeneralRe: RAPI invoke error Pin
Joao Paulo Figueira18-Mar-08 0:02
memberJoao Paulo Figueira18-Mar-08 0:02 
GeneralRe: RAPI invoke error Pin
tigerharry18-Mar-08 0:19
membertigerharry18-Mar-08 0:19 
GeneralRe: RAPI invoke error Pin
Joao Paulo Figueira18-Mar-08 1:09
memberJoao Paulo Figueira18-Mar-08 1:09 
GeneralRe: RAPI invoke error Pin
tigerharry24-Mar-08 21:21
membertigerharry24-Mar-08 21:21 
GeneralNote about ZLIB Pin
Joao Paulo Figueira17-Mar-08 23:44
memberJoao Paulo Figueira17-Mar-08 23:44 
GeneralCan't download Pin
yulincat17-Mar-08 23:21
memberyulincat17-Mar-08 23:21 
GeneralRe: Can't download Pin
Joao Paulo Figueira17-Mar-08 23:42
memberJoao Paulo Figueira17-Mar-08 23:42 
GeneralRe: Can't download Pin
yulincat18-Mar-08 0:09
memberyulincat18-Mar-08 0:09 
GeneralRe: Can't download Pin
Joao Paulo Figueira18-Mar-08 0:29
memberJoao Paulo Figueira18-Mar-08 0:29 
GeneralRe: Can't download Pin
hemaoshun21-Mar-08 15:38
memberhemaoshun21-Mar-08 15:38 
GeneralRe: Can't download Pin
Joao Paulo Figueira22-Mar-08 2:55
memberJoao Paulo Figueira22-Mar-08 2:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160919.1 | Last Updated 28 Mar 2008
Article Copyright 2008 by João Paulo Figueira
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid