モニターの電源をオフにするアプリを開発していて、かなり簡単にまとめる事ができたので備忘録的な記事として投稿します。
結局Win32API
この辺りの、オペレーティングシステム(Windows)機能になると.NETアセンブリでは用意されていなく、昔ながらのWin32APIでの呼び出しで解決できました。
また、モニターの電源関連の専用関数も容易されていないようで、SendMessage関数のパラメタ受け渡しでモニターの電源関連の処理をWindowsがしてくれます。
SendMessage関数
VC++での開発を離れてから、久しく使わなくなってしまいましたがWindowsに仕事をさせるための基礎中の基礎関数です。
LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
hWnd:1 つのウィンドウのハンドルを指定します。このウィンドウのウィンドウプロシージャがメッセージを受信します。HWND_BROADCAST を指定すると、この関数は、システム内のすべてのトップレベルウィンドウ(親を持たないウィンドウ)へメッセージを送信します。無効になっている所有されていないウィンドウ、不可視の所有されていないウィンドウ、オーバーラップされた(手前にほかのウィンドウがあって覆い隠されている)ウィンドウ、ポップアップウィンドウも送信先になります。子ウィンドウへはメッセージを送信しません。
Msg:送信するべきメッセージを指定します。
wParam:メッセージ特有の追加情報を指定します。
lParam:メッセージ特有の追加情報を指定します。
戻り値
メッセージ処理の結果が返ります。この戻り値の意味は、送信されたメッセージにより異なります。
このSendMessage関数に、Msg、WM_SYSCOMMAND(0x112)、wParamにSC_MONITORPOWER(0xf170) を受け渡し、lParamで電源オンとオフを切り替えることができます。
コードサンプル
クラス化するとこんな感じのコードになりました。
MonitorPower.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace MonitorPower
{
class Monitor
{
const int SC_MONITORPOWER = 0xf170;
const int WM_SYSCOMMAND = 0x112;
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(int hWnd, uint Msg, int wParam, int lParam);
public static void PowerSave()
{
//省電力
SendMessage(-1, WM_SYSCOMMAND, SC_MONITORPOWER, 1);
}
public static void PowerOff()
{
//モニター停止
SendMessage(-1, WM_SYSCOMMAND, SC_MONITORPOWER, 2);
}
public static void PowerOn()
{
//モニター復帰
SendMessage(-1, WM_SYSCOMMAND, SC_MONITORPOWER, -1);
}
}
}
実際に使う場合には、こんな感じで呼び出すことが出来ます。
Private void Func()
{
MonitorPower.Monitor.PowerOff();
}
実際にコードを実行すると、lParamに2を指定するとモニターの電源が切れます。マウスやキーボードで復帰するのはWindows側がやってくれるので考慮することはありませんでした。
lParamに1を指定すると省電力になるとリファレンスにありますが省電力設定がないデスクトップで試してみましたが、何も起きませんでした。
オペレーティングシステムの処理をする場合には、今でもWin32APIは現役のようです。
SendMessageから処理が戻らない場合
コメントをいただいた内容をページに追記しています。
SendMessageを呼び出すて、モニターを復帰させると呼び出し元のアプリが応答不可になる場合があるようです。
Win32 の SendMessage関数は、”メッセージを処理が終了後に制御を返す” という仕様なので、メッセージ処理が終了しない場合にアプリが停止する要因になってしまいます。
Win32で同様なメッセージ処理をする関数で PostMessage関数があり、こちらは “メッセージを処理の終了を待たずに制御を返す” 仕様なので、処理の有無に関わらすアプリが依存して応答しなくなることを回避できます。
引数などはSendMessage関数と同じなのでWin32のインポート部分を次の様に変更して
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(int hWnd, uint Msg, int wParam, int lParam);
呼び出す部分をSendMessageからPostMessageに変更します。
//SendMessage(-1, WM_SYSCOMMAND, SC_MONITORPOWER, 2);
PostMessage(-1, WM_SYSCOMMAND, SC_MONITORPOWER, 2);
まとめ
今回は、短い記事ですが、C#のコードからモニター(ディスプレイ)の電源をオフにする方法を書きました。一定時間経過後ではなく、即時にオフにしたいような場合に使えるアプリを作るのに使えるかもしれません。
Windowsのディスプレイ(モニター)の電源を制御したい方の参考になれば幸いです。
スポンサーリンク
最後までご覧いただき、ありがとうございます。
早速の返信ありがとうございます。
試してみました。
[PowerOff]内の[SendMessage]→[PostMessage]にする事で復帰後に応答しなくなるという不具合はなくなりました。
まさにビンゴです。解決できました。ありがとうございます。
ちなみに不具合が出たマシンはWindows11 Proでした。
ご連絡ありがとうございます。
休止やスタンバイなど電源関連の不具合は、問題点が分からない部分が多いので心配していましたが
解決できて安心しました。
貴重な情報をいただき、ありがとうございました。
こちらのコードそのままにWindowsFoamで使用してみたのですが、
private void button1_Click(object sender, EventArgs e)
{
WindowsFormsApp1.Monitor.PowerOff();
}
これを実行するとモニターがオフになるのはなるのですが、復帰後アプリが応答なしになります。
どういった理由が考えられますでしょうか?
コメントありがとうございます。
直接の原因までは分かりませんが、アプリが復帰しないという現象から考えると、Win32内の[SendMessage]関数から制御が戻ってきていない可能性が考えられます。
[SendMessage]は、”メッセージを処理が終了後に制御を返す” 関数なので、代わりに “メッセージを処理の終了を待たずに制御を返す” [PostMessage]関数が使えるかもしれません。
引数は、SendMessageと同じなのでMonitorクラスにPostMessageのインポート文を追加して[PowerOff]内の[SendMessage]関数の呼び出しを[PostMessage]関数に変更すると改善するかもしれません。
[System.Runtime.InteropServices.DllImport(“user32.dll”, CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr PostMessage(int hWnd, uint Msg, int wParam, int lParam);
public static void PowerOff()
{
//モニター停止
//SendMessage(-1, WM_SYSCOMMAND, SC_MONITORPOWER, 2);
PostMessage(-1, WM_SYSCOMMAND, SC_MONITORPOWER, 2);
}
念のため、ブログに記載のコードを試してみましたが私の環境(Windows10/11)でモニターを復帰後でもアプリは動作してくれてました。
よろしくお願いいたします。