Skip to content

Instantly share code, notes, and snippets.

@yoggy
Created February 3, 2011 08:49
Show Gist options
  • Save yoggy/809224 to your computer and use it in GitHub Desktop.
Save yoggy/809224 to your computer and use it in GitHub Desktop.
Windowsでコンソールからの1行読み込みを途中で中断するサンプル
//
// how_to_abort_reading_from_stdin.cpp
//
// Windowsでコンソールからの1行読み込みを途中で中断するサンプル
//
// メモ:
// * ReadConsole()を使うと入力されるまで制御が帰ってこなくなるので、
// 低レベルAPIのReadConsoleInput()を使用している。
// * GetStdHandle(STD_INPUT_HANDLE)で取れるハンドルに対して
// WaitForSingleObject()するとReadConsoleInput()のイベントを待つことになる。
// * キーアップ・キーダウンなどでイベントが発生する
// * 特殊キーの入力処理はかなり適当なので、必要に応じてまじめに実装すること。
//
#include <windows.h>
#include <stdio.h>
#include <process.h>
// see also... http://d.hatena.ne.jp/kazuhooku/20110126/1296031454
template <typename T, void (T::*FUNC)()>
unsigned int __stdcall begin_thread_callback(void* cb_arg)
{
T* obj = reinterpret_cast<T*>(cb_arg);
(obj->*FUNC)();
return 0;
}
class ThreadBase {
protected:
HANDLE thread_;
bool break_flag_;
public:
ThreadBase() : thread_(INVALID_HANDLE_VALUE), break_flag_(false) {};
virtual ~ThreadBase() {
stop();
};
virtual bool start() {
if (init() == false) return false;
thread_ = (HANDLE)_beginthreadex(
NULL,
0,
begin_thread_callback<ThreadBase, &ThreadBase::entry_point>,
(unsigned int *)this,
0,
NULL
);
return true;
};
virtual void stop() {
if (thread_ != INVALID_HANDLE_VALUE) {
break_flag_ = true;
::WaitForSingleObject(thread_, INFINITE);
::CloseHandle(thread_);
thread_ = INVALID_HANDLE_VALUE;
}
};
protected:
void entry_point() {
break_flag_ = false;
while (!break_flag_){
run();
}
finish();
_endthreadex(0);
};
virtual bool init() {return true;};
virtual void finish() {};
virtual void run() {};
};
class CUIThread : public ThreadBase
{
private:
HANDLE break_signal_;
public:
CUIThread() : ThreadBase() {
break_signal_ = CreateEvent(NULL, FALSE, FALSE, NULL);
};
virtual ~CUIThread() {
if (break_signal_ != INVALID_HANDLE_VALUE) {
CloseHandle(break_signal_);
break_signal_ = INVALID_HANDLE_VALUE;
}
};
bool readline(char *buf, unsigned int buf_size, unsigned int &read_size) {
memset(buf, 0, buf_size);
read_size = 0;
HANDLE std_in = GetStdHandle(STD_INPUT_HANDLE);
SetConsoleMode(std_in, ENABLE_PROCESSED_INPUT);
bool ret_val = false;
while(true) {
HANDLE h[2];
h[0] = break_signal_;
h[1] = std_in;
DWORD rv = WaitForMultipleObjects(2, h, FALSE, INFINITE);
DWORD err = GetLastError();
if (rv == WAIT_OBJECT_0) {
// abort
memset(buf, 0, buf_size);
read_size = 0;
return false;
}
else {
INPUT_RECORD input_record;
DWORD s = 0;
BOOL rv = ReadConsoleInput(std_in, &input_record, 1, &s);
if (input_record.EventType == KEY_EVENT && input_record.Event.KeyEvent.bKeyDown) {
switch(input_record.Event.KeyEvent.wVirtualKeyCode) {
case VK_BACK:
if (read_size > 0) {
printf("\b \b");
fflush(stdout);
buf[read_size] = 0;
read_size -= 1;
}
break;
case VK_RETURN:
printf("\n");
fflush(stdout);
return true;
default:
char c = (char)input_record.Event.KeyEvent.uChar.AsciiChar;
if (read_size + 1 < buf_size) {
// echo
putc(c, stdout);
fflush(stdout);
buf[read_size] = c;
read_size += 1;
}
break;
}
}
}
}
}
bool read_number_from_stdin(int &n) {
char buf[256];
unsigned int read_size;
bool rv = readline(buf, 256, read_size);
n = atoi(buf);
return rv;
}
virtual void run() {
char buf[256];
unsigned int read_size;
int n;
bool rv;
// readline() test
printf("input string: ");
rv = readline(buf, 256, read_size);
if (rv == false) {
printf("abort...\n");
return;
}
printf("buf=%s, read_size=%d\n", buf, read_size);
// read_number_from_stdin() test
printf("input number: ");
rv = read_number_from_stdin(n);
if (rv == false) {
printf("abort...\n");
return;
}
printf("n=%d\n", n);
};
virtual void stop() {
SetEvent(break_signal_);
ThreadBase::stop();
};
};
int main(int argc, char* argv[])
{
CUIThread *thread = new CUIThread();
thread->start();
Sleep(10 * 1000);
thread->stop();
delete thread;
thread = NULL;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment