北京理工大学 20981 陈罡
在玩手机的时候偶然间看到了手机上屏幕抓图的软件,感到很有趣,
尤其是它的隐藏到后台,只要我按下了它里面的组合键,抓图窗口
就会从后台切换到前台来。但是这是怎么实现得呢?从原理上分析
或许是作者采用了一个后台监视exe服务,做为一个filter过滤器,
常规的按键可以通过,这个过滤器不做响应;一旦出现程序预设的
特征按键,过滤器就会激活特定的程序,从而达到全局按键呼出的
效果。
在网上搜了一下,原来国外的大侠们很早就讨论过这个获取全局按键
的service的方法了,现在就借花献佛,把他们的代码拿出来分析一下。
整个demo是一个exe文件,需要安装到手机上,然后使用ExeLauncher,
或者System Explorer之类的第三方软件启动起来才能看到效果。
基本的效果是,捕获手机的"左方向键",连续捕获3次,第三次的时候
捕获程序会退出。
这里还顺便展示了可以在切换窗口隐藏程序的小技巧,用心的朋友可以
从中找到很多有用的东西。
使用的时候,可以分为以下几步来检验效果:
(1)编译源代码包,需要注意的是bld.inf中,原作者采用的是thumb
平台编译的,你可以根据需要改为armi的方式编译。
(2)修改.pkg文件,这个不用多说了,只是路径和平台版本号改一下,
就可以通过蓝牙或者数据线拷贝到手机上安装了。
(3)通过ExeLauncher或者System Explorer之类的软件在手机上找到
该软件,并启动它。
(4)可以开始测试了,需要先按一下手机的“菜单键”(也有人叫它“花”键)
三秒钟,会看到一个当前运行的任务列表,其中一个叫做“Console”的
运行程序,就是这个demo生成的输出窗口,此时窗口中应该显示出
“Starting key capturing”的字样。
(5)再次按下“菜单键”三秒钟,选择电话模样的图标,它就是让手机
切换到正常的电话模式,同时又保证了globalkeycapture这个exe仍然
在后台运行着。
(6)现在按一下左方向键,然后按住“菜单键”三秒钟,然后选择“Console”
切换回“Console”输出窗口,现在应该就可以看到如下的输出
“Captured correct key press for the 1 time”
(7)重复(5)和(6)的操作,会发现
“Captured correct key press for the 2 time”
(8)然后再次重复(5)和(6)的时候,会发现“Console”这个程序已经
退出(这是正常的,在程序中只设置了截获3次而已)。
这里是global key capture的头文件,
#ifndef __GLOBALKEYCAPTURE_H__
#define __GLOBALKEYCAPTURE_H__
// Include Files
#include
#include // RWsSession
//symbian体系结构中,广泛地采用了C/S结构的模式,所有的客户端需要
//连接服务器的时候,都需要建立一个session,通过该session进行连接
//以及数据的收发。
//这里我们目的是连接全局的窗口服务器,所以必须借助于RWsSession来
//建立与窗口服务器的连接。
// Forward declarations
class RWindowGroup;
class CApaWindowGroupName;
/**
* Registers itself for event capturing.
* Is notified about events via CActive calls
*/
// 该类是通过活动对象来处理键盘事件
class CGlobalCapturer : public CActive
{
public:
// New functions
/**
* Registers itself for the key press events
* @leave Symbian standard leave codes
*/
void StartCapturingL();
~CGlobalCapturer();
CGlobalCapturer();
public:
// From CActive
/**
* Is called by active scheduler when key press happens
*/
void RunL();
protected:
/**
* Is called when event listening should be stopped
*/
virtual void DoCancel();
private:
// Data
// Session to the window server
// 用它来连接窗口服务器
RWsSession iWsSession;
// Window group created to listed to the events
// 用它来获取按键事件
RWindowGroup* iWindowGroup;
// Is used to hide window from the task switcher
// 这个就是用来在任务切换器中隐藏当前窗口的地方了
CApaWindowGroupName* iWindowGroupName;
// Handle to the capturing request
TInt32 iCaptureHandle;
// Number of keypresses already captured
TInt iCaptureCounter;
};
// Function Prototypes
#ifdef __WINS__
IMPORT_C TInt WinsMain(TAny* aParam);
#else
GLDEF_C TInt E32Main();
#endif
#endif // __GLOBALKEYCAPTURE_H__
这里是global key capture的源文件中定义的内容了
// Include Files
#include "GlobalKeyCapture.h"
#include
#include
#include // Console 完全是为了显示方便,
// 其实不用它也可以利用文件之类的手段做到
#include // CApaWindowGroupName
// Constants
// Text strings created by wizard
_LIT( KTextConsoleTitle, "Console" );
_LIT( KTextFailed, " failed, leave code = %d" );
_LIT( KTextPressAnyKey, "\n[press any key to exit]\n" );
// Key to listen to
// 定义需要捕获的全局按键,这里是Left Arrow,左方向键
const TUint KKeyCode = EKeyLeftArrow;
// Capture KNumberOfPressesToCapture key presses
// 定义捕获多少次的,这里只定义了3次,可以根据需要修改
const TInt KNumberOfPressesToCapture = 3;
// Global Variables
// write all messages to this console
// 用于显示的console,可以根据需要改成别的,不过最好
// 不要用于做违法的事情。
LOCAL_D CConsoleBase* console;
// Local Functions
// 创建按键捕获类的对象capture,并做了一些异常防护工作。
// 然后调用capturer->StartCapturingL()初始化活动对象
LOCAL_C void MainL(const TDesC& /*aArgs*/)
{
console->Write( _L( "Starting key capturing\n" ) );
// Create capturer
CGlobalCapturer* capturer = new (ELeave) CGlobalCapturer();
CleanupStack::PushL( capturer );
// And start capturing
capturer->StartCapturingL();
// In a real application you should use CActiveSchedulerWait,
// but in this small demo application we know that there is just
// a single inner loop and no CActiveSchedulerWait "protection" is
// needed
CActiveScheduler::Start();
// Cleanup. Demo completed
CleanupStack::PopAndDestroy( capturer );
}
// Main function. Is TRAPD outside, in Start()
// 创建用于按键捕获的活动对象调度器
// 然后调用MainL(symbian是多么啰嗦的一个系统啊,需要绕这么多圈子,
// 不过这些都是为了保证系统可以“万无一失”的运行)
LOCAL_C void DoStartL()
{
// Create active scheduler (to run active objects)
CActiveScheduler* scheduler = new (ELeave) CActiveScheduler();
CleanupStack::PushL(scheduler);
CActiveScheduler::Install(scheduler);
// Call main function with command line
// Command line is not really needed for this demo, but
// why not to demonstrate this either?
TBuf<256> cmdLine;
RProcess().CommandLine( cmdLine );
MainL( cmdLine );
// Delete active scheduler
CleanupStack::PopAndDestroy(scheduler);
}
// Global Functions
// 本函数将初始化异常处理栈,创建console对象
// 创建完毕后调用DoStartL,开始进入按键捕获
// 捕获完毕后,销毁对象和异常处理栈
GLDEF_C TInt Start()
{
// Create cleanup stack
__UHEAP_MARK;
CTrapCleanup* cleanup = CTrapCleanup::New();
// Create output console
TRAPD(createError, console = Console::NewL(KTextConsoleTitle, TSize(KConsFullScreen,KConsFullScreen)));
if (createError)
return createError;
// Run application code inside TRAP harness, wait keypress when terminated
TRAPD(mainError, DoStartL());
if (mainError)
console->Printf(KTextFailed, mainError);
console->Printf(KTextPressAnyKey);
console->Getch();
delete console;
delete cleanup;
__UHEAP_MARKEND;
return KErrNone;
}
////////////////////////////////////
// CGlobalCapturer
////////////////////////////////////
// Constructor
CGlobalCapturer::CGlobalCapturer() :
CActive( EPriorityNormal )
{
// nothing
}
// Destructor
CGlobalCapturer::~CGlobalCapturer()
{
delete iWindowGroupName;
delete iWindowGroup;
iWsSession.Close();
}
// Cancel listening to key presses
void CGlobalCapturer::DoCancel()
{
iWindowGroup->CancelCaptureKey( iCaptureHandle );
}
// 活动对象的初始化,连接symbian的窗口服务器,
// 创建了一个新的窗口群,并把它的名字设为全局的
// (只有这样才会收到窗口服务器的消息)
// 让当前窗口群在任务切换列表中隐藏起来
void CGlobalCapturer::StartCapturingL()
{
// Connect to the window server
User::LeaveIfError( iWsSession.Connect() );
// Create an invisible window group. Well, we'll make it invisible later
iWindowGroup = new (ELeave) RWindowGroup ( iWsSession );
// @see RBlankWindow::Construct,这是空窗口的初始化
iWindowGroup->Construct( (TUint32)iWindowGroup, EFalse );
// Capture a key
User::LeaveIfError( iCaptureHandle = iWindowGroup->CaptureKey( KKeyCode , 0, 0 ) );
// Send created window to the background and hide it from the
// application switcher,这里就是隐藏窗口群了
iWindowGroup->SetOrdinalPosition(-1);
iWindowGroup->EnableReceiptOfFocus( EFalse );
iWindowGroupName = CApaWindowGroupName::NewL( iWsSession );
iWindowGroupName->SetHidden(ETrue);
iWindowGroupName->SetWindowGroupName( *iWindowGroup );
// Tell window server, that we are ready to receive events
// 这里就是进入了等待过程,由于传入了iStatus,系统自动处于
// 阻塞状态,除非左方向键被按下,否则就会一直阻塞,无法运行
// 活动对象的RunL函数
iWsSession.EventReady( &this->iStatus );
CActiveScheduler::Add( this );
SetActive();
}
// Key press happened
// 这里才是真正的截获全局按键的处理函数,放在活动对象里面了
void CGlobalCapturer::RunL()
{
if( iStatus == KErrNone )
{
// EEventKey received
console->Write( _L( "Captured key press\n" ) );
TWsEvent we;
iWsSession.GetEvent( we );
if( we.Key()->iCode == KKeyCode )
{
console->Printf( _L( "Captured correct key press for the %i time\n" ), ++iCaptureCounter );
}
else
{
// This should never happen, but just to demonstrate how
// it is possible to forward events to the default destination
// 这几行是以防万一,万一系统产生了按键事件,但是这个键又不是
// 我们需要监测的(这从原理上讲不太可能发生,不过如果是symbian的话,
// 就没什么不可能的,还是预防一下比较好),就把这个按键事件再送回给
// 系统。有心做代替用户输入的朋友看好了喔~~
TInt foregroundAppId = iWsSession.GetFocusWindowGroup();
iWsSession.SendEventToWindowGroup( foregroundAppId, we );
} // if iCode
if( iCaptureCounter == KNumberOfPressesToCapture )
{
// exit MainL() inner loop
// 这里就是退出活动对象的调度器了
CActiveScheduler::Stop();
}
else
{
iWsSession.EventReady( &iStatus );
SetActive();
} // if captured enough times
} // if iStatus
else
{
// Framework notified of some error
/** @todo Handle error if required */
}
}
// Exported Functions
#ifdef __WINS__
EXPORT_C TInt WinsMain(TAny* /*aParam*/)
{
return Start();
}
#else
// 这是整个exe开始的地方,它调用了start函数
GLDEF_C TInt E32Main()
{
return Start();
}
#endif
#ifdef __WINS__
TInt E32Dll(TDllReason /*aReason*/)
{
return KErrNone;
}
#endif
// End of file
最后为了大家能够方便的用真机测试,贴上整个项目的源代码,
symbian s60 sdk v21环境下编译通过,并在6670上测试通过。
| 文件: | GlobalKeyCapture.rar | 大小: | 6KB | 下载: | 下载 |
|
阅读(4671) | 评论(2) | 转发(0) |