Canceling Long Processes with ESC
Windows only
Overview
When writing commands that contain long, time-consuming tasks, you might want to allow the user to cancel the process or command. Here are a couple examples of ways this can be achieved…
Example 1
The following example demonstrates how to periodically test to see if the user pressed the ESC key by “peeking” in Rhino’s message queue…
// -1 = Quit Rhino
// 1 = Escape key pressed
// 0 = Okay to proceed
int EscapeKeyPressed()
{
AFX_MANAGE_STATE( RhinoApp().RhinoModuleState() );
MSG msg;
memset( &msg, 0, sizeof(MSG) );
while( ::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE) )
{
if( msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE )
return 1;
if( !RhinoApp().PumpMessage() )
return -1;
}
return 0;
}
Using this function is quite simple: in the middle of your loop, call the above function. If the function returns something other than 0, break out of your loop.
Example 2
The following sample class demonstrates how to hook the Windows keyboard from a Rhino plugin 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;
}