65.9K
CodeProject 正在变化。 阅读更多。
Home

移动触控板

2008年11月12日

CPOL

7分钟阅读

viewsIcon

94064

downloadIcon

1550

Mobile TouchPad 允许您通过触摸板手机控制您的 PC。您可以轻松连接到您的桌面,无需进行任何配置。

目录

什么是 Mobile TouchPad?

Mobile TouchPad 允许您通过触摸板手机控制您的 PC。您可以轻松连接到您的桌面,无需进行任何配置,您只需要激活驱动程序(也包含源代码)并启用标准的 ActiveSync。

您可以在演示文稿中使用它,或者控制您的媒体中心。

引言

我尝试解释为移动设备编写代码是多么简单。该项目包含两个解决方案,需要两个程序,因为一个程序会拦截移动设备上的鼠标移动,而第二个程序则与桌面进行交互。

我解决了一些关于

  • ActiveSync
  • OpenNETCF Mobile Ink
  • 鼠标钩子
  • Marshal
  • P/Invoke
  • Socket(套接字)
  • 字节序列化/反序列化
  • 注册表

信息:为了使以下代码示例更易于阅读,未包含安全检查和错误处理。

开发者要求

首先,要部署任何 WM 应用程序,您需要 Windows Mobile 6 Professional and Standard Software Development Kits Refresh[^]。

在这里,您可以找到您系统的所有要求,并且可以选择平台 Pro 或 Standard 或两者都选。您至少需要其中之一。我推荐您使用“Professional Windows Mobile 6 Professional SDK Refresh.msi”,这是 Pocket PC 的新 SDK。

请单独检查每个应用程序的系统要求。

总之,我添加了简要的系统要求

  • Microsoft Visual Studio 2005
  • Microsoft Visual Studio 2005 SP1 [^]
  • Microsoft .NET Compact Framework v2 SP2 [^]
  • 对于 Windows XP - ActiveSync 4.5 [^]
  • 对于 Windows Vista - Windows Mobile Device Center [^]

信息:为了部署此项目,我使用了 Visual Studio 2005 SE EU、Vista Business、Windows Mobile Device Center 和三星 i900。

其他要求

对于移动墨迹,我更喜欢使用免费的 Mobile Ink Library。OpenNETCF Mobile Ink 是一个提供 WM 6 Ink 支持的组件,可以在 此处 [^] 获取,并根据 MIT X11 许可证 [^] 发布。

我使用这个库,首先因为它免费且开源,而且非常易于使用,我将向您展示如何使用它。

使用移动端代码

移动解决方案名为 MobileTouchPad,您可以在 _WM_ 文件夹下找到它。移动应用程序将类似于下图。

OpenNETCF Mobile Ink

如上所示,我使用它是因为它可以为触摸提供一些酷炫的效果。目前,我编写了触摸的线条,并在释放屏幕后将其清除。使用此组件非常简单,在项目中使用的步骤是

添加 Using 和 Reference

using OpenNETCF.WindowsMobile.Ink;
using OpenNETCF.WindowsMobile.Ink.Interfaces;

安装下载的 OpenNETCF Mobile Ink 文件后,您可以在此处找到它(您也可以在安装目录中找到源代码)。

添加一个 Panel

在您的窗体上添加一个 Panel 或 PictureBox,它将成为您的触摸板,我将其命名为 panelTouchPad

添加 Overlay

在您的 Panel 上添加一个 overlay

IInkOverlay overlay;

添加 Load 事件处理程序

在加载窗体时,将 Panel 附加到 overlay

overlay.hWnd = pictureBox1.Handle;
overlay.Enabled = true;

添加 Event Stroke

这将启用 ink

overlay = Ink.CreateInstance();
overlay.SetEventInterest(InkCollectorEventInterest.Stroke, true);

调整屏幕

在 Resize 窗体中,检索屏幕坐标并调整 panelTouchPad 以适应屏幕。

Rectangle oScreen = Screen.PrimaryScreen.WorkingArea;
panelTouchPad.Location = new System.Drawing.Point(0, 0);
panelTouchPad.Size = new System.Drawing.Size
	(oScreen.Size.Width, oScreen.Size.Height - oScreen.Y);

