Windows Mobile触摸屏截获(手写)

2024-06-13 03:38

本文主要是介绍Windows Mobile触摸屏截获(手写),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

欢迎加入Wince技术讨论群QQ#326444254

为了做全屏手写功能,需要把鼠标的事件全部截获过来,研究了一个星期左右,发现有三种方法可以实现。而且对每种方法已经写了测试代码。根据三种方法效果的好坏排序:

1. 用英文手写识别(TRNSCRBR)Touch的拦截代码,这种方法实现是上上策,这个是微软为手写专门在Touch 驱动中加的。

2. 自己写一个伪Touch驱动,让GWES加载这个Touch驱动,在你的Touch驱动中再调用原来的驱动。

3. QASetWindowsJournalHook来获取键盘和鼠标事件, 从QA打头可以看出这个API用来QA来用的,它是用来记录键盘和鼠标的事件,并不能截获,我试了很久,最后找出一种比较BT的方法,就是在收到WM_LBUTTON的时候,用SetWindowsPos设置一个窗体到TOPMOST,这样其他的窗体就收不到WM_LBUTTON的消息。但是Mouse消息还是能够收到。所以才把这种方法定到最后的方案。

上面说了这三种方法,想必大家想知道是如何实现的吧。我们就一个个的来,先说第一个。

1. 利用微软手写识别驱动

由于项目比较紧,这种方法我目前还没有完全试验成功,但我可以这个方法肯定是可行的,只要给点时间肯定是可以的。不说废话了,还是把我知道的东东介绍给大家吧。

先来介绍一下Windows CE Touch Panel的驱动。微软的Touch驱动是一个本地驱动,非流驱动。分为MDD层和PDD层。对于PDD层我们不需要了解,它导出了如下的函数供MDD调用(DDIS接口)。

DdsiTouchPanelAttach

DdsiTouchPanelDetach

DdsiTouchPanelDisable

DdsiTouchPanelEnable

DdsiTouchPanelGetDeviceCaps

DdsiTouchPanelGetPoint

DdsiTouchPanelPowerHandler

   微软把与驱动无关的代码放到了MDD层中,这部分代码都开源的,我们可以直接研究它的代码,看我们如何来实现。由于微软不提供相应的截获鼠标的文档,所以我只能看驱动代码,从中得到方法。 微软的MDD层由几个独立的模块来协同完成。

Touch Panel校准算法:源码在PUBLIC/COMMON/OAK/DRIVERS/TCH_CAL下,这部分跟我们没有多大关系,就不细介绍了。

Touch Panel校准UI:源码在PUBLIC/COMMON/OAK/DRIVERS/CALIBRUI

Touch Panel DDI接口:C:/WM605/PUBLIC/COMMON/OAK/DRIVERS/TOUCH,这个目录下又分了三个子目录

1. TCHMAIN:实现DDI接口。

TouchPanelGetDeviceCaps

TouchPanelEnable

TouchPanelDisable

TouchPanelSetMode

TouchPanelReadCalibrationPoint

TouchPanelReadCalibrationAbort

TouchPanelSetCalibration

TouchPanelCalibrateAPoint

TouchPanelPowerHandler

这里面最重要的一段代码就在TouchPanelEnable中,只所以重要,是跟我们实现Touch截获有关。看一下这个函数里的代码

extern PFN_TOUCH_PANEL_CALLBACK v_pfnCgrPointCallback;

extern PFN_TOUCH_PANEL_CALLBACK v_pfnCgrCallback;

...

BOOL

TouchPanelEnable(

    PFN_TOUCH_PANEL_CALLBACK    pfnCallback

    )

{

...

  v_pfnCgrPointCallback = pfnCallback;

    if (v_pfnCgrCallback != NULL)

    v_pfnPointCallback = v_pfnCgrCallback;

    else

        v_pfnPointCallback = pfnCallback;

...

}

说明下PFN_TOUCH_PANEL_CALLBACK ,每次Touch得到一个点后都会调用它指向的函数。v_pfnCgrPointCallback用来指向TouchPanelEnable原始的函数入口、而v_pfnPointCallback用来指向我们自己的入口。当我们把v_pfnCgrCallback指向自己写的函数时。

