2006年03月01日

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

今回から数回にわたって、シリーズとしてお送りしたいと思います。題して、……この記事のタイトルに出てますね。コードは(ほぼ)完成しているので、あとは記事を書くのみ(いやまあそれも骨なんですけど)。

ちなみに、こんなタイトルですが、実際に COM の部分はそう大した量ではないと思います。.NET とのギャップのマッチング部分が私の作成した実装の肝ですし、COM インターフェイスやアンマネージドメモリを便利に扱うためのラッピングクラスにもそれなりに気を配ったり。

いや、一番疲れたのは XML ドキュメント作成部分でしたけどね。

さて、まずは何故タスクスケジューラなのかという話ですが、そこにネタが転がっていたからです(笑)。考え出したのは某掲示板に「タスクスケジューラをユーザを指定して登録したいんだが WMI では無理だった、どうすればいいか」という質問が出たからですね。その場では「.NET から直接タスクスケジューラを操作するのは面倒だ。schtasks.exe を使え」で済んだんですが、その後個人的にタスクウィザードをよく使うことになって、ちょくちょく不満点が出てきました。毎回実行するプログラムを走査するので時間がかかるとか、デフォルトでユーザパスワードの入力が要求されるとか。ですので、それなら自分でGUIを作っちまおうかと考えて、はたと困ったのが、まずそもそもタスクのプロパティに並ぶダイアログの意味が皆目検討つかないのばかりってこと。おいおい根本的に駄目じゃん。そんなのなので、schtasks.exe も正直パラメータが掴めないので使わない方向で。

そんな不安いっぱいのスタートですが、まずはできることを。COM の ITask インターフェイス辺りを使うことは知っていましたから、まずはその調査です。MSDN には目次に幸い TaskScheduler の見出しがありますから、それを通読すれば使えるようになるはずです。理論上は。

ドキュメントは全く和訳されてませんけどね。

ざっと目を通して分かったのは、インターフェイスは6つだけしかないこと。このくらいなら全て C# でラップするのもそう大した手間ではなさそうです。メンバが多いインターフェイスもありますけど。

さて、COM インターフェイスの C# / .NET での宣言方法は知っていましたが、寧ろ知らないのは COM コクラスの宣言方法。Microsoft.mshtml.dll の mshtml.HTMLDocumentClass を普通に new で作成できたりするアレです。こういう場合、目の前にヒントが転がってるんですから早速 ILDASM で調べてみましょう。

mshtml.HTMLDocumentClass を見るとずらずらメソッドが並んでいます。それぞれオーバーライドしてるようですけどひょっとして全部書かなくちゃいけないんでしょうか。挫けそうです。そんなことからは目をそらして、やはりポイントとなるのは属性。HTMLDocumentClass には、ComSourceInterfaces、TypeLibType、ClassInterface、Guid、の4つのカスタム属性がマークされています。また、カスタムでない(Type.Attributesで取得できる)属性で目につくのが import 。この辺がキーワードでしょうか。MSDN で当たってみると、ComSourceInterfaces はイベントソースがどうこうとか言っているので関係なさそうです。TaskScheduler の項目にもイベントに関する文言は無いですし。TypeLibType は、共通言語ランタイムでは、この属性は使用されません。 とヘルプに書かれてるのでやっぱり関係なさそう。ClassInterface は、Tlbexp.exe の挙動に影響するらしいですが、寧ろインポートする側なので関係なさそうです。Guid はまあ今望む機能に必須な属性というのは分かりますね。API における CoCreateInstance に使う IID を指すんでしょう。後一つ、import ってのは何でしょう? System.Reflection.TypeAttributes の Import には以下のように書かれています。

クラスまたはインターフェイスが別のモジュールからインポートされることを指定します。

分かるようで分かりません。そう言えば System.Type にはこの手の属性をそれぞれ bool で判断できるプロパティをIsHogehogeって名前で公開しています。

Type.IsImport プロパティ
Type が ComImportAttribute 属性を持っているかどうかを示す (つまり、COM タイプ ライブラリからインポートされたかどうかを示す) 値を取得します。

ほほう、ComImport ですか。ComImport を調べてみると

ComImportAttribute クラス
属性の型が以前に COM で定義されたことを示します。

こんな感じです。タスクスケジューラ関係のインターフェイスは当然ながら COM で定義されていますから、これで間違いないっぽいですね。

ではこの二つの属性を実装させればいいのか? 確認のためにこの二つのキーワードでぐぐってみますと、一番初めに分かりやすい解説が見つかります。…… MSDN に。おおう。盲点だったぜ。

と言うことで、COM 相互運用性 - 第 1 部 : C# クライアント チュートリアルを以下参考にしていきます。

さて、COM コクラスの定義方法は分かりました。では実際にどう書くか? これらの属性を定義したクラスは、new するだけで自動的にフレームワークが COM オブジェクトを作ってくれるそうで、その意味は CoCreateInstance を呼び出すことと等価であるらしいです。CoCreateInstance には引数として CLSID、LPUNKNOWN、DWORD、REFIID、LPVOID、の五つがありますが、new するときは引数なしのコンストラクタ。いったい何をどう割り当てるのでしょうか?

MSDN の TaskScheduler のドキュメントには、幸いなことに C++ のサンプルコードが豊富に転がっています。それによると、引数にはそれぞれ CLSID_CTaskScheduler、NULL、CLSCTX_INPROC_SERVER、IID_ITaskScheduler、ITaskScheduler へのポインタ、を渡しています。C# で定義するコクラスに付加できるのは基本的に属性二つだけ、というか値を指定できるのは Guid 一つだけ。当てはまりそうなのは CLSID_CTaskScheduler と IID_ITaskScheduler のどちらかですね。じゃあどっち? IID_ITaskScheduler は、その名の通りインターフェイスのID。つまり実体がありません。実体がなければ作れません。と言うことで CLSID_CTaskScheduler の方がそれっぽいです。CLSID_CTaskScheduler の実際の値は Platform SDK の MSTask.h に書かれています。なお、他のいろいろな定数やらも、タスクスケジューラ絡みのものは大抵このヘッダファイルに記載されています。

さて、以上の流れでまず TaskSchedulerClass コクラスの定義が分かりました。では早速やってみましょう。

using System;
using System.Runtime.InteropServices;

[ComImport, Guid("148BD52A-A2AB-11CE-B11F-00AA00530503")]
public class TaskSchedulerClass {
}

ん〜、これだけですか。不安な雰囲気漂いますね。動くかどうか確認してみます。

public class TaskTest {
    public static void Main() {
        TaskSchedulerClass scheduler = new TaskSchedulerClass();
        Marshal.ReleaseComObject(scheduler);
    }
}

ちなみに .NET がもつ COM との相互運用マーシャラが参照カウントは管理してくれているはずですが、どうにもそれに頼るわけにも行かないらしいので手動で Marshal.ReleaseComObject を呼び出しています。

さあ実行。おお、取りあえず例外もなく動くようです。

しかしこれではこのクラス、何のメンバも持ってません。つまりこのオブジェクトに対して C# から働きかける手段が無い。無用の長物とはまさにこのことですね。

と言うところで今回はお開き。次回はこのオブジェクトに対して働きかける窓口となるインターフェイスを定義します。

posted by Hongliang at 06:36| Comment(0) | TrackBack(0) | C# | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

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


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

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

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

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

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

×

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