清除屏幕

overlay.Enabled = false;
overlay.Ink = InkDisp.CreateInstance();
overlay.Enabled = true;

好了,第一步已经结束,现在我们有一个非常简单的移动画板。

鼠标移动

要简单地拦截鼠标移动,我使用 panelTouchPadMouseMoveEventHandler。在这里,我决定如何将数据发送到桌面。我每次都保存最后一个点,并且只发送一个步进移动到桌面。数据可以是 (+1,0) (-1,1) (0,-1) 等。

Point pointDirection = new Point();
pointDirection.X = e.X - LastPoint.X  ;
pointDirection.Y = e.Y - LastPoint.Y  ;

SendData(pointDirection);

LastPoint.X = e.X;
LastPoint.Y = e.Y; 

按钮按下和双击

要拦截按钮按下,您需要添加 KeyDownKeyPressKeyEventHandler,并以零移动发送。

private void FormTouchPad_KeyDown(object sender, KeyEventArgs e)
{
    m_buttonState = CommonStruct.MouseButtonState.ButtonPressed;     
    SendData(new Point(0,0));
}

private void FormTouchPad_KeyUp(object sender, KeyEventArgs e)
{
    m_buttonState = CommonStruct.MouseButtonState.None;
    SendData(new Point(0, 0));
}

要拦截双击,请为 panelTouchPad 添加 DoubleClick 事件,并以零移动发送。

m_buttonState = CommonStruct.MouseButtonState.DblClick;
SendData(new Point(0,0));

如您在此处所见,我为两个解决方案使用了通用的 CommonStruct 结构。

这是按钮状态的 enum

public enum MouseButtonState
{
    None = 0,
    ButtonClick,
    ButtonPressed,
    DblClick
}

这是按钮操作的 struct

public struct TouchPro
{
    public Point point;
    public MouseButtonState buttonState;
}

这个通用结构在 Common.cs 中。

桌面 IP

信息:要与桌面通信,您需要同步设备或模拟器。为此,您需要安装 ActiveSync 4.5 作为 Windows XP 的系统要求,以及 Windows Vista 的 Windows Mobile Device Center。

同步后,您只需将连接设置为 DMA 即可,使用此选项,您可以动态检索桌面 IP。

为了检索桌面 IP,我使用了一些由 ActiveSync 启用的注册表项。这些项存储在 [HKEY_LOCAL_MACHINE\Comm\Tcpip\Hosts\ppp_peer] 中,或者如果不存在,则存储在 [HKEY_LOCAL_MACHINE\Comm\Tcpip\Hosts\dtpt_peer] 中。如果您遇到问题,我建议您参考这个 博客 [^]。

使用模拟器

您可以使用模拟器来测试项目,但显然鼠标交互可能会有些困难。

注意:如果您使用模拟器,则必须激活基座。

在 Visual Studio 2005 中,打开 **Tools** 菜单并选择 **Device Emulator Manager**。在 Device emulator manager 中,选择您当前的模拟器并选择 **cradle**。

ActiveSync 启动后,您可能会注意到一个小的托盘图标,类似于网络图标,带有向左和向右旋转的加号。

序列化/反序列化

使用套接字,我需要将我的类转换为 byte[]。通常在桌面上,我使用 BinaryFormatterMemoryStream,但在 WM 中,该引用丢失了……不过,我找到了另一种方法,使用 Marshaling。这是我在我的两个项目(可以在 Common.cs 中找到)中使用的 CommonConvertion

class CommonConvertion
{
    public static byte[] StructureToByteArray(object obj)
    {
        int Length = Marshal.SizeOf(obj);
        byte[] bytearray = new byte[Length];
        IntPtr ptr = Marshal.AllocHGlobal(Length);
        Marshal.StructureToPtr(obj, ptr, false);
        Marshal.Copy(ptr, bytearray, 0, Length);
        Marshal.FreeHGlobal(ptr);
        return bytearray;
    }
    public static void ByteArrayToStructure(byte[] bytearray, ref object obj)
    {
        int Length = Marshal.SizeOf(obj);
        IntPtr ptr = Marshal.AllocHGlobal(Length);
        Marshal.Copy(bytearray, 0, ptr, Length);
        obj = Marshal.PtrToStructure(ptr, obj.GetType());
        Marshal.FreeHGlobal(ptr);
    }
}

连接并发送数据到桌面

为了将数据发送到桌面,我更倾向于使用 TCP/IP 而不是 Web 服务、Rapi 等,因为它更简单(我认为)。

TCP 用法

定义一个 TcpClient 和一个 NetworkStream,它们在应用程序生命周期内一直使用。

private TcpClient m_client;
private NetworkStream m_stream;

在构造函数中,添加到服务器的连接并打开到它的流。

m_client = new TcpClient(server, port);
m_stream = m_client.GetStream();

如果失败(桌面服务器已关闭),应用程序会在您每次触摸屏幕时尝试连接到服务器,并向您显示一个消息框……您可以继续不连接,或者继续在其上绘制……。

最后,我们按照前面在 MouseMove 中看到的发送数据。这里的代码显示了一个新方法 SendData。这是一个我管理数据并将其发送到桌面的方法。

private void SendData(Point pointDirection)
{
    ...
    CommonStruct.TouchPro AddMove = new CommonStruct.TouchPro();
    AddMove.point = pointDirection;
    AddMove.buttonState = m_buttonState;
    object objTmp = (object)AddMove;
    Byte[] data = CommonConvertion.StructureToByteArray(AddMove);
	
    m_networkstream.Write(data, 0, data.Length);
    ...
}

使用桌面端代码

解决方案名为 DriverMobileTouchPad,您可以在 _x86_ 文件夹下找到它。这是一个显示其界面的屏幕截图。在这里,您必须激活服务器以等待移动设备连接。

移动设备有多种限制,其中最重要的是屏幕分辨率低。为了避免这个问题,我添加了更改光标速度的可能性。

连接并接收来自移动设备的数据

TCP 用法

在窗体中,我创建了一个服务器并在某个端口上等待连接。

 m_tcpserver = new TcpListener(IPAddress.Any, 5000);
 m_tcpserver.Start();
 m_tcpserver.BeginAcceptTcpClient
	( new AsyncCallback(DoAcceptTcpClientCallback), m_tcpserver); 

警告:请记住在您的防火墙上打开端口 5000。

当它连接到客户端时,它开始在 DoAcceptTcpClientCallback 中从打开的流中读取 stream.Read,并将其转换为我的通用 struct。读取数据后,使用 CommonConvertion 进行反序列化,如这里显示的序列化/反序列化。

CommonStruct.TouchPro AddMove = new CommonStruct.TouchPro();
object objTmp = new object();
objTmp = (object)AddMove;
CommonConvertion.ByteArrayToStructure(bytes, iBufflen, ref objTmp);
AddMove = (CommonStruct.TouchPro)objTmp;

鼠标交互

为了交互,我创建了一个名为 MouseMov 的类。在这里,我使用 P/Invoke 来发送单击(按下和释放)和双击。

 [DllImport("user32.dll")]
 private static extern void mouse_event
	(UInt32 dwFlags, UInt32 dx, UInt32 dy, UInt32 dwData, IntPtr dwExtraInfo);
 private const UInt32 MOUSEEVENTF_LEFTDOWN = 0x0002;
 private const UInt32 MOUSEEVENTF_LEFTUP = 0x0004;
 internal static void SendUp()
 {
    mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, new System.IntPtr());
 }
 internal static void MoveMouse(Point p)
 {
    Cursor.Position = new Point(Cursor.Position.X + p.X, Cursor.Position.Y + p.Y);
 }

MoveMouse 是一个简单的光标移动。

internal static void SendDown()
{
    mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, new System.IntPtr());
}

好了,我们终于完成了。尝试运行我添加的两个解决方案,您可以看到代码在运行。让我知道您遇到的问题以及您的想法。玩得开心!

链接

我发现了这些链接非常有帮助

历史

  • 2008 年 11 月 10 日 - 首次发布 - v 10 2008
© . All rights reserved.