Laden...

KShellExtensions: Managed Shell Extensions

Erstellt von Khalid vor 13 Jahren Letzter Beitrag vor 13 Jahren 6.695 Views
Khalid Themenstarter:in
3.511 Beiträge seit 2005
vor 13 Jahren
KShellExtensions: Managed Shell Extensions

Hallo,

da die CLR 4 nun Side-By-Side Prozesse unterstützt (CLR Inside Out: In-Process Side-By-Side*) ist es von Microsoft nun gestattet Shell Extensions in managed Sprachen zu schreiben.

Aus dem Grund habe ich angefangen ein Framework zu schreiben, mit dem man sehr einfach (hoffe ich jedenfalls) eigene Shell Extensions schreiben kann. Dieses Framework setzt vollständig auf .NET 4.0 auf.

Momentan unterstützt die Bibliothek folgende Shell Extensions:* Shell Icon Overlayer

Folgende Extensions sind geplant:* Shell Thumbnail Preview

  • Shell Contextmenu Extensions
  • Copy Hooks

Kleine FAQ zu den Shell Extensions:

Wie registriere ich die DLLs, damit der Explorer diese auch findet?
Da Shell Extensions COM Objekte sind, müssen diese per RegAsm registriert werden. RegAsm wird mit dem .NET 4.0 mitgeliefert und befindet sich im Frameworkordner.

Die DLL ist registriert, aber der Explorer reagiert nicht!?
Das liegt daran, das die DLLs nicht im GAC liegen. Die KShellExtension.dll, sowie die DLL die die Shell Extension enthält müssen im GAC liegen. Diese können per GacUtil in den GAC gelegt werden.

Der Explorer reagiert unter x64 immer noch nicht auf die DLLs!?
Bei x64 Systemen muss darauf geachtet werden, das das Tool RegAsm auch in der x64 Variante benutzt wird. Sonst wird die DLL als WOW6432 registriert und der Explorer hostet nur x64 DLLs unter x64.

Kann ich die Einstellung AnyCPU verwenden?
Ja, das ist kein Problem. Bei x64 Systemen muss nur daruf geachtet werden, das auch die x64 Variante von RegAsm benutzt wird.

Alles ist richtig installiert, aber der Explorer reagiert immer noch nicht!?
Beim Registrieren wird zwar per SHChangeNotify der Shell mitgeteilt, das sich was getan hat, aber meist reagiert der Explorer erst, wenn dieser neu gestartet wird. Also entweder das System neu starten, oder per Task Manager den Explorer abschießen und neu ausführen.


Achtung:
Theoretisch wäre es möglich die Solution auch für .NET 3.5 zu kompilieren. Ich rate davon ab! Das kann zu fatalen Systemabstürzen führen, soweit das die Maschine nicht mehr hochfahren kann!

*

With the ability to have multiple runtimes in process with any other runtime, we can now offer general support for writing managed shell extensions—even those that run in-process with arbitrary applications on the machine. We still do not support writing shell extensions using any version earlier than .NET Framework 4 because those versions of the runtime do not load in-process with one another and will cause failures in many cases.

"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)

Khalid Themenstarter:in
3.511 Beiträge seit 2005
vor 13 Jahren

So, gleich mal das erste Beispiel: Ein Shell Icon Overlayer

Um ein Shell Icon Overlayer zu erstellen, muss eine neue Klassenbibliothek erstellt werden und gleich ein Verweis auf die KShellExtensions hinzugefügt werden.

Die Extension muss dann von der Klasse "ShellIconOverlayIdentifier" abgeleitet werden und die abstrakten Methoden müssen dann mit Leben gefüllt werden.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using KShellExtension;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Diagnostics;

namespace Sample1.IconOverlay
{
  [ComVisible(true)]
  [Guid("[Eindeutige GUID]")] //kann z.B. auf http://www.guidgen.com/ generiert werden
  public class Sample1IconOverlay : ShellIconOverlayIdentifier
  {
    [ComRegisterFunction()]
    public static void RegisterServer(Type type)
    {
      ShellComRegister.RegisterShellIconOverlayIdentifier(typeof(Sample1IconOverlay));
    }

    [ComUnregisterFunction()]
    public static void UnregisterServer(Type type)
    {
      ShellComRegister.UnregisterShellIconOverlayIdentifier(typeof(Sample1IconOverlay));
    }

    public override bool IsOverlayIconVisible(string fileName, int attributes)
    {
      return fileName.ToLower().EndsWith(".exe");
    }

    public override string GetOverlayIconLocation()
    {
      // ToDo: Der Pfad muss vollständig angegeben werden. Die beiliegende Icon-Datei
      // muss also an einem festen Ort kopiert werden, und dann muss hier ebenfalls der
      // Pfad angepasst werden.
      return @"d:\star_blue.ico";
    }

    public override ShellIconOverlaySource GetOverlayIconSource()
    {
      return ShellIconOverlaySource.FromFile;
    }

    public override int GetOverlayIconIndex()
    {
      return 0;
    }
  }
}

Zum Code:
Wichtig ist, das die Klasse ComVisible ist und eine eindeutige ID anhand einer GUID erhält. Um beim Registrieren der DLL gleich alle nötigen Registryeinträge zu schreiben, müssen zwei statische Methoden hinzugefügt werden, die mit den Attributen ComRegisterFunction und ComUnregisterFunction versehen werden. In der RegisterFunction werden dann über die Hilfsklasse "ShellComRegister" alle nötigen Registryeinträge geschrieben. Und in der UnregisterFunction werden diese Einträge wieder entfernt.

