コンテキストメニューに追加する簡単なWindowsシェル拡張機能を作成したかったのですが、C#は最近最もよく使用する言語です。それはシェル拡張のためのまともな選択ですか?インターフェースは簡単に利用できますか?メニューのポップアップが遅くなる原因となる追加のオーバーヘッドはありますか?
始めるための良い指針を持っている人はいますか?
コンテキストメニューに追加する簡単なWindowsシェル拡張機能を作成したかったのですが、C#は最近最もよく使用する言語です。それはシェル拡張のためのまともな選択ですか?インターフェースは簡単に利用できますか?メニューのポップアップが遅くなる原因となる追加のオーバーヘッドはありますか?
始めるための良い指針を持っている人はいますか?
Raymondの投稿:マネージコードでインプロセスシェル拡張機能を記述しないでください。
最近のフォローアップ:.NET Frameworkのバージョン4がインプロセスのサイドバイサイドランタイムをサポートするようになったので、マネージコードでシェル拡張機能を記述しても大丈夫ですか?
肝心なのは、いや、それは大丈夫ではないということです。
インプロセス拡張機能を実装するためのガイダンスが改訂され、バージョン4以降を使用している場合でも、マネージコードでシェル拡張機能とInternet Explorer拡張機能(およびその他のタイプのインプロセス拡張機能)を記述しないように推奨されています。
バージョンの競合
バージョンの競合は、単一のプロセス内で複数のランタイムバージョンのロードをサポートしていないランタイムを通じて発生する可能性があります。バージョン4.0より前のバージョンのCLRは、このカテゴリに分類されます。ランタイムの1つのバージョンをロードすると、同じランタイムの他のバージョンをロードできない場合、ホストアプリケーションまたは別のインプロセス拡張機能が競合するバージョンを使用すると、競合が発生する可能性があります。バージョンが別の処理中の拡張機能と競合する場合、障害には適切な競合する拡張機能が必要であり、障害モードは競合する拡張機能がロードされる順序に依存するため、競合を再現するのは難しい場合があります。
バージョン4.0より前のバージョンのCLRを使用して記述されたインプロセス拡張について考えてみます。[ファイルを開く]ダイアログボックスを使用するコンピューター上のすべてのアプリケーションでは、ダイアログのマネージコードとそれに付随するCLR依存関係がアプリケーションのプロセスに読み込まれる可能性があります。4.0より前のバージョンのCLRをアプリケーションのプロセスに最初にロードするアプリケーションまたは拡張機能は、そのプロセスで後で使用できるCLRのバージョンを制限します。[開く]ダイアログボックスを備えた管理対象アプリケーションが競合するバージョンのCLRで構築されている場合、拡張機能が正しく実行されず、アプリケーションで障害が発生する可能性があります。逆に、拡張機能がプロセスで最初に読み込まれ、その後、競合するバージョンのマネージコードが起動しようとすると(おそらく、マネージアプリケーションまたは実行中のアプリケーションがオンデマンドでCLRを読み込む)、操作は失敗します。ユーザーには、アプリケーションの一部の機能がランダムに機能しなくなったり、アプリケーションが不思議なことにクラッシュしたりしているように見えます。
バージョン4.0以降のバージョンのCLRは、相互に共存するように設計されており、ほとんどの4.0より前のバージョンのCLRと共存するように設計されているため(バージョン1.0を除いて、バージョン管理の問題の影響を受けにくいことに注意してください。他のバージョンと共存します)。ただし、このトピックの残りの部分で説明するように、バージョンの競合以外の問題が発生する可能性があります。
パフォーマンスの問題
パフォーマンスの問題は、プロセスにロードされるときに重大なパフォーマンスペナルティを課すランタイムで発生する可能性があります。パフォーマンスの低下は、メモリ使用量、CPU使用率、経過時間、さらにはアドレス空間の消費の形で発生する可能性があります。CLR、JavaScript / ECMAScript、およびJavaは、影響の大きいランタイムであることが知られています。インプロセス拡張機能は多くのプロセスにロードでき、パフォーマンスに敏感な瞬間(ユーザーに表示するメニューを準備するときなど)にロードされることが多いため、影響の大きいランタイムは全体的な応答性に悪影響を与える可能性があります。
大量のリソースを消費する影響の大きいランタイムは、ホストプロセスまたは別のインプロセス拡張で障害を引き起こす可能性があります。たとえば、ヒープに数百メガバイトのアドレス空間を消費する影響の大きいランタイムでは、ホストアプリケーションが大きなデータセットをロードできなくなる可能性があります。さらに、インプロセス拡張機能は複数のプロセスにロードできるため、単一の拡張機能での高いリソース消費は、システム全体での高いリソース消費にすぐに倍増する可能性があります。
ランタイムがロードされたままであるか、そのランタイムを使用する拡張機能がアンロードされた場合でもリソースを消費し続ける場合、そのランタイムは拡張機能での使用には適していません。
.NETFrameworkに固有の問題
次のセクションでは、拡張機能にマネージコードを使用する際に見つかった問題の例について説明します。これらは、発生する可能性のあるすべての問題の完全なリストではありません。ここで説明する問題は、マネージコードが拡張機能でサポートされていない理由と、他のランタイムの使用を評価する際に考慮すべき点の両方です。
再入
可能性たとえば、Monitor.Enter、WaitHandle.WaitOne、または競合するロックステートメントが原因でCLRがシングルスレッドアパートメント(STA)スレッドをブロックすると、CLRは標準構成でネストされたメッセージループに入ります。待ちます。多くの拡張メソッドはメッセージの処理を禁止されており、この予測不可能で予期しない再入可能性により、再現や診断が困難な異常な動作が発生する可能性があります。マルチスレッドアパートメント CLRは、コンポーネントオブジェクトモデル(COM)オブジェクトのランタイム呼び出し可能ラッパーを作成します。これらの同じRuntimeCallableWrappersは、マルチスレッドアパートメント(MTA)の一部であるCLRのファイナライザーによって後で破棄されます。プロキシをSTAからMTAに移動するにはマーシャリングが必要ですが、拡張機能で使用されるすべてのインターフェイスをマーシャリングできるわけではありません。
非決定論的な
オブジェクトの有効期間CLRのオブジェクトの有効期間の保証は、ネイティブコードよりも弱くなります。多くの拡張機能には、オブジェクトとインターフェイスの参照カウント要件があり、CLRで採用されているガベージコレクションモデルはこれらの要件を満たすことができません。
- CLRオブジェクトがCOMオブジェクトへの参照を取得する場合、Runtime Callable Wrapperによって保持されているCOMオブジェクト参照は、RuntimeCallableWrapperがガベージコレクションされるまで解放されません。非決定論的なリリース動作は、一部のインターフェイスコントラクトと競合する可能性があります。たとえば、IPersistPropertyBag :: Loadメソッドでは、Loadメソッドが戻ったときに、プロパティバッグへの参照がオブジェクトによって保持されていない必要があります。
- CLRオブジェクト参照がネイティブコードに返される場合、Runtime Callable WrapperのReleaseへの最後の呼び出しが行われると、Runtime Callable WrapperはCLRオブジェクトへの参照を放棄しますが、基になるCLRオブジェクトはガベージコレクションされるまでファイナライズされません。非決定論的なファイナライズは、一部のインターフェイスコントラクトと競合する可能性があります。たとえば、サムネイルハンドラは、参照数がゼロになったらすぐにすべてのリソースを解放する必要があります。
マネージコードおよびその他のランタイムの許容される使用法
マネージコードやその他のランタイムを使用して、アウトプロセス拡張を実装することは許容されます。アウトプロセスシェル拡張の例には、次のものがあります。
- プレビューハンドラ
- shell \ verb\commandサブキーの下に登録されているようなコマンドラインベースのアクション。
- アウトプロセスアクティベーションを可能にするシェル拡張ポイント用に、ローカルサーバーに実装されたCOMオブジェクト。
一部の拡張機能は、インプロセス拡張機能またはアウトプロセス拡張機能として実装できます。これらの拡張機能がインプロセス拡張機能のこれらの要件を満たしていない場合は、これらの拡張機能をアウトプロセス拡張機能として実装できます。次のリストは、インプロセスまたはアウトプロセス拡張として実装できる拡張の例を示しています。
- shell \ verb\commandサブキーの下に登録されたDelegateExecuteエントリに関連付けられたIExecuteCommand。
- shell \ verb\DropTargetサブキーの下に登録されたCLSIDに関連付けられたIDropTarget。
- shell\verbサブキーの下に登録されたCommandStateHandlerエントリに関連付けられたIExplorerCommandState。
SharpShellを使用すると、.NETFrameworkを使用してWindowsシェル拡張機能を簡単に作成できます。
ソースコードはhttps://github.com/dwmkerr/sharpshellでホストされています。質問や機能のリクエストは、ここまたはそこに投稿できます。サポートされている拡張機能
SharpShellを使用して、以下の拡張機能のいずれかを構築できます。
SharpShellを使用するプロジェクト1.Trello
コンテキストメニュー
2.REALShuffle Player 2.0
CodeProjectの記事シリーズ
EZShellExtensionsは、サクラのように見えるリスクがありますが、C#でのシェル拡張開発のための素晴らしい(ただし無料ではない)フレームワークです。約20行のコードで簡単なコンテキストメニュー拡張機能を記述でき、何よりも、COMインターフェイスをいじる必要はありません。私の会社は、現在何万人もの顧客が使用している一連の拡張機能にそれ(およびその名前空間拡張フレームワーク)を使用しています。その価値については、上記のCLRの競合で問題が発生したことはありません。
これがいかに簡単かを示す簡単なサンプルです。
[Guid("00000000-0000-0000-0000-000000000000"), ComVisible(true)]
[TargetExtension(".txt", true)]
public class SampleExtension : ContextMenuExtension
{
protected override void OnGetMenuItems(GetMenuitemsEventArgs e)
{
e.Menu.AddItem("Sample Extension", "sampleverb", "Status/help text");
}
protected override bool OnExecuteMenuItem(ExecuteItemEventArgs e)
{
if (e.MenuItem.Verb == "sampleverb")
; // logic
return true;
}
[ComRegisterFunction]
public static void Register(Type t)
{
ContextMenuExtension.RegisterExtension(typeof(SampleExtension));
}
[ComUnregisterFunction]
public static void UnRegister(Type t)
{
ContextMenuExtension.UnRegisterExtension(typeof(SampleExtension));
}
}