モバイル PC の電源管理
Dr. Neil Roodyn
June 2005
適用対象:
Microsoft Windows XP Tablet PC Edition
要約: この記事では、Microsoft Windows XP で実行しているモバイル PC アプリケーションの電源管理に関するトピックを紹介します。このプラットフォームで利用可能な API を使用して、モバイル PC でアプリケーションをより効率的に機能させる方法を調査します。この記事には英語のページへのリンクも含まれています。
目次
はじめに
電源の重要性
電源を把握する
電源の状態が変化したときに通知を受け取る
使用可能な電力を効率的に使用する
電源ツール
省電力イベントの処理
電源管理機能のまとめ
ソリューションをテストする
まとめ
著者について
はじめに
この記事では、Microsoft .NET Framework アプリケーションで電源を検出したり、アプリケーションに電源の状態が変化したことを通知したりできるようにする方法について説明します。また、アプリケーションで電力が必要なときに、消費する電力を抑える技法についても説明します。
この記事では、.NET Framework 1.1 で実行する C# のサンプル コードも提供しています。このサンプルは、2 つのプロジェクトで提供されています。1 つのプロジェクトには NUnit 単体テストが含まれていますが、もう 1 つのプロジェクトには含まれていません。NUnit をインストールしていない場合は、http://www.nunit.org/ からダウンロードできます。NUnit を使用しない場合は、ソリューションから "テストを伴う電源管理" プロジェクトを削除できます。
電源の重要性
コンピュータの処理能力やメモリは増加していますが、モバイル デバイスのバッテリ寿命は同じ割合では進歩していません。現在、大部分のモバイル PC に展開されているバッテリ テクノロジは、ここ数年の間に大きな変化は見られていません。ユーザーがバッテリを交換したり、AC 電源コンセントに接続したりせずに、ラップトップや Tablet PC を使用して作業できる時間が長くなると、ユーザーの操作性は劇的に向上します。
当然のことですが、バッテリ寿命はデバイスで実行されるソフトウェアに大きく左右されます。グラフィックスやオーディオを集中的に使用する操作など、特定のタスクを実行すると、単に画面にウィンドウを表示する場合よりもはるかに速い速度でバッテリが消耗します。そのため、ソフトウェア開発者は、ユーザーがモバイル PC で作業しているときにバッテリ寿命を延ばすことができるアプリケーションを作成する必要があります。
この記事のガイダンスに従うと、モバイル PC でアプリケーションを実行したときのユーザーの操作性を向上させることができます。
電源を把握する
まず、バッテリが装着されているコンピュータでアプリケーションが実行されているのかどうかを判断します。次に、現在コンピュータが AC 電源に接続されているのか、またはバッテリ電源で実行されているのかを判断します。これらの 2 つの事項を確認したら、適宜、アプリケーションの動作を調整します。
.NET Framework 1.x では、この情報が提供されません。代わりに、Platform Invoke (P/Invoke) メカニズムを使用して、Win32 の GetSystemPowerStatus 関数を呼び出す必要があります。この関数では、SYSTEM_POWER_STATUS 構造体を指すポインタを取得します。
これらの関数や構造体を、次のようにマネージ コードで宣言します。
[StructLayout(LayoutKind.Sequential)]
public class SYSTEM_POWER_STATUS
{
public byte ACLineStatus;
public byte BatteryFlag;
public byte BatteryLifePercent;
public byte Reserved1;
public Int32 BatteryLifetime;
public Int32 BatteryFullLifetime;
}
public enum ACLineStatus : byte
{
Battery = 0,
AC = 1,
Unknown = 255
}
[FlagsAttribute]
public enum BatteryFlag : byte
{
High = 1,
Low = 2,
Critical = 4,
Charging = 8,
NoSystemBattery = 128,
Unknown = 255
}
[DllImport("Kernel32.DLL", CharSet = CharSet.Auto,
SetLastError = true)]
private extern static
bool GetSystemPowerStatus(
SYSTEM_POWER_STATUS SystemPowerStatus);
SYSTEM_POWER_STATUS 構造体を .NET Framework マネージ クラスとして宣言していることに注意してください。.NET Framework のクラスは必ず参照渡しで渡されます。そのため、当分はポインタの処理に関連する問題が生じません。次のサンプル コードの ReportPowerStatus メソッドは、この関数を呼び出し、SYSTEM_POWER_STATUS クラスから電源の詳細情報を取得する方法を示しています。
protected virtual string ReportPowerStatus()
{
string status = string.Empty;
SYSTEM_POWER_STATUS powerStatus;
powerStatus = new SYSTEM_POWER_STATUS();
bool result = GetSystemPowerStatus(powerStatus);
if (result)
{
StringBuilder statusMsg = new StringBuilder();
statusMsg.Append("AC 電源の状態 : ");
if (ACLineStatus.Battery ==
powerStatus.ACLineStatus)
{
statusMsg.Append("オフライン");
}
else if (ACLineStatus.AC ==
powerStatus.ACLineStatus)
{
statusMsg.Append("オンライン");
}
else
{
statusMsg.Append("不明");
}
statusMsg.Append(System.Environment.NewLine);
statusMsg.Append("バッテリの状態 : ");
statusMsg.Append(powerStatus.BatteryFlag.ToString());
statusMsg.Append(System.Environment.NewLine);
statusMsg.Append("バッテリの充電状態 : ");
if (255 == powerStatus.BatteryLifePercent)
{
statusMsg.Append("不明");
}
else
{
statusMsg.Append(
powerStatus.BatteryLifePercent.ToString());
statusMsg.Append("%");
}
statusMsg.Append(System.Environment.NewLine);
statusMsg.Append("バッテリの残り寿命 : ");
if (-1 == powerStatus.BatteryLifetime)
{
statusMsg.Append("不明");
}
else
{
statusMsg.Append(
powerStatus.BatteryLifetime.ToString());
statusMsg.Append(" 秒");
}
statusMsg.Append(System.Environment.NewLine);
statusMsg.Append("完全充電されたバッテリの寿命 : ");
if (-1 == powerStatus.BatteryFullLifetime )
{
statusMsg.Append("不明");
}
else
{
statusMsg.Append(
powerStatus.BatteryFullLifetime.ToString());
statusMsg.Append(" 秒");
}
statusMsg.Append(System.Environment.NewLine);
Console.Out.WriteLine(statusMsg);
status = statusMsg;
}
else
{
Console.Out.WriteLine("電源の状態を取得できませんでした");
}
Console.Out.WriteLine();
return status;
}
注: デバイスによっては、BatteryLifetime フィールドと BatteryFullLifetime フィールドが使用できないことがあります。そのため、バッテリの現在の状態を調べる場合は、ACLineStatus フィールドと BatteryLifePercent フィールドを使用することをお勧めします。
コンピュータがバッテリ電源で実行されているかどうかを把握する作業は、最初の手順にすぎません。次は、コンピュータに搭載されているデバイスに現在アクセスできるかどうかを判断する必要があります。多くのコンピュータには、非アクティブな状態が一定期間続くと、画面やハード ディスクの電源がオフになる機能が備わっています。このように電源がオフになったデバイスを使用しないようにすることで、バッテリ寿命を延ばすことができます。ほとんどの場合、アプリケーションで、電源がオフになったデバイスを使用するには、デバイスをアクティブにする処理が発生するのを待機する必要があります。
現在、.NET Framework 1.x ではデバイスの状態に関する情報も提供されません。この情報を取得するには、GetDevicePowerState という別の Win32 関数を呼び出す必要があります。GetDevicePowerState 関数では、情報が必要なデバイスへのハンドルと、デバイスがアクティブかどうかを示すブール型のフラグの 2 つのパラメータを受け取ります。GetDevicePowerState 関数は、次のコード例で示すように C# にインポートできます。
[DllImport("Kernel32.DLL", CharSet = CharSet.Auto,
SetLastError=true)]
private extern static
bool GetDevicePowerState(
IntPtr hDevice,
out bool fOn);
GetDevicePowerState 関数をインポートしたら、この関数を呼び出してデバイスの現在の状態を確認します。ここで例として使用している関数 ReportDiskStatus は、ディスクの状態を取得する方法を例示しています。ReportDiskStatus 関数では、アセンブリ ファイルを使用して、アセンブリが実行されているディスクが現在アクティブであるかどうかを判断します。通常、アプリケーションには、アセンブリ ファイルが格納されているディスクの電源が現在オンになっているかどうかを確認するために使用できるファイル ハンドルが既に存在していると考えられます。
protected string ReportDiskStatus()
{
string status = string.Empty;
bool fOn = false;
Assembly assembly = Assembly.GetExecutingAssembly();
FileStream[] files = assembly.GetFiles();
if (files.Length > 0)
{
IntPtr hFile = files[0].Handle;
bool result = GetDevicePowerState(hFile, out fOn);
if (result)
{
if (fOn)
{
status = "ディスクは電源が入っていて回転しています";
}
else
{
status = "ディスクの電源はオフになっています";
}
}
else
{
status = "ディスクの状態を取得できませんでした";
}
Console.WriteLine(status);
}
return status;
}
注: GetDevicePowerState 関数を使用して、ディスプレイ デバイスに関する情報を取得することはできません。
電源に関する情報を取得したら、アプリケーションで使用できるクラスに情報をカプセル化できます。
アプリケーションでは、電源やデバイスの現在の状態に関する情報を保持するようになったので、次は、この状態が変化したときに通知を受け取るようにします。
電源の状態が変化したときに通知を受け取る
.NET Framework 1.x では、Microsoft.Win32 名前空間に属する SystemEvents クラスで PowerModeChanged イベントが用意されています。PowerModeChanged イベントでは、PowerModeChangedEventArgs オブジェクトが提供されます。このオブジェクトには、Mode という名前の、このトピックに関連のあるプロパティが含まれており、このプロパティは次の表に示す PowerModes 列挙体の 3 つの値のうちのいずれかと等しくなります。
表 1. PowerModes 列挙体の値
メンバ名 | 説明 |
---|---|
Resume | オペレーティング システムが再開されます。 |
StatusChange | オペレーティング システムの電源モードの状態が変化しました。これは、バッテリ電力の低下、バッテリの充電中、AC 電源からバッテリへの移行など、システムの電源状態が変化したことを示しています。 |
Suspend | オペレーティング システムが一時停止されます。 |
大部分のアプリケーションの場合、電源の状態に関して必要な情報はこれだけです。
StatusChange イベントでは、(前のセクションで説明したように) GetSystemPowerStatus 関数を呼び出して、新しい電源の状態を調べることができます。その後、状態の変化に応じてアプリケーションの動作を調整できます。コンピュータがバッテリ電源で実行されるようになった場合、アプリケーションでは、バッテリ寿命を保てるように操作モードを変更する必要があります。次のセクションでは、この処理を行う方法について説明します。
コンピュータが休止状態またはスタンバイ状態の場合、イベントは Suspend モードで発生します。アプリケーションでは、すべての重要なデータをできるだけ効率的に保存する必要があります。この処理については、次のセクションで詳しく説明します。
また、この記事の後半では、イベントが Resume モードで発生したときに、そのイベントを処理する方法についても詳しく説明します。このイベントは、コンピュータの休止状態またはスタンバイ状態が解除されたときに発生します。手短に説明すると、このイベントが発生したときには、保存したすべてのデータを読み込み、アプリケーションをできるだけ早く対話できる状態にする必要があります。
PowerModeChanged イベントには、Windows プラットフォームで提供されるイベントのサブセットが用意されています。すべてのイベントを受け取るには、WM_POWERBROADCAST メッセージをトラップする必要があります。PowerModeChanged イベントでは、大部分のアプリケーションに必要なすべての情報が提供されます。
WM_POWERBROADCAST メッセージの wParam には、イベント情報が含まれています。使用できるイベントは 13 個あります。WM_POWERBROADCAST メッセージとイベントは、次のようにマネージ コードで宣言して使用できます。
const int WM_POWERBROADCAST = 0x0218;
const int PBT_APMQUERYSUSPEND = 0x0000;
const int PBT_APMQUERYSTANDBY = 0x0001;
const int PBT_APMQUERYSUSPENDFAILED = 0x0002;
const int PBT_APMQUERYSTANDBYFAILED = 0x0003;
const int PBT_APMSUSPEND = 0x0004;
const int PBT_APMSTANDBY = 0x0005;
const int PBT_APMRESUMECRITICAL = 0x0006;
const int PBT_APMRESUMESUSPEND = 0x0007;
const int PBT_APMRESUMESTANDBY = 0x0008;
const int PBT_APMBATTERYLOW = 0x0009;
const int PBT_APMPOWERSTATUSCHANGE = 0x000A;
const int PBT_APMOEMEVENT = 0x000B;
const int PBT_APMRESUMEAUTOMATIC = 0x0012;
APMSUSPEND、APMRESUMESUSPEND、および APMPOWERSTATUSCHANGE イベントは、それぞれ .NET Framework の PowerModeChanged イベントの Suspend、Resume、および StatusChange イベントに相当します。
WM_POWERBROADCAST メッセージをトラップするには、いくつかの方法があります。メイン アプリケーション ウィンドウが表示される場合は、次のようにフォームの WndProc メソッドをオーバーライドできます。
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (WM_POWERBROADCAST == m.Msg)
{
// 状態を確認し、それに応じて動作を変更します。
}
}
ただし、アプリケーションがコンソール アプリケーションまたはウィンドウが表示されない Windows Service である場合、上記の方法は使用できません。このような場合は、アプリケーション メッセージ ループにフィルタを追加することが最善の方法です。この操作を行うには、IMessageFilter を実装するクラスを宣言します。次の例では、コンソールにイベントを出力します。
public class PowerMessageFilter : IMessageFilter
{
protected void ReportPowerChange(int reason)
{
string report = string.Empty;
switch (reason)
{
case PBT_APMQUERYSUSPEND:
report = "アクセス許可の中断を要求します。";
break;
case PBT_APMQUERYSTANDBY:
report = "アクセス許可の休止を要求します。";
break;
case PBT_APMQUERYSUSPENDFAILED:
report = "中断要求が拒否されました。 ";
break;
case PBT_APMQUERYSTANDBYFAILED:
report = "休止要求が拒否されました。";
break;
case PBT_APMSUSPEND:
report = "システムの操作を中断しています。";
break;
case PBT_APMSTANDBY:
report = "システムはスタンバイ状態です。";
break;
case PBT_APMRESUMECRITICAL:
report =
"重大なエラーにより中断された操作を再開しています。";
break;
case PBT_APMRESUMESUSPEND:
report = "中断した操作を再開しています。";
break;
case PBT_APMRESUMESTANDBY:
report = "スタンバイ状態の操作を再開しています。";
break;
case PBT_APMBATTERYLOW:
report = "バッテリの残量が少なくなっています。";
break;
case PBT_APMPOWERSTATUSCHANGE:
report = "電源の状態が変化しました。";
break;
case PBT_APMOEMEVENT:
report = "OEM で定義されているイベントが発生しました。";
break;
case PBT_APMRESUMEAUTOMATIC:
report =
"イベントにより操作が自動的に再開しました。";
break;
}
Console.Out.WriteLine(report);
}
public bool PreFilterMessage(ref Message m)
{
if (WM_POWERBROADCAST == m.Msg )
{
Console.Out.WriteLine("電源ブロードキャストを受信しました。" );
int reason = m.WParam.ToInt32();
ReportPowerChange(reason);
}
return false;
}
}
PowerMessageFilter クラスを使用するには、次のように Application クラスにフィルタを追加します。
filter = new PowerMessageFilter();
Application.AddMessageFilter(filter);
電源の状態変化が通知されたとき、アプリケーションでは、使用可能なバッテリの寿命を延ばすために適切な処理を行う必要があります。次のセクションでは、アプリケーションを開発するときに考慮が必要な点について説明します。
使用可能な電力を効率的に使用する
ソフトウェアの動作は、CPU、ディスプレイ、ディスク ドライブ、ネットワーク ポート (特に、ワイヤレス ネットワークの場合)、およびコンピュータに接続されている他のデバイスによる電力消費量に直接影響します。モバイル PC で実行するソフトウェアを構築するときは、アプリケーションがこれらのリソースとどのように対話するかを考慮してください。コンピュータがバッテリ電源で実行されている場合、これらのリソースの使用を最小限に抑える必要があります。
モバイル PC のバッテリ寿命はソフトウェアに大きく左右されるので、エンド ユーザーの操作性を大幅に向上させるには、アプリケーションが実行されているコンピュータで使用可能な電力を考慮することが重要です。
グラフィックスを集中的に使用する操作やオーディオ操作など、明らかに電力を消費する操作は多数あり、このような操作は回避するようにします。また、前述の操作ほど明白ではありませんが、他にもバッテリ寿命に大きな影響を与える操作が多数あります。ディスクやネットワークへのアクセスが、その一例です。また、ポーリング タスクやループ タスクを実行するバックグラウンド スレッドもバッテリ寿命に影響します。
最も効率的なアプリケーションとは、バッテリのみで電源が供給されているコンピュータで実行されている場合には、業務上重要な操作しか実行しないアプリケーションです。バックグラウンドでのスペル チェック、アプリケーションの更新プログラムやヘルプのオンライン検索、オーディオ フィードバック、グラフィックスを集中的に使用するフィードバック、およびデータベースのポーリングなどのタスクは、すべて無効にすることができます。また、コンピュータがバッテリのみの電力で実行されていることを認識している場合は、増分保存などの他のタスクの実行頻度を下げることをお勧めします。
アプリケーションで、このようなタスクまたは同様のタスクを実行する場合、そのタスクがエンド ユーザーにとってどの程度重要であるかを考慮する必要があります。また、情報を繰り返し要求すると、プロセッサ (およびその他の必須デバイス) がビジー状態のままになり、プロセッサの使用率は減少せず、電力消費量も抑えることができないので、ポーリング操作は行わないようにします。
アプリケーション設計者と協力して、電力を消耗するタスクの実行コストとバッテリ寿命が短くなる場合にエンド ユーザーに与える影響のトレードオフについて、業務上の意思決定を行う必要があります。
電源ツール
特定の機能の電力消費量を知るのに役立つ多数のツールがあります。前のセクションで説明した業務上の意思決定に沿って、これらのツールを使用します。たとえば、アプリケーションの主機能がオーディオ プレイヤーである場合、アプリケーションのオーディオ機能を終了してしまっては意味がありません。多くの場合、意思決定はそれほど明確なものにはなりません。アプリケーションに機能を実装したら、電源ツールを使用してその機能の電力消費量を評価できます。
この用途に使用できる代表的なツールは、パフォーマンス モニタ (perfmon.exe) です。パフォーマンス モニタは、システム全体の利用状況や、特定のプロセスの利用状況を測定する場合に役立ちます。また、.NET Framework を使用すると、PerformanceCounter クラスを使用して、独自のカウンタを容易にパフォーマンス モニタに追加することもできます。この方法により、特定の操作で使用されるリソース量が予想よりも多いか少ないかを確認できます。
他には、Intel 社製の 2 つのツールがあります。VTune を使用すると、最も多くのリソースを消費しているスレッドやメソッドを特定できます。予想外のメソッドがリソースを使用していたり、プロセッサをビジー状態にさせている場合は多々あります。VTune は、このようなメソッドを見つけるのに役立ち、コンピュータの現在の電源状態に基づいて、このようなメソッドを最適化できます。
Intel 社では、Power Evaluation Tool (電源評価ツール) も提供しています。このツールは、バッテリで電源が供給されているコンピュータでアプリケーションを実行しているときに、アプリケーションの電力消費量を測定するのに役立つコマンド ライン ツールです。Power Evaluation Tool は、同じコンピュータで 2 つのバージョンのアプリケーションを実行して比較する場合に使用できます。このツールを使用すると、特定の機能を最適化して、最適化されていない前のバージョンとの電力消費量を比較することができます。また、Power Evaluation Tool を使用して、アプリケーションを 2 つの異なる電源設定で実行した場合の動作を比較することもできます。
これらの Intel 社製のツールは、https://go.microsoft.com/fwlink/?LinkId=49476 で Intel Software Developer Dispatch の登録を行うと入手できます。
これらのツールを使用する場合、テストの実行時間を考慮することは重要です。適切な作業負荷 (アプリケーションの動作) で長い時間テストを実行すると、より詳細かつ正確な情報を入手できます。実行時間が短いテストでは、あまり有用な情報は得られません。1 ~ 2 時間テストを実行できるコンピュータをセットアップし、テスト結果を収集します。このようなテストを行うと、アプリケーションを実行した場合の電力消費量をさらに詳しく把握できるようになります。
省電力イベントの処理
これまでのセクションでは、アプリケーションの実行時にバッテリ寿命を保つことに注目してきました。ただし、考慮が必要な点が他にもう 1 つあります。それは、コンピュータで操作が中断されたときにアプリケーションがどのように動作するかということです。これについては、主に次の 2 つのシナリオについて考慮する必要があります。
- コンピュータが一定期間アイドル状態になっている場合、アクティブな電源設定により、ハードウェアがスタンバイ状態または休止状態のいずれかになるように指示されることがあります。
- ラップトップ コンピュータを閉じたり電源ボタンを押したりするなどの操作をユーザーが実行すると、コンピュータの操作が中断されます。
アプリケーションに中断要求が通知されたときには、考慮が必要となる重要な点がいくつかあります。アプリケーションでは、実行中の処理をできるだけ早く停止し、すべての状態情報を保持する必要があります。
どのような状況でも、アプリケーションで、ユーザー入力を要求してはいけません。ラップトップ コンピュータを閉じたユーザーが 5 時間後に再びラップトップ コンピュータを開いたときに、ユーザーが一番困るのは、アプリケーションでユーザー入力を待機したために、バッテリが消耗されてしまっているという状況でしょう。これに該当するのは、文書を保存するかどうかを確認するメッセージ ボックスが表示される場合などです。ユーザーには何も確認せず、アプリケーションの状態を保存し、コンピュータが中断状態になることを妨げる可能性のあるすべての処理を停止する必要があります。
また、アプリケーションでは再開イベントに対応し、保存されたすべての状態を再度読み込んで、中断されたすべての操作を開始する必要もあります。アプリケーションは、中断状態から再開したとき、エンド ユーザーにはアプリケーションでこれまで何も発生しなかったように見えるようにする必要があります。アプリケーションは、中断された時点の状態をできる限り維持する必要があります。場合によっては、この処理は言葉で言うほど簡単ではないことがあります。接続されているデバイスは、コンピュータが中断されてから再開されるまでの間に変更されている可能性があるので、再開時にリムーバブル ドライブやネットワーク接続がまだ使用できるとは想定できないことに注意してください。
アプリケーションがネットワーク ドライブから実行されているか、またはアプリケーションで情報の更新処理が行われていた場合、そのアプリケーションでは再開時に問題が発生する可能性があります。ユーザーに状態を復元できなかったことを通知するのは、最後の手段としてください。アプリケーションの実行を続けられる何らかの方法がある場合は、機能が制限されても、その方法を使用してアプリケーションの実行を続ける必要があります。
再開時には、コンピュータの電源状態が中断モードになったときとは異なる可能性があることに注意してください。たとえば、コンピュータは、バッテリ電源で実行されているときに中断し、AC 電源に接続されているときに再開することがあります。つまり、アプリケーションでは再開時に電源状態を確認し、それに応じて動作を調整する必要があります。
中断要求を拒否してはいけません。アプリケーションをすばやく中断し、再開後には環境が変化している可能性があることを考慮して、アプリケーションを再開できるようにする必要があります。アプリケーションが中断時に実行するタスクには、重要なすべての作業の保存、すべてのネットワーク処理の停止、開かれているすべてのファイルを閉じるタスク (特に、ファイルがローカル ディスク上にない場合は重要です)、および Bluetooth デバイスや USB デバイスなどの外部デバイスとの対話の停止などがあります。
ただし、コンピュータが一定時間アイドル状態であっても (ユーザーによる操作が行われていない場合でも)、アプリケーションで中断してはいけない操作が実行されている場合はどうしたらよいでしょうか。これに該当するのは、ムービーの再生やプレゼンテーション アプリケーションなどです。アプリケーションでは、コンピュータのアイドル カウンタから中断操作が要求されることを阻止することができます。ただし、アプリケーションでは、ユーザーからのアプリケーションの中断要求を阻止してはいけません。
コンピュータでタイムアウトが発生したり、中断操作が要求されたりしないようにするには、Win32 の SetThreadExecutionState 関数を使用します。この関数は、どうしても使用する必要があるとき以外は使用しないでください。コンピュータでバッテリ電源が節約されなくなったり、無駄にバッテリ寿命を短くするようなことは望まないためです。また、アプリケーションで重要なタスクの実行が終了したら、実行状態を元に戻すようにします。このようにすると、オペレーティング システムでは、中断操作を要求して、再びバッテリ寿命を保つことができるようになります。
SetThreadExecutionState 関数では、名前のとおり、スレッドごとに実行状態を設定します。戻り値は、呼び出し側のスレッドの実行状態で、コンピュータの状態を示すものではありません。Windows オペレーティング システムでは、特定の実行状態を要求したスレッドを追跡し、その状態に応じて動作します。そのため、2 つのアプリケーションで SetThreadExecutionState 関数を呼び出してディスプレイの電源を入れた状態を維持した場合、これらの両方のアプリケーションからディスプレイが必要でなくなったことが通知されるまで、ディスプレイの電源は入ったままの状態になります。
つまり、SetThreadExecutionState 関数から返される値を格納しなくてもよい場合があります。オペレーティング システムに制御を戻さない場合に必要なことは、SetThreadExecutionState(ES_CONTINUOUS) を呼び出すだけです。要求されているスレッドとリソースはオペレーティング システムで監視されているので、アプリケーションがクラッシュし、スレッドが存在しなくなると、存在しなくなったスレッドから以前に送信されたすべての要求が削除されます。
SetThreadExecutionState 関数と EXECUTION_STATE 型は、次のようにして C# にインポートできます。
[FlagsAttribute]
public enum EXECUTION_STATE :uint
{
ES_SYSTEM_REQUIRED = 0x00000001,
ES_DISPLAY_REQUIRED = 0x00000002,
// 古いフラグは使用しないでください。
// ES_USER_PRESENT = 0x00000004,
ES_CONTINUOUS = 0x80000000,
}
[DllImport("Kernel32.DLL", CharSet = CharSet.Auto,
SetLastError = true)]
private extern static
EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE state);
次に、私が作成した電源管理関数をカプセル化する PowerClass のメソッドを示します。
public EXECUTION_STATE SetExecutionState(EXECUTION_STATE state)
{
EXECUTION_STATE oldState = SetThreadExecutionState(state);
return oldState;
}
この記事で使用する VisualizerForm メソッドでは、フォームが最大化されているときには必ず PowerClass メソッドを使用し、画面に何も表示されなくなったり、コンピュータが中断状態になったりすることを回避します。フォームが最大化されているときは、コンピュータがバッテリ電源で実行されている場合でも、必ずアニメーションが実行されます。
次のサンプルでは、以前の実行状態を追跡し、完了時に元の状態に戻す方法を例示します。
private void VisualizerForm_SizeChanged(object sender, EventArgs e)
{
if (null == power)
{
return;
}
if (this.WindowState == FormWindowState.Maximized)
{
previousState = power.SetExecutionState(
PowerClass.EXECUTION_STATE.ES_DISPLAY_REQUIRED |
PowerClass.EXECUTION_STATE.ES_CONTINUOUS);
animationTimer.Enabled = true;
}
else
{
power.SetExecutionState(
PowerClass.EXECUTION_STATE.ES_CONTINUOUS);
if (power.RunningOnBattery)
{
animationTimer.Enabled = false;
}
}
}
PowerClass オブジェクトと VisualizerForm オブジェクトがどのように対話するのかの詳細については、次の「電源管理機能のまとめ」を参照してください。
電源管理機能のまとめ
この記事で説明した電源管理機能を使用する方法についてのガイダンスを提供するために、このセクションでは、コピーまたは機能強化して使用できるアーキテクチャ パターンを紹介します。
PowerClass では、すべての Win32 関数をカプセル化し、メソッド、プロパティ、および他のクラスで電源の状態や発生したイベントを調べるために使用できるイベントを提供します。
IPowerAware インターフェイスでは、電源の状態を認識できるクラスとして見なされるためにサポートする必要がある RunOnBattery、RunOnAC、Suspend、および Resume という 4 つのメソッドを定義します。このインターフェイスは、Beeper、OnlineUpdater、および VisualizerForm という 3 つのクラスで実装されています。
PowerAwareController クラスでは、IPowerAware インターフェイスを実装するクラスの一覧を管理します。このクラスでは、PowerClass を使用して、電源状態の変化を認識し、IPowerAware インターフェイスを実装するオブジェクトに通知します。図 1 の UML 図はこの構造を理解するのに役立ちます。このモデルをコードで使用する場合に必要な作業は、IPowerAware インターフェイスを実装するクラスを作成し、そのクラスを PowerAwareController のインスタンスに追加することだけです。
図 1. PowerAwareController クラスの構造
ソリューションをテストする
(NUnit を使用した) 単体テストを行って、クラスで IPowerAware インターフェイスがサポートされるかどうかをテストできます。Beeper クラスでは一連のビープ音が繰り返し再生されます。また、このクラスには BeeperTests クラスが付随しています。このテスト クラスには、さまざまな電源状態での Beeper オブジェクトの動作を検証するためのテストが含まれています。
電源の変化をシミュレートするために、MockPowerClass を作成しました。この MockPowerClass を使用すると、テスト コードで電源状態を変化させ、PowerAwareController クラスで受け取ることができるイベントを発生させることができます。その後、PowerAwareController クラスでは、管理している PowerAwareObjects の一覧に含まれている各オブジェクトに状態の変化を通知できます。
PowerAwareController クラスをテストするために、MockPowerAwareClass 型を宣言しました。この MockPowerAwareClass 型を使用すると、MockPowerClass クラスに加えられた変更に基づいてオブジェクトの状態が設定されているかどうかをテストで検証できます。たとえば、次の TransitionBatteryToAC テスト メソッドでは、power オブジェクトで RunOnBattery メソッドを実行した後に、MockPowerAware オブジェクトのプロパティを確認しています。
[Test]
public void TransitionBatteryToAC()
{
power.RunOnBattery();
Assert.IsTrue(aware.runningOnBattery,
"MockPowerAware オブジェクトはバッテリ電源で実行されています");
power.RunOnAC();
Assert.IsFalse(aware.runningOnBattery,
"MockPowerAware オブジェクトは AC 電源で実行されています");
}
まとめ
この記事では、アプリケーションで消費される電源について考慮する必要がある理由と、アプリケーションが実行されているコンピュータのバッテリ寿命を延ばすための処置について説明しました。役立つ関数の多くは Win32 API に含まれていますが、そのような関数は容易にマネージ コード クラスにカプセル化し、.NET Framework アプリケーションで使用することができます。
ユーザーが一番困るのは、コンピュータがスタンバイ状態または休止状態であると考えているときに、コンピュータのバッテリが消耗していることであるという点に留意してください。ユーザーがこのような状況に直面するのを回避するには、アプリケーションでは、常に中断要求にタイムリーに応答し、コンピュータの中断を妨げないようにする必要があります。
次に、省電力のアプリケーションを設計するときに考慮する必要がある事項の一部を示します。
- 電源を特定します。
- システムからの中断要求を妨げないようにします。
- システムの再開時、アプリケーションは円滑に実行される必要があります。
- システムの再開時、使用可能なデバイスと電源は変化していることがあります。
- 電源がバッテリの場合は、アプリケーションの電力消費量を減らします。ポーリング操作を回避し、重要でないワーカー スレッドは実行せず、不必要なデバイス (ディスク、ディスプレイ、Bluetooth、USB など) へのアクセスを回避します。
作成したクラスでは発生する可能性のあるすべての状態の変化についてテストし、ユーザーがアプリケーションをモバイル PC で実行したときに優れた操作性がもたらされるようにすることが重要です。
著者について
英国生まれの Dr. Neil は、さまざまなソフトウェア会社で仕事をしながら世界を旅しています。彼のお気に入りはオーストラリアで、南半球の夏季 (12 ~ 2 月) はシドニーの生活様式を楽しみながら、ソフトウェア開発チームの生産性向上を支援しています。また、毎年、北半球の夏季 (6 ~ 8 月) には北欧と米国の間を行き来して、ソフトウェア チームと仕事をしたり、彼自身の経験を綴ったりしています。Neil は持ち合わせたビジネス スキルと技術的スキルを仕事先の会社で活用し、顧客の満足を保証しています。
Neil の Web サイト (http://www.roodyn.com/) にアクセスすると、彼について詳しく知ることができます。また、Neil への電子メールは Neil@Roodyn.com まで英語でお送りください。