ちょっと開発ではまったネタなので書いてみます。
WindowsVista以上だととある条件でスクリーンセーバーの起動が停止できなくなる?
というのも、WindowsXP以前だとこういう書き方でスクリーンセーバーの起動を停止できました。
私が今まで使っていたスクリーンセーバーの起動停止コードです。
LRESULT WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) { switch(uMsg){ case WM_SYSCOMMAND: if((wParam & 0xfff0) == SC_SCREENSAVE){ return 0; } break; ・・・ } return DefWindowProc(hWnd,uMsg,wParam,lParam); }
で、問題なのはMicrosoftのとあるセクションにもありますが、この方法だと
- WindowsVista以上
- スクリーンセーバー起動設定あり
- スクリーンセーバーからの復帰時にログイン画面に戻す
のときにこの処理を行うことなくスクリーンセーバーを起動してしまうらしいです。
何という仕様なのでしょうか・・・。
簡単な解決法は「ダミー入力を与えてスクリーンセーバーを起動させない」
コードとしてはこんな感じでしょうか。
#define TIMERID_STOPSCREENSAVER 1 #define TIMERINTERVAL_STOPSCREENSAVER 50000 ・・・ SetTimer(hWnd,(UINT_PTR)TIMERID_STOPSCREENSAVER,TIMERINTERVAL_STOPSCREENSAVER,NULL); ・・・ LRESULT WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) { switch(uMsg){ case WM_TIMER: if(wParam == TIMERID_STOPSCREENSAVER){ INPUT inputinfo; inputinfo.type = INPUT_MOUSE; inputinfo.mi.dx = 0; inputinfo.mi.dy = 0; inputinfo.mi.mouseData = 0; inputinfo.mi.dwFlags = MOUSEEVENTF_MOVE; inputinfo.mi.time = 0; inputinfo.mi.dwExtraInfo = GetMessageExtraInfo(); ::SendInput(1,&inputinfo,sizeof(inputinfo)); return 0; } break; ・・・ } return DefWindowProc(hWnd,uMsg,wParam,lParam); }
わかりやすく言うと、
- 50秒ごとにタイマ割り込みをウィンドウプロシージャに発行(この50秒はスクリーンセーバーの最小起動時間の1分より少し短い時間、という意味)
- 割り込みがあった段階で強制的にWM_MOUSEMOVEを発行
- ただし、マウスカーソル位置はそのときの前の位置から動いていない
- Windows側はWM_MOUSEMOVEを発行することで「マウスが動いた」ことになり、スクリーンセーバーは起動しない
という動作を行います。
本来は50秒間入力がなかったことを確認した方がよいのですが、簡易コードなのでこんな感じです。
Windowsに入力処理を発行するSendInput関数に対するパラメータは自身のプログラムが処理しないものなら何でもいいと思います。
(Enterキーを発行するようにSendInput関数に指示をすると状態が変わる、という場合はそのパラメータを使うと大変なことになります)
このサンプルではマウスカーソル移動(ただし、位置は変更されない)を使っています。
ほかのスクリーンセーバーを起動させないゲームなどではどうやっているのでしょうか?
こういう対策がちゃんとされているのかちょっと知りたいな、と思いました。
記事数が300件越え
です。ここまで来るのに約三年ですか・・・・。いろいろなことがありましたね~と感慨深いものがあります。
ただの日記ネタとプログラムネタをいり混ぜながら書いていますが、日記の方は・・・、まあ検索で引っかかるような記事ではないですが。
これが政治ネタとかやり始めるといろいろとコメントが書き込まれるようになるのでしょうが・・・。今ならおもしろそうだと思っています。