Windows Mobile的远程控制

来源:互联网 发布:知乎 川腾空气净化器 编辑:程序博客网 时间:2024/05/17 03:45

程序代码



介绍:
这里描述的是一个Windows Mobile 的桌面远程控制程序。用这个程序,你可以通过键盘和鼠标远程控制你的Windows Mobile设备。
背景:
代码实在我以前的文章类曾经提到的:一个远程的Windows Mobile屏幕截取工具。除了调用RAPI,该软件可以通过一个RAPI服务流,允许桌面程序建立设备之间不间断的连接。当然,在该程序中。我放弃了GAPI和DirectDraw的屏幕截取方式,使用了一个简单的基于GDI屏幕截取方式。为了曾强通信能力,代码在连接两端使用了ZLIB的压缩库。
桌面代码:
请在解压该程序前建立一个CeRemoteClient的目录来保存桌面项目。
桌面程序代码的大部分包含在CeRemoteClientView.h中,事实上WTL 8.0框架子窗口实现了所有桌面客户端的功能。
设备的连接通过Connect() 和 Disconnect()方法实现。在菜单和工具条中可以使用该命令,这些都包含在主框架窗口类中(MainFrm.h)。当桌面成功的连接到设备,一个200微秒的时钟被建立,用来压缩屏幕位图。
屏幕设备是由GetScreen()方法控制的。它先关掉计时器,然后发送信息到服务设备请求当前屏幕图象:
KillTimer(SCREEN_TIMER);
hr = Write(RCM_GETSCREEN);


服务设备返回一个信息包含同样的信息代码,屏幕缓冲的压缩大小,然后解压流数据。在读取类三个字大小的数据后,代码判断压缩和解压缓冲的空间是否够用,然后读取压缩的数据流:

//读取压缩缓冲区
hr = m_pStream->Read(m_pZipBuf, cbZipBuf, &ulRead);

如果一切顺利,压缩缓冲将被解压,结果DIB是位图的大小。如果大小不一样则再来一次,当非常接近时,屏幕设备将被刷新,这样整个窗口被清空:
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();
}
在强制窗口更新后,计时器重新启动,这样我们就可以获得下一张屏幕图象。
发送输入:
发送键盘和鼠标的输入信息非常简单:处理相应的窗口信息,将其数据转化为INPUT结构,将其发送到服务端处理。以下是对WM_KEYDOWN的处理:
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;
}
MapKey()函数在桌面和键盘设备之间进行基本的安键索引。用F1键用来控制左,F2用来控制右。F3和F4自然的做为数字键。
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;
}
注意鼠标的屏幕坐标是可以被屏幕做参照的,SendInput API在服务器上使用的原因。
现在注意,让我们进一步看一下服务器设备的代码。
设备代码:
请建立设备项目的CeRemSrv路径。
大部分设备代码是CRemoteControl类中执行的。所有由桌面客户发送的信息在循环运行的方法中被处理。
设备屏幕可以被SendScreen方法扑捉。它有一个和桌面副本很相似的结构。看一下屏幕设备是怎么被方便的扑捉的:
hDC = GetWindowDC(NULL);

当获得了设备屏幕的HDC后,你可以非常容易的拷贝到一个位图文件中并和桌面联系。这里不需要在怀念 GAPI或DirectDraw技术:
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;
}

一个非常简单的方式。
感兴趣的几点:
这里有两件事比较有趣:这样模拟鼠标的双击事件,为什么代码不会在WM5和WM6设备上工作?
双击必须发送消息到设备。这样是因为窗口将合并四个鼠标事件(down - up - down - up)到一个单一的信息WM_LBUTTONDBLCLK中。如果你看了我的代码,你就回知道这是怎样失败的。
在WM5和WM6设备中,你可能必须用RAPI的连续性,以便服务设备DLL响应客户端。我一次写了一个简单的设备工具以解决着类复杂问题。你在这就能找到,拷贝EXE文件到设备上然后运行它。 

原创粉丝点击