Mit der Methode "IsOverlayIconVisible" wird bestimmt, bei welcher Datei das Icon angezeigt werden soll. "fileName" ist dabei der vollqualifizierte Dateiname. In dem obigen Beispiel soll also das Icon nur angezeigt werden, wenn diese mit ".exe" endet.

Die Methode "GetOverlayIconLocation" gibt an, wo sich die Datei befindet, in der das Icon abgelegt ist. Das können direkt ICO Dateien sein, aber auch DLL und EXE Dateien. Der Pfad muss dabei vollständig angegeben werden.

Mit "GetOverlayIconSource" gibt man an, ob es sich bei der Quelle direkt um eine ICO Datei handelt ("FromFile"), oder um eine Auflistung von Icons ("FromIndex"). Wird als Quelle eine EXE oder DLL angegeben, oder es sich um eine ICO Datei mit mehreren Icons handelt, muss FromIndex angegeben werden.

Um den Index festzulegen muss in der Methode "GetOverlayIconIndex" der Index angegeben werden, beginnend bei 0. Bei "FromFile" ist dies immer 0.

Um die fertige DLL zu registrieren müssen dann in der Console (als Administrator) folgende Befehle ausgeführt werden


gacutil /f /i KShellExtension.dll
gacutil /f /i Sample1.IconOverlay.dll
regasm Sample1.IconOverlay.dll

Danach den Explorer neu starten und kontrollieren, ob der Explorer die Extension erkannt hat.

Im Anhang befindet sich das Beispielprojekt.

"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)

Khalid Themenstarter:in
3.511 Beiträge seit 2005
vor 13 Jahren

Hier noch ein Screenshot, auf dem man sieht, das die Extension erfolgreich geladen wurde. Jede "exe" Datei wird jetzt mit einem blauen Stern "überdeckt"

"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)

390 Beiträge seit 2008
vor 13 Jahren

Hallo Khalid

Wäre es mit den Managed Shell Extensions nun theoretisch möglich auf das Drop Event des Explorers zu reagieren? Da gibt es ja viele sehr komplizierte und nur knapp funktionierende (wie z.b. hier besprochen) Lösungsansätze.

Gruss

using Skill

5.742 Beiträge seit 2007
vor 13 Jahren

Klingt interessant - werde ich mir mal im Laufe der nächsten tage anschauen.

Nur ein Verbesserungsvorschlag:
Ersetzte folgende Zeile im Beispiel:


[Guid("B8FA9E43-38E6-4654-8A13-FF905AD22CE6")]

durch


[Guid("[Eindeutige GUID]")] //kann z.B. auf http://www.guidgen.com/ generiert werden

um ein paar schwer lokalisierbare Probleme, die aufgrund eifriger Copy&Paster auftreten könnten, von vornehinein auszuschließen 😉

Khalid Themenstarter:in
3.511 Beiträge seit 2005
vor 13 Jahren

@edsplash:
In dem verlinkten Artikel gibt es wiederum ein Link zu Codeproject für die Implementation eines IStream. Genau das ist eigentlich der richtige Weg. Nur benutzt der Autor die .NET Klassen. Man muss aber voll auf Win32 gehen. Das wird ein schönen Stück komplizierte Arbeit 😃

Willst du denn genau das gleiche erreichen? Also auch ein physich nicht vorhanden Bytestream in den Explorer kopieren?

@winSharp93:
Danke, habe ich im Beitrag geändert.

"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)

390 Beiträge seit 2008
vor 13 Jahren

Willst du denn genau das gleiche erreichen? Also auch ein physich nicht vorhanden Bytestream in den Explorer kopieren?

Ja Prinzipiell.

Gäbe ja noch die Möglichkeit ein eindeutiges, temporäres File zu übergeben und sich mit einem File System Watcher den Pfad zu holen, wo die Datei hinverschoben wird.

Ich glaube aber gelesen zu haben, dass dieses Problem auch über Shell Extensions zu lösen wäre und darum habe ich gefragt. 😉

Gruss

using Skill

Khalid Themenstarter:in
3.511 Beiträge seit 2005
vor 13 Jahren

Hi

Es gibt auf CodePlex ein Projekt (LizardTF: allerdings in C++), welches eine Shell Extension für den TFS bereit stellt. Die Dateien liegen in dem Moment ja auch nicht physisch auf der Platte, sondern im Server. Mit dieser Shell Extension kann man diese Dateien aber auch einfach irgendwo hinziehen. Deswegen gehe ich mal davon aus, das es mit einer Shell Extension machbar ist. Man müsste sich mal den Sourcecode anschauen, aber C++ sieht immer so zugemüllt aus 😃

"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)

Gelöschter Account
vor 13 Jahren
[Guid("[Eindeutige GUID]")] //kann z.B. auf http://www.guidgen.com/ generiert werden  

um ein paar schwer lokalisierbare Probleme, die aufgrund eifriger Copy&Paster auftreten könnten, von vornehinein auszuschließen 😉

kann man auch direkt in VS -> Tools -> Create GUID -> 4. Registry Format -> Copy
und dann nur noch an der passenden stelle im code einfügen.