2006年03月18日

COM クライアント実装の道程 for TaskScheduler その8

いよいよ連載も大詰め。TaskTrigger について解説していきましょう。

その前にまずは大元の TASK_TRIGGER 構造体を解説しなければなりません。TASK_TRIGGER 構造体はこれまで述べたとおりタスクの起動するタイミング(トリガ)を記述する構造体で、この構造体一つであらゆるトリガを表現します。そのため、TASK_TRIGGER は以下のメンバを持ちます。

  • 自身がどの種類の起動タイプなのかを表す TASK_TRIGGER_TYPE 列挙体の値
  • その起動タイプに固有の値を持つ TRIGGER_TYPE_UNION 共用体

起動タイプというのは、つまりそのトリガが一回だけ有効なトリガなのか、週単位で実行するトリガなのか、ユーザのログオン時だけ発動するトリガなのか、などを表します。また起動タイプに依らない共通のメンバとして以下のものがあります。

  • そのトリガの有効期間の開始年月日と終了年月日
  • トリガの開始時間。ただし一部のイベント駆動タイプのトリガ起動タイプでは無視されます
  • そのトリガで起動したプログラムの持続時間と間隔
  • トリガの詳細を記述するための各種フラグ

わざわざ分けて書いたとおり、TASK_TRIGGER の肝は TRIGGER_TYPE_UNION にあると言えます。この共用体は日単位、週単位、月単位、月の週単位、の各起動タイプでそれぞれ別構造体を格納し、TASK_TRIGGER.TASK_TRIGGER_TYPE に応じたものを使用します。

さて、以上がTASK_TRIGGER の概要ですが、これはお世辞にも直感的とは言えません。なにより共用体が邪魔です。そこでここを整理し、直接 TaskTrigger にそれらをまとめてセットする SetAs... メソッドを用意することにします。ユーザは SetAs... メソッドを呼び出すだけで起動タイプを設定できます。このやり方は、ユーザが起動タイプを列挙できないと言うデメリットが存在しますが、いずれにせよ設定するには各起動タイプの詳細を知っている必要があるので問題にはならないでしょう。

また、TASK_TRIGGER が持つ日付や時間間隔のパラメータはいずれも整数値ですが、これらは整理して DateTime/TimeSpan に構成します。

そう言えば今まで共用体を扱ったことが無かった気がするので、今回はその解説に行を費やすことにしましょう。

共用体は、同じバイトデータを違う意味で捉えるための構造体の一種です。例えば 4 バイトのデータがあるとき、これを DWORD 一つとして見たり WORD 二つとして見たりするわけです。今回の題材である TRIGGER_TYPE_UNION は、同じバイトデータを四つの構造体のいずれかとして見ます。MSDN では次のような定義になっています

typedef union _TRIGGER_TYPE_UNION {
  DAILY Daily;
  WEEKLY Weekly;
  MONTHLYDATE MonthlyDate;
  MONTHLYDOW MonthlyDOW;
} TRIGGER_TYPE_UNION;

この四つの構造体はいずれも同じバイトデータを持つことになります。Daily メンバの一バイト目とMonthlyDOW メンバの一バイト目はどちらも TRIGGER_TYPE_UNION の一バイト目です。

今回の TASK_TRIGGER のように起動タイプによって与えるべき構造体が異なる場合、このような共用体を使えば、構造体の利点を持ちつつシンプルに格納することができます。

.NET では型がはっきりしていること、いざとなれば Object 型を使うことができることから共用体の必然性はほぼ無いですが、今回のようにアンマネージドとの相互運用においてはそう言うわけにもいきません。C#/VB.NET では、共用体は構造体の構文を利用して定義します。通常の構造体と違うのは、型宣言部に StructLayout 属性で LayoutKind.Explicit を指定すること、各フィールドに FieldOffset 属性で共用体先頭からのバイトオフセットを明示的に指定することです。例えば TRIGGER_TYPE_UNION なら次のような感じになります。

[StructLayout(LayoutKind.Explicit)]
public struct TriggerTypeUnion {
  [FieldOffset(0)] Daily Daily;
  [FieldOffset(0)] Weekly Weekly;
  [FieldOffset(0)] MonthlyDate MonthlyDate;
  [FieldOffset(0)] MonthlyDayOfWeek MonthlyDayOfWeek;
}

TRIGGER_TYPE_UNION は独立した共用体のため FieldOffset が全部 0 なので単純ですね。構造体のメンバとして無名の共用体が混じってたりするとややこしくなりますが。

この共用体は、マーシャリング時だけでなくマネージドコード内や unsafe 構文内でも普通に共用体として機能します。Daily フィールドを設定した後 Weekly フィールドのメンバを見れば、Weekly フィールドに対してはなんの操作もしていないのに値が入っているのが確認できます。

繰り返しますが、.NET では出番はないものです。オーバーロードや継承を使えば解決できるはずのことですから。こんな面倒な定義方法になっているのもそれを強調したいためでしょう。それを念頭に置いて、ご利用は計画的に。

さて、大体解説もこの程度で一通りと言うことになります。次回、多分最終回。TaskScheduler クラスの使用方法と全ソース、の予定です。

posted by Hongliang at 21:03| Comment(2) | TrackBack(1) | .NET | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
過去の記事を見たのですが、VB.NETでDDEを簡単に使えるような機能を開発されたのですか?
Posted by KK1 at 2006年03月20日 12:39
そう言えばそんなのもちょっと書いてた気がしますねー(遠い目
制作途上で放置中です。ご期待に添えられず申し訳ないですが。
何か実装が面倒なことに引っかかって疲れたからだったような気がします<放置

何か他にもう一つくらいここにちょろっとだけ書いたけど放置中、なのがある気がするな……。
Posted by Hongliang at 2006年03月20日 13:52
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/15039496

この記事へのトラックバック

[業務日誌.NET] 共用体を.NETで
Excerpt: C用のライブラリをVB.NETから使う必要があり、調べていたのだが、C側の定義に共用体があるためにうまくいかず、困っている。 共用体に対応する概念は.NETには存在しないのだが、構造体の宣言にStr..
Weblog: misc.log
Tracked: 2006-08-23 13:44

ここ(hongliang.seesaa.net)で公開しているものについて、利用は自由に行って頂いて構いません。改変、再頒布もお好きになさって下さい。利用に対しこちらが何かを要求することはありません。

ただし、公開するものを使用、または参考したことによって何らかの損害等が生じた場合でも、私はいかなる責任も負いません。

あ、こんなのに使ったってコメントを頂ければ嬉しいです。

×

この広告は1年以上新しい記事の投稿がないブログに表示されております。