web-dev-qa-db-ja.com

C#によるWindowsシェル拡張

コンテキストメニューに追加する単純なWindowsシェル拡張を作成したかったのですが、最近C#が最もよく使用する言語です。シェル拡張の適切な選択ですか?インターフェイスは使いやすいですか?メニューのポップアップが遅くなる追加のオーバーヘッドはありますか?

誰もが始めるための良い指針がありますか?

37
Pete McKinney

Raymondの投稿: マネージコードでインプロセスシェル拡張を記述しないでください


最近のフォローアップ: 。NET Frameworkのバージョン4がインプロセスサイドバイサイドランタイムをサポートするようになりました。マネージコードでシェル拡張を記述してもかまいませんか?

一番下の行は、いいえ、それは大丈夫ではありません:

インプロセス拡張機能を実装するためのガイダンス が改訂され、シェル拡張機能やInternet Explorer拡張機能(およびその他のタイプのインプロセス拡張機能)をマネージコードで記述しないようにすることを推奨しています。バージョン4以降を使用してください。

26
GSerg

悪ふざけのように見えるリスクがありますが、 EZShellExtensions は、C#でのシェル拡張開発のための素晴らしい(しかしフリーではない)フレームワークです。約20行のコードで単純なコンテキストメニュー拡張を記述できます。何よりも、COMインターフェイスをいじる必要はありません。私の会社は、何万人もの顧客が現在使用している一連の拡張機能に、それ(およびその名前空間拡張フレームワーク)を使用しています。

これがどれほど簡単かを示す簡単なサンプルです。

[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));
   }
}
14
ladenedge

インプロセス拡張機能の実装に関するガイダンス

バージョンの競合

バージョンの競合は、単一のプロセス内での複数のランタイムバージョンのロードをサポートしていないランタイムを通じて発生する可能性があります。バージョン4.0より前のCLRのバージョンは、このカテゴリに分類されます。ランタイムのあるバージョンをロードすることで、同じランタイムの他のバージョンをロードできない場合、ホストアプリケーションまたは別のインプロセス拡張機能が競合するバージョンを使用していると、競合が発生する可能性があります。バージョンが別のインプロセス拡張機能と競合する場合、障害は正しい競合拡張機能を必要とし、障害モードは競合する拡張機能がロードされる順序に依存するため、競合を再現するのは難しい場合があります。

バージョン4.0より前のバージョンのCLRを使用して記述されたインプロセス拡張を検討してください。ファイルを開くダイアログボックスを使用するコンピューター上のすべてのアプリケーションは、ダイアログのマネージコードとそれに付随するCLR依存関係をアプリケーションのプロセスにロードする可能性があります。 4.0より前のバージョンのCLRを最初にアプリケーションのプロセスにロードするアプリケーションまたは拡張機能は、そのプロセスで後で使用できるCLRのバージョンを制限します。 [開く]ダイアログボックスのあるマネージアプリケーションが、競合するバージョンのCLRで構築されている場合、拡張機能が正しく実行されず、アプリケーションでエラーが発生する可能性があります。逆に、拡張機能がプロセスで最初にロードされ、その後、競合するバージョンのマネージコードが起動した場合(おそらく、マネージアプリケーションまたは実行中のアプリケーションがオンデマンドでCLRをロードする)、操作は失敗します。ユーザーには、アプリケーションの一部の機能がランダムに動作を停止したり、アプリケーションが不思議なことにクラッシュしたように見えます。

バージョン4.0以降のCLRのバージョンは、相互に、およびCLRの4.0より前のほとんどのバージョンと共存するように設計されているため(バージョン1.0を除き、他のバージョンと共存します)。ただし、このトピックの残りの部分で説明するように、バージョンの競合以外の問題が発生する可能性があります。

パフォーマンスの問題

実行時にパフォーマンスの問題が発生する可能性があり、それらがプロセスに読み込まれると、パフォーマンスが大幅に低下します。パフォーマンスの低下は、メモリ使用量、CPU使用量、経過時間、またはアドレススペースの消費の形をとることがあります。 CLR、JavaScript/ECMAScript、およびJavaはインパクトの高いランタイムであることが知られています。インプロセス拡張機能は多くのプロセスにロードできるため、パフォーマンスに敏感な瞬間にロードされることがよくあります(ユーザーに表示するメニューを準備するときなど)、影響の大きいランタイムは全体的な応答性に悪影響を及ぼす可能性があります。

大量のリソースを消費する影響の大きいランタイムは、ホストプロセスまたは別のインプロセス拡張機能で障害を引き起こす可能性があります。たとえば、ヒープに数百メガバイトのアドレス空間を消費する影響の大きいランタイムでは、ホストアプリケーションが大きなデータセットをロードできなくなる可能性があります。さらに、インプロセス拡張機能は複数のプロセスにロードできるため、1つの拡張機能でのリソース消費量が多くなると、システム全体でリソース消費量が急増します。

