Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1518498
  • 博文数量: 114
  • 博客积分: 10010
  • 博客等级: 上将
  • 技术积分: 1357
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-19 18:13
文章分类
文章存档

2010年(8)

2009年(9)

2008年(27)

2007年(62)

2006年(8)

我的朋友

分类: C/C++

2008-02-15 12:36:33

北京理工大学 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
下载:下载



阅读(4633) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2008-02-24 05:16:36

iWindowGroup->SetOrdinalPosition(-1); 你好,不知能不能从这个方法出发讲讲window group的排列以及事件接收机制? 正学symbian,学艺不精,希望找高手多请教~~ ibn$live.com

chinaunix网友2008-02-18 10:32:15

你好,我现在也在做mplayer的移植工作,希望能和你交流,谢谢! msn:czhongkun◎gmail.com