2. BASIC:没有微软手写驱动的时,这里面的代码就只有DLLMain的功能

3. TRNSCRBR:要想把TRNSCRBR编译到驱动中,需要在BSP的设置加WCESHELLFE_MODULES_TRANSCRIBER 或者SHELLW_MODULES_CGRTOUCH。最终导出如下函数:

TouchReset

TouchRegisterWindow

TouchUnregisterWindow

TouchSetValue

TouchGetValue

TouchCreateEvent

TouchGetFocusWnd

TouchGetLastTouchFocusWnd

TouchGetQueuePtr

下面详细来讲下关于TRNSCRBR截获Touch Panle的方法。先看一下它导出的函数.

void TouchReset(BOOL bSetAllValuesToDefault)TRNSCRBR定义了一些配置,通过这个函数来Reset到默认。

BOOL TouchRegisterWindow(HWND hClientWnd); 注册Wnd到Touch驱动,Touch把键盘消息发送给这个窗口了。

void TouchUnregisterWindow(HWND hClientWnd); 取消注册,Touch把消息发送给系统

void TouchSetValue(DWORD dwNameDWORD dwValue); 设置配置

LRESULT TouchGetValue(DWORD dwNameDWORD dwValue); 得到有个配置

void TouchCreateEvent(int iXint iY); 发送Mouse 消息给系统

LPVOID TouchGetQueuePtr(); 从队列中读取一个POINT。

HWND TouchGetFocusWnd(); 

HWND TouchGetLastTouchFocusWnd();

所以你建立一个自己的窗体,然后设置窗体的属性。在Touch驱动中,它会根据你窗体设置的属性来发送哪些mouse消息给你。

   / / set flags 

        _iClientFlags = (int)GetWindowLong(_hClientWnd, 0);

        //just adjust

         if((_iClientFlags&TABLET_TEST_FIRST_POINT)!=0 &&

           !SendMessage(_hClientWnd, WM_PEGREC_FIRSTPOINT, 0, MAKELPARAM(X, Y)))

           {

              _iClientFlags = TABLET_ALL_TO_SYSTEM;

           }

很是奇怪,一般的窗体是不能SetWindowLong(hWnd,0,XXX)。只有对于对话框才有设置这个属性。#define DWL_MSGRESULT   0。但是对话框有一个问题,当你SendMessage给对话框时,返回值总为空。所以上面的SendMessage(_hClientWnd, WM_PEGREC_FIRSTPOINT, 0, MAKELPARAM(X, Y)返回为FALSE。这样导致把_iClientFlags位置为TABLET_ALL_TO_SYSTEM,这样所有的消息都发给了系统。还有即使是用对话框,调用SetWindowLong(hDlg, 0, 0x2FD); 但是GetWindowLong(_hClientWnd,0),返回始终为空。

下面是SetWindowLong需要设置的内容(pegc_def.h 内)。

// TCHSTUB strokes dispatch modes (set in window longs)

#define TABLET_SILENT                0x0000

#define TABLET_ALL_TO_CLIENT         0x0001

#define TABLET_ALL_TO_SYSTEM         0x0002

#define TABLET_CLICK_TO_SYSTEM       0x0004

#define TABLET_STARTDELAY_TO_SYSTEM  0x0008

#define TABLET_INTERDELAY_TO_SYSTEM  0x0010

#define TABLET_SEND_RELEASE_MSG      0x0020

#define TABLET_SEND                  0x0040

#define TABLET_NEED_PERMITION        0x0080

#define TABLET_SAVE_FOCUS_WND        0x0100

#define TABLET_TEST_FIRST_POINT      0x0200

我现在调用TouchRegisterWindow后,要么只截获Touch Down的消息,mouse和up都没有截获,或者是把系统的所以消息都屏蔽了。哪位有兴趣可以研究一下。在模拟器上是可以试的。模拟器的PDD代码目录在PLATFORM/DEVICEEMULATOR/SRC/DRIVERS/TOUCH下,需要把MDD的代码合成在一起,通过打印信息就可以看出效果了。这个方法没有实现,还讲了这么多的废话,哈哈。

