
介绍:
这里描述的是一个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方法扑捉。它有一