ランタイムがロードされたままであるか、そうでなければそのランタイムを使用する拡張機能がアンロードされた場合でもリソースを消費し続ける場合、そのランタイムは拡張機能での使用に適していません。

.NET Frameworkに固有の問題

次のセクションでは、拡張機能のマネージコードを使用して見つかった問題の例について説明します。これらは、発生する可能性のあるすべての問題の完全なリストではありません。ここで説明する問題は、マネージコードが拡張機能でサポートされていない理由と、他のランタイムの使用を評価するときに考慮すべき点の両方です。

  • 再入可能
    たとえば、Monitor.Enter、WaitHandle.WaitOne、または競合するロックステートメントが原因で、CLRがシングルスレッドアパートメント(STA)スレッドをブロックすると、CLRは標準構成で、ネストされたメッセージを入力します。待機している間ループします。多くの拡張メソッドはメッセージの処理を禁止されており、この予測不可能な予期しない再入は、再現や診断が難しい異常な動作を引き起こす可能性があります。

  • マルチスレッドアパートメントCLRは、コンポーネントオブジェクトモデル(COM)オブジェクトのランタイム呼び出し可能ラッパーを作成します。これらの同じランタイム呼び出し可能ラッパーは、マルチスレッドアパートメント(MTA)の一部であるCLRのファイナライザーによって後で破棄されます。プロキシをSTAからMTAに移動するにはマーシャリングが必要ですが、拡張機能で使用されるすべてのインターフェースをマーシャリングできるわけではありません。

  • 非決定的なオブジェクトの寿命
    CLRは、ネイティブコードよりもオブジェクトの有効期間の保証が弱いです。多くの拡張機能には、オブジェクトとインターフェイスに関する参照カウント要件があり、CLRで採用されているガベージコレクションモデルはこれらの要件を満たすことができません。

    • CLRオブジェクトがCOMオブジェクトへの参照を取得する場合、ランタイム呼び出し可能ラッパーが保持するCOMオブジェクト参照は、ランタイム呼び出し可能ラッパーがガベージコレクションされるまで解放されません。非決定的なリリース動作は、一部のインターフェイスコントラクトと競合する可能性があります。たとえば、IPersistPropertyBag :: Loadメソッドでは、Loadメソッドが戻ったときに、プロパティバッグへの参照がオブジェクトによって保持されていないことが必要です。
    • CLRオブジェクト参照がネイティブコードに返される場合、ランタイム呼び出し可能ラッパーは、Releaseへのランタイム呼び出し可能ラッパーの最後の呼び出しが行われたときにCLRオブジェクトへの参照を放棄しますが、基になるCLRオブジェクトはガベージコレクションされるまで確定されません。非決定的なファイナライズは、一部のインターフェイスコントラクトと競合する可能性があります。たとえば、サムネイルハンドラーは、参照カウントがゼロになったらすぐにすべてのリソースを解放する必要があります。

マネージコードおよびその他のランタイムの許容される使用法

マネージコードやその他のランタイムを使用して、アウトプロセス拡張機能を実装することは許容されます。アウトプロセスシェル拡張の例には、次のものがあります。

  • プレビューハンドラ
  • Shell\verb\commandサブキーの下に登録されているようなコマンドラインベースのアクション。
  • プロセス外のアクティブ化を可能にするシェル拡張ポイント用のローカルサーバーに実装されたCOMオブジェクト。

一部の拡張機能は、インプロセスまたはアウトプロセスの拡張機能として実装できます。これらの拡張機能がインプロセス拡張機能のこれらの要件を満たさない場合、これらの拡張機能をアウトプロセス拡張機能として実装できます。次のリストは、プロセス内またはプロセス外の拡張機能として実装できる拡張機能の例を示しています。

  • Shell\verb\commandサブキーの下に登録されているDelegateExecuteエントリに関連付けられているIExecuteCommand。
  • Shell\verb\DropTargetサブキーの下に登録されているCLSIDに関連付けられているIDropTarget。
  • Shell\verbサブキーの下に登録されたCommandStateHandlerエントリに関連付けられたIExplorerCommandState。

SharpShell

SharpShellを使用すると、.NET Frameworkを使用してWindowsシェル拡張を簡単に作成できます。

ソースコードは https://github.com/dwmkerr/sharpshell でホストされています-質問や機能のリクエストはここまたはそこに投稿できます。サポートされる拡張機能

SharpShellを使用して、以下の拡張機能を構築できます。

  • シェルのコンテキストメニュー
  • アイコンハンドラー
  • 情報ヒントハンドラ
  • ドロップハンドラー
  • プレビューハンドラ
  • アイコンオーバーレイハンドラー
  • サムネイルハンダー
  • プロパティシート拡張

SharpShellを使用するプロジェクト
1。 Trelloコンテキストメニュー
2。 REAL Shuffle Player 2.

CodeProjectの記事シリーズ

14
Ehsan Mohammadi