我把Touch的MDD层代码放到了TouchMDD中了。

2. 编写伪Touch驱动。

这个方法跟上面的方法其实是一个方法,只是微软来写和我们自己的区别。微软写的太复杂了,搞得我没有搞定上面的方法。可能是微软为了更好的扩展和其他应用吧。但是我们只给自己的应用用的话,那就很简单了。看代码吧

BOOL APIENTRY DllMain( HANDLE hModule, 

                       DWORD  ul_reason_for_call, 

                       LPVOID lpReserved

 )

{

switch ( ul_reason_for_call )

{

case DLL_PROCESS_ATTACH:

DisableThreadLibraryCalls((HMODULE) hModule);

g_hInstTouch = LoadLibrary(TOUCH_DLL);

break;

case DLL_PROCESS_DETACH:

if(NULL != g_hInstTouch)

FreeLibrary(g_hInstTouch);

break;

}

    return TRUE;

}

在DllMain中,把Touch.dll Load进来。我们只要看TouchPanelEnable和自己的一个callback函数。

// 原始Callback

PFN_TOUCH_PANEL_CALLBACK pfnOrgTouchPanelCallback = NULL;

HWND g_sipWnd = NULL;

HWND g_hwWnd = NULL;

#define  HW_CLASSNAME  L"CeSipEng"

INT xSaved = 0;

INT ySaved = 0;

int  iMinX = 4;

int  iMinY = 4;

BOOL hwTouchPanelCallback(

TOUCH_PANEL_SAMPLE_FLAGS Flags,

INT X,

INT Y

)

{

if(NULL == g_sipWnd)

{

g_sipWnd = FindWindow(L"SipWndClass", NULL);

}

g_hwWnd = GetWindow(g_sipWnd, GW_CHILD);

if(IsWindowVisible(g_hwWnd))

{

TCHAR szClassName[32];

GetClassName(g_hwWnd, szClassName, 32);

if(wcsicmp(szClassName, HW_CLASSNAME) ==  0)

{

// down

if(Flags == (TouchSampleDownFlag | TouchSampleIsCalibratedFlag | TouchSampleValidFlag))

{

SendMessage(g_hwWnd, WM_LBUTTONDOWN, 0, MAKELPARAM(X,Y));

}

// mouse 

else if(Flags == (TouchSampleDownFlag | TouchSamplePreviousDownFlag | TouchSampleIsCalibratedFlag | 

TouchSampleValidFlag) &&

xSaved - X > iMinX || X - xSaved > iMinX &&

ySaved - Y > iMinY || Y - ySaved > iMinY)

{

SendMessage(g_hwWnd, WM_MOUSEMOVE, 0, MAKELPARAM(X,Y));

}

// up

else if(Flags ==(TouchSampleIsCalibratedFlag | 

TouchSampleValidFlag | TouchSamplePreviousDownFlag))

{

SendMessage(g_hwWnd, WM_LBUTTONUP, 0, MAKELPARAM(X,Y));

}

xSaved = X;

ySaved = Y;

return TRUE;

}

Else

// 发送给系统

return pfnOrgTouchPanelCallback(Flags, X,Y);

}

//发送给系统

return pfnOrgTouchPanelCallback(Flags, X,Y);

}

BOOL

TouchPanelEnable(

 PFN_TOUCH_PANEL_CALLBACK    pfnCallback

 )

{

if(g_hInstTouch)

{

if (NULL == pfnTouchPanelEnable)

{

pfnTouchPanelEnable = (PFN_TOUCH_PANEL_ENABLE)GetProcAddress(g_hInstTouch, 

TEXT("TouchPanelEnable"));

}

// 保存原来的Callback

pfnOrgTouchPanelCallback = pfnCallback;

// 传入自己的Callback

return pfnTouchPanelEnable(hwTouchPanelCallback);

}

return FALSE;

}

