McNeel Wiki
Canceling Long Processes with the ESC Key
edit · print · help · all topics
Main Pages

AccuRender

Bongo

Brazil r/s

Developer

Flamingo

Penguin

Rhino Blogs

Rhino

Rhino Labs

Search

Languages

Česky

Deutsch

English

Español

Français

Italiano

Polish

日本語

한국어

中文(繁體)

 
.
DeveloperC++

Overview

When writing commands that contain long, time-consuming tasks, you might want to allow the user to cancel the process or command. An easy way to do this is periodically test to see if the user pressed the ESC key. A fool-proof way for check to see if the ESC key has been pressed is to hook the Windows keyboard.

Example

The following sample class demonstrates how to hook the Windows keyboard from a Rhino plug-in and check for the ESC key.

  //
  // rhinoEscapeKey.h
  //


  class CRhinoEscapeKey
  {
  public:
    CRhinoEscapeKey( bool bHookNow = false );
    ~CRhinoEscapeKey();
    bool Start();
    void Stop();
    bool EscapeKeyPressed() const;
    void ClearEscapeKeyPressedFlag();
  protected:
    static LRESULT CALLBACK HookProc( int code, WPARAM wParam, LPARAM lParam );
    static HHOOK m_KeyboardHookProc;
    static bool m_escape_pressed;
  };
  //
  // rhinoEscapeKey.cpp
  //


  bool CRhinoEscapeKey::m_escape_pressed = false;
  HHOOK CRhinoEscapeKey::m_KeyboardHookProc = NULL;


  CRhinoEscapeKey::CRhinoEscapeKey( bool bStartNow )
  {
    if( bStartNow )
      Start();
  }


  CRhinoEscapeKey::~CRhinoEscapeKey()
  {
    Stop();
  }


  bool CRhinoEscapeKey::Start()
  {
    if( NULL == m_KeyboardHookProc )
      m_KeyboardHookProc = ::SetWindowsHookEx( 
                                WH_KEYBOARD, 
                                CRhinoEscapeKey::HookProc, 
                                RhinoApp().RhinoInstanceHandle(), 
                                ::AfxGetThread()->m_nThreadID 
                                );
    ClearEscapeKeyPressedFlag();
    return( NULL != m_KeyboardHookProc );
  }


  void CRhinoEscapeKey::Stop()
  {
    if( m_KeyboardHookProc )
      UnhookWindowsHookEx( m_KeyboardHookProc );
    m_KeyboardHookProc = NULL;
  }


  bool CRhinoEscapeKey::EscapeKeyPressed() const
  {
    RhinoApp().Wait(0);
    return m_escape_pressed;
  }


  void CRhinoEscapeKey::ClearEscapeKeyPressedFlag()
  {
    m_escape_pressed = false;
  }


  LRESULT CALLBACK CRhinoEscapeKey::HookProc( int code, WPARAM wParam, LPARAM lParam )
  {
    // On escape key down....
    if( code == HC_ACTION && wParam == VK_ESCAPE && !(lParam & 0x80000000) )
    {
      m_escape_pressed = true;
      UnhookWindowsHookEx( m_KeyboardHookProc );
      m_KeyboardHookProc = NULL;
      return 0; // Eat the escape key
    }
    // call next hook proc including standard windows proc.
    return CallNextHookEx( m_KeyboardHookProc, code, wParam, lParam );
  }

Usage

The following sample code demonstrates using the CRhinoEscapeKey class within a Rhino command.

  CRhinoCommand::result CCommandTest::RunCommand( const CRhinoCommandContext& context )
  {
    CRhinoEscapeKey escape;
    escape.Start();


    int i = 0;
    while( true )
    {
      if( escape.EscapeKeyPressed() )
      {
        escape.Stop();
        RhinoApp().Print( L"Command canceled.\n" );
        break;
      }
      RhinoApp().Print( L"Count = %d.\n", ++i );
    }


    return success;
  }
rename · changes · history · subscriptions · lost and found · references · file upload