だらだら〜自由自在〜

インディーゲーム制作チーム GAME GABURI でプログラム担当してます

QWidget継承クラスでRegisterHotKey使おうとしてハマった

作成中のファイラーにグローバルホットキーを実装しようとしてハマった話。
Qt5.11を使っている。
RegisterHotKeyはWindowsでグローバルホットキーを実装するときの選択肢の一つだが、このメッセージハンドラをQtWidget継承クラスのoverrideしたnativeEventFilterで受けようとしたけどメッセージが来なかった。
最終的にqApp->installNativeEventFilterでハンドラーを登録してやって実装できた。
下記コードは実装例(※色々省いた擬似コードみたいなものなのでコンパイルは通らない)

static const int HOTKEY_EVENT_ID = 100;

class Filer : public QWidget
{
	Filer(QWidget *parent)
		: QWidget(parent)
	{
		qApp->installNativeEventFilter(&_globalShortcutEvent);
		
		//コストラクタなどで購読開始
		HWND hwnd = (HWND)winId();
		auto result = ::RegisterHotKey(
			hwnd,
			HOTKEY_EVENT_ID,
			MOD_CONTROL,
			0x32 //2キー
		);
		
		if(!result) {/*登録エラー*/}
	}
	
	void closeEvent(QCloseEvent* )
	{
		//closeEventなどで購読解除
		::UnregisterHotKey(HWND(winId()), HOTKEY_EVENT_ID);
	}

	class EventFilter : public QAbstractNativeEventFilter
	{
	public:
		bool nativeEventFilter(const QByteArray& eventType, void* message, long* result);
	};
	
	static EventFilter _globalShortcutEvent;
	
#if 0
	//最初は基底クラスにあるこの関数をOverrideしていたけど、WM_HOTKEYメッセージが来ることがなかった・・・
    virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result);
#endif
}

bool Filer::EventFilter::nativeEventFilter(const QByteArray& eventType, void* message, long* result)
{
	Q_UNUSED(eventType)
	Q_UNUSED(result)
	// Transform the message pointer to the MSG WinAPI
	MSG* msg = reinterpret_cast<MSG*>(message);

	// If the message is a HotKey, then ...
	if (msg->message == WM_HOTKEY)
	{
		// ... check HotKey
		if (msg->wParam == HOTKEY_EVENT_ID)
		{
			// We inform about this to the console
			qDebug() << "HotKey worked";
			return true;
		}
	}
	return false;
}