其他的函数跟TouchPanelEnable一样的形式,就是GetProcAddress一下,然后再Call一下。

差点忘记,把自己的写的注册到系统,让GWES.exe调用。

[HKEY_LOCAL_MACHINE/HARDWARE/DEVICEMAP/TOUCH]

“DriverName”=MyTouch.dll

我把伪Touch的代码放到了MyTouch中

3. QASetWindowsJournalHook

这个函数在pwinuser.h中。微软在Windows CE中支持键盘HOOK、键盘/鼠标的记录、键盘/鼠标的回放,看pwinuser.h中的定义

#define WH_JOURNALRECORD    0

#define WH_JOURNALPLAYBACK  1

#define WH_KEYBOARD_LL      20

写累了,直接看代码

EVENTMSG evtMsg; 

ZeroMemory(&evtMsg, sizeof(EVENTMSG));

g_hHook = QASetWindowsJournalHook(WH_JOURNALRECORD, KeyboardProc, &evtMsg);

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)

{

switch (nCode)

{

case HC_ACTION:

{

EVENTMSG *mesg = (EVENTMSG *)lParam;

UINT message = mesg->message;

if(message == WM_LBUTTONDOWN)//pressed

{

//DEBUGMSG (ZONE_FUNCTION, (TEXT("WM_LBUTTONDOWN/r/n")));

}

else if(message == WM_MOUSEMOVE)

{

//DEBUGMSG (ZONE_FUNCTION, (TEXT("WM_MOUSEMOVE/r/n")));

}

else if(message == WM_LBUTTONUP)

{

//DEBUGMSG (ZONE_FUNCTION, (TEXT("WM_LBUTTONUP/r/n")));

}

POINT pt = {0};

pt.x = LOWORD(mesg->paramL);

pt.y = HIWORD(mesg->paramL);

if(message == WM_LBUTTONDOWN )

{

HWND hWnd = WindowFromPoint(pt);

if(pMouseSpy != NULL && pt.y < 600)

{

// 用SetWindowsPos来设置到最前台,这样窗体就接收不到Down消息。

SetWindowPos(pMouseSpy->m_hWnd, HWND_TOPMOST,pt.x -10, pt.y -10, 20, 20, SWP_SHOWWINDOW);

}

}

}

return TRUE;

default:

break;

}

}

这里SetWindowPos(pMouseSpy->m_hWnd, HWND_TOPMOST,pt.x -10, pt.y -10, 20, 20, SWP_SHOWWINDOW);,这个很重要很重要,是我试了很多方法才把WM_LBUTTONDOWN消息给屏蔽掉。但是WM_MOUSEMOVE和WM_LBUTTONUP用这种方法是不能过滤掉的。有人会问,在窗体 (pMouseSpy->m_hWnd的WM_LBUTTONDOWN中去SetCapture不就OK了,哈哈,不行的。这个窗体虽然是TOPMOST的,但是也是收不到WM_LBUTTONDOWN消息的。又有人说,我们可以PostMessage(pMouseSpy->m_hWnd, WM_LBUTTONDOWN, 0,mesg->paramL)。SetWindowPos后,发送WM_LBUTTONDOWN消息,但是WM_LBUTTONDOWN先于SetWindowPos发送的WM_WINDOWPOSCHANGED的消息,还是没用。

最后,当你不用的时候用QAUnhookWindowsJournalHook(WH_JOURNALRECORD);释放掉。

  OK了, So Long No Write, 写的不好,请见谅。

这篇关于Windows Mobile触摸屏截获(手写)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1056173

相关文章

在 Windows 上部署 gitblit

在 Windows 上部署 gitblit 在 Windows 上部署 gitblit 缘起gitblit 是什么安装JDK部署 gitblit 下载 gitblit 并解压配置登录注册为 windows 服务 修改 installService.cmd 文件运行 installService.cmd运行 gitblitw.exe查看 services.msc 缘起

Windows如何添加右键新建菜单

Windows如何添加右键新建菜单 文章目录 Windows如何添加右键新建菜单实验环境缘起以新建`.md`文件为例第一步第二步第三步 总结 实验环境 Windows7 缘起 因为我习惯用 Markdown 格式写文本,每次新建一个.txt后都要手动修改为.md,真的麻烦。如何在右键新建菜单中添加.md选项呢? 网上有很多方法,这些方法我都尝试了,要么太麻烦,要么不凑效

Windows下Nginx的安装及开机启动

1、将nginx-1.16.1.zip解压拷贝至D:\web\nginx目录下。 2、启动Nginx,两种方法: (1)直接双击nginx.exe,双击后一个黑色的弹窗一闪而过。 (2)打开cmd命令窗口,切换到nginx目录下,输入命令 nginx.exe 或者 start nginx ,回车即可。 3、检查nginx是否启动成功。 直接在浏览器地址栏输入网址 http://lo

Windows环境利用VS2022编译 libvpx 源码教程

libvpx libvpx 是一个开源的视频编码库,由 WebM 项目开发和维护,专门用于 VP8 和 VP9 视频编码格式的编解码处理。它支持高质量的视频压缩,广泛应用于视频会议、在线教育、视频直播服务等多种场景中。libvpx 的特点包括跨平台兼容性、硬件加速支持以及灵活的接口设计,使其可以轻松集成到各种应用程序中。 libvpx 的安装和配置过程相对简单,用户可以从官方网站下载源代码

C++实现俄罗斯方块(Windows控制台版)

C++实现俄罗斯方块(Windows控制台版) 在油管上看到一个使用C++控制台编写的俄罗斯方块小游戏,源代码200多行,B站上也有相关的讲解视频,非常不错,值得学习。 B站讲解视频地址为:【百万好评】国外技术大神C++游戏编程实战教程,油管580W收藏,新手10小时入门,并快速达到游戏开发能力(中英字幕) B站 CSDN博主千帐灯无此声还为此写了一篇博客:C++实现俄罗斯方块(源码+详解),讲

Windows下php扩展开发c++动态库

PHP扩展开发,从零了解到初步完成一个小项目,经过三天的仔细研究,现整理如下 一、需求介绍 PHP扩展开发,调用自己之前的c++动态库,完成功能 二、项目之前 系统:windows xp  开发工具:vs 2008 web环境:apache2.4  PHP5.3.29-VC9-ts-x86 aphach和PHP 环境之前已经搭建完成 PHP源码:去官网http://www.php.n

OpenStack镜像制作系列4—Windows Server2019镜像

本系列文章主要对如何制作OpenStack镜像的过程进行描述记录  CSDN:OpenStack镜像制作教程指导(全) OpenStack镜像制作系列1—环境准备 OpenStack镜像制作系列2—Windows7镜像 OpenStack镜像制作系列3—Windows10镜像 OpenStack镜像制作系列4—Windows Server2019镜像 OpenStack镜像制作系

Windows与linux中docker的安装与使用

windos中安装使用docker 下载Docker_Desktop 安装包进入docker官网下载Docker_Desktop: https://www.docker.com/ 启用wsl 我们搜索“启用或关闭Windows功能”,打开后勾选适用于Linux的Windows 子系统 Docker_Desktop设置 出现Docker Engine stopped的解决

Windows 10 各版本

对应于服务选项的 Windows 10 当前版本 Version服务选项上市日期OS build最后修订日期1803半年频道7/10/201817134.1917/24/2018Microsoft 建议使用1803半年频道(定向)4/30/201817134.1917/24/20181709半年频道1/18/201816299.5797/24/20181709半年频道(定向)10/17/2017

Windows系统不关机故障的解决方法

当Windows系统出现不关机故障时,首先要查找引起Windows系统不关机的原因,然后根据 具体的故障原因采取相应的解决方法。   Windows系统不关机故障的解决方法如下。   1.检查所有正在运行的程序   检查运行的程序主要包括关闭任何在实模式下加载的TSR程序、关闭开机时从启动组自动启 动的程序、关闭任何非系统引导必需的第三方设备驱动程序。   检查运行的程序并停