Anpassen eines Kontextmenüs mithilfe von dynamischen Verben

Kontextmenühandler werden auch als Verbhandler bezeichnet. Ein Kontextmenühandler ist eine Art Dateityphandler.

Dieses Thema ist wie folgt organisiert:

Informationen zu statischen und dynamischen Verben

Sie sollten Kontextmenüs unbedingt mithilfe einer der Methoden für statische Verben implementieren. Es empfiehlt sich, die Anweisungen im Abschnitt „Anpassen eines Kontextmenüs mithilfe von statischen Verben“ im Artikel Erstellen von Kontextmenühandlern zu befolgen. Informationen zum dynamischen Verhalten für statische Verben unter Windows 7 und höher finden Sie im Abschnitt „Erzielen eines dynamischen Verhaltens für statische Verben“ in Erstellen von Kontextmenühandlern. Ausführliche Informationen zur Implementierung statischer Verben und dazu, welche dynamischen Verben vermieden werden sollten, finden Sie unter Auswählen eines statischen oder dynamischen Verbs für Ihr Kontextmenü.

Wenn Sie das Kontextmenü für einen Dateityp erweitern müssen, indem Sie ein dynamisches Verb für den Dateityp registrieren, folgen Sie den Anweisungen weiter unten in diesem Thema.

Hinweis

Bei 64-Bit-Versionen von Windows gelten beim Registrieren von Handlern, die im Kontext von 32-Bit-Anwendungen funktionieren, besondere Überlegungen: Wenn Shellverben im Kontext einer 32-Bit-Anwendung aufgerufen werden, leitet das WOW64-Subsystem den Dateisystemzugriff auf einige Pfade um. Wenn Ihr EXE-Handler in einem dieser Pfade gespeichert ist, ist er in diesem Kontext nicht zugänglich. Speichern Sie daher als Problemumgehung entweder Ihren EXE-Handler in einem Pfad, der nicht umgeleitet wird, oder speichern Sie eine Stubversion des EXE-Handlers, die die echte Version startet.

 

Funktionsweise von Kontextmenühandlern mit dynamischen Verben

Neben IUnknown exportieren Kontextmenühandler die folgenden zusätzlichen Schnittstellen, um das für die Implementierung von Ownerdrawn-Menüelementen erforderliche Messaging zu verarbeiten:

Weitere Informationen zu Ownerdrawn-Menüelementen finden Sie im Abschnitt Erstellen von Ownerdrawn-Menüelementen im Artikel Verwenden von Menüs.

Die Shell verwendet die IShellExtInit-Schnittstelle, um den Handler zu initialisieren. Wenn die Shell IShellExtInit::Initialize aufruft, übergibt sie darin ein Datenobjekt mit dem Namen des Objekts und einen Zeiger des Ordners, der die Datei enthält, auf eine Elementbezeichnerliste (PIDL). Der hkeyProgID-Parameter ist der Registrierungsspeicherort, in dem das Kontextmenühandle registriert ist. Die IShellExtInit::Initialize-Methode muss den Dateinamen aus dem Datenobjekt extrahieren und den Namen und den Zeiger des Ordners auf eine Elementbezeichnerliste (PIDL) zur späteren Verwendung speichern. Weitere Informationen zur Handlerinitialisierung finden Sie unter Implementieren von IShellExtInit.

Wenn Verben in einem Kontextmenü angezeigt werden, werden sie zuerst ermittelt, dann den Benutzer*innen angezeigt und schließlich aufgerufen. In der folgenden Liste werden diese drei Schritte ausführlicher beschrieben:

  1. Die Shell ruft IContextMenu::QueryContextMenu auf, wodurch ein Satz Verben zurückgegeben wird, der auf dem Zustand der Elemente oder des Systems basieren kann.
  2. Das System übergibt ein HMENU-Handle, mit dem die Methode Elemente zum Kontextmenü hinzufügen kann.
  3. Wenn Benutzer*innen auf eines der Elemente des Handlers klicken, ruft die Shell IContextMenu::InvokeCommand auf. Der Handler kann dann den entsprechenden Befehl ausführen.

Vermeiden von Kollisionen aufgrund nicht qualifizierter Verbnamen

Da Verben pro Typ registriert sind, kann ein und derselbe Verbname für Verben in verschiedenen Elementen verwendet werden. Auf diese Weise können Anwendungen unabhängig vom Elementtyp auf allgemeine Verben verweisen. Obwohl diese Funktionalität nützlich ist, kann die Verwendung nicht qualifizierter Namen zu Kollisionen führen, wenn verschiedene unabhängige Softwareanbieter (ISVs) denselben Verbnamen auswählen. Um dies zu vermeiden, fügen Sie bei Verben immer den ISV-Namen als Präfix hinzu:

ISV_Name.verb

Verwenden Sie immer eine anwendungsspezifische ProgID. Mithilfe der Konvention, dass die Dateinamenerweiterung einer vom ISV bereitgestellten ProgID zugeordnet wird, lassen sich potenzielle Kollisionen vermieden. Da einige Elementtypen diese Zuordnung jedoch nicht verwenden, sind anbieterspezifische Namen erforderlich. Wenn Sie ein Verb zu einer vorhandenen ProgID hinzufügen, für die dieses Verb möglicherweise bereits registriert ist, müssen Sie zuerst den Registrierungsschlüssel für das alte Verb entfernen, bevor Sie Ihr eigenes Verb hinzufügen. Dies ist erforderlich, um ein Zusammenführen der Verbinformationen aus den beiden Verben zu vermeiden. Wenn Sie dies unterlassen, sind unvorhersehbare Verhaltensweisen die Folge.

Registrieren eines Kontextmenühandlers mit einem dynamischen Verb

Kontextmenühandler sind entweder einem Dateityp oder einem Ordner zugeordnet. Bei Dateitypen wird der Handler im dem folgenden Unterschlüssel registriert.

HKEY_CLASSES_ROOT
   Program ID
      shellex
         ContextMenuHandlers

Um einen Kontextmenühandler entweder einem Dateityp oder einem Ordner zuzuordnen, erstellen Sie zuerst einen Unterschlüssel im Unterschlüssel ContextMenuHandlers. Benennen Sie den Unterschlüssel nach dem Handler, und legen Sie den Standardwert des Unterschlüssels auf die Zeichenfolgenform der Klassenbezeichner-GUID (CLSID) des Handlers fest.

Um dann einen Kontextmenühandler mit verschiedenen Ordnertypen zu verknüpfen, registrieren Sie den Handler auf die gleiche Weise wie für einen Dateityp, aber im Unterschlüssel FolderType, wie im folgenden Beispiel gezeigt.

HKEY_CLASSES_ROOT
   FolderType
      shellex
         ContextMenuHandlers

Weitere Informationen zu den Ordnertypen, für die Sie Handler registrieren können, finden Sie unter Registrieren von Shellerweiterungshandlern.

Wenn einem Dateityp ein Kontextmenü zugeordnet ist, wird beim Doppelklicken auf ein Objekt normalerweise der Standardbefehl gestartet, und die IContextMenu::QueryContextMenu-Methode des Handlers wird nicht aufgerufen. Um anzugeben, dass beim Doppelklicken auf ein Objekt die IContextMenu::QueryContextMenu-Methode des Handlers aufgerufen werden soll, erstellen Sie einen Unterschlüssel im Unterschlüssel CLSID des Handlers, wie hier gezeigt.

HKEY_CLASSES_ROOT
   CLSID
      {00000000-1111-2222-3333-444444444444}
         shellex
            MayChangeDefaultMenu

Beim Doppelklicken auf ein dem Handler zugeordnetes Objekt wird IContextMenu::QueryContextMenu mit dem im Parameter uFlags festgelegten Flag CMF_DEFAULTONLY aufgerufen.

Kontextmenühandler sollten den Unterschlüssel MayChangeDefaultMenu nur dann festlegen, wenn sie möglicherweise das Standardverb des Kontextmenüs ändern müssen. Durch Festlegen dieses Unterschlüssels wird das System gezwungen, die DLL des Handlers zu laden, wenn auf ein zugeordnetes Element doppelgeklickt wird. Wenn Ihr Handler das Standardverb nicht ändert, sollten Sie diesen Unterschlüssel nicht festlegen, da dadurch das System die DLL unnötig lädt.

Das folgende Beispiel veranschaulicht Registrierungseinträge, die einen Kontextmenühandler für einen MYP-Dateityp aktivieren. Der Unterschlüssel CLSID des Handlers enthält den Unterschlüssel MayChangeDefaultMenu, um sicherzustellen, dass der Handler aufgerufen wird, wenn der Benutzer oder die Benutzerin auf ein entsprechendes Objekt doppelklickt.

HKEY_CLASSES_ROOT
   .myp
      (Default) = MyProgram.1
   CLSID
      {00000000-1111-2222-3333-444444444444}
         InProcServer32
            (Default) = C:\MyDir\MyCommand.dll
            ThreadingModel = Apartment
         shellex
            MayChangeDefaultMenu
   MyProgram.1
      (Default) = MyProgram Application
      shellex
         ContextMenuHandler
            MyCommand = {00000000-1111-2222-3333-444444444444}

Implementieren der IContextMenu-Schnittstelle

IContextMenu ist die leistungsfähigste, aber auch die komplizierteste Methode für die Implementierung. Es wird dringend empfohlen, ein Verb mithilfe einer der Methoden für statische Verben zu implementieren. Weitere Informationen finden Sie unter Auswählen eines statischen oder dynamischen Verbs für das Kontextmenü. IContextMenu verfügt über drei Methoden: GetCommandString, InvokeCommandund QueryContextMenu. Diese werden hier ausführlich erläutert.

IContextMenu::GetCommandString Method

Die IContextMenu::GetCommandString-Methode des Handlers wird verwendet, um den kanonischen Namen für ein Verb zurückzugeben. Diese Methode ist optional. Unter Windows XP und früheren Windows-Versionen wird diese Methode verwendet – wenn der Windows-Explorer über eine Statusleiste verfügt –, um den Hilfetext abzurufen, der in der Statusleiste für ein Menüelement angezeigt wird.

Der Parameter idCmd enthält den Bezeichneroffset des Befehls, der beim Aufrufen von IContextMenu::QueryContextMenu definiert wurde. Wenn eine Hilfezeichenfolge angefordert wird, wird uFlags auf GCS_HELPTEXTW festgelegt. Kopieren Sie die Hilfezeichenfolge in den pszName-Puffer, und wandeln Sie sie in ein PWSTR-Element um. Die Verbzeichenfolge wird durch Festlegen von uFlags auf GCS_VERBW angefordert. Kopieren Sie die entsprechende Zeichenfolge in pszName – genauso wie bei der Hilfezeichenfolge. Die Flags GCS_VALIDATEA und GCS_VALIDATEW werden von Kontextmenühandlern nicht verwendet.

Das folgende Beispiel zeigt eine einfache Implementierung von IContextMenu::GetCommandString, die dem Beispiel IContextMenu::QueryContextMenu im Abschnitt IContextMenu::QueryContextMenu-Methode dieses Themas entspricht. Da der Handler nur ein einziges Menüelement hinzufügt, gibt es nur einen Satz von Zeichenfolgen, die zurückgegeben werden können. Die Methode testet, ob idCmd gültig ist, und gibt die angeforderte Zeichenfolge zurück, wenn dies der Fall ist.

Mithilfe der Funktion StringCchCopy wird die angeforderte Zeichenfolge in pszName kopiert, um sicherzustellen, dass die kopierte Zeichenfolge die Größe des durch cchName angegebenen Puffers nicht überschreitet. In diesem Beispiel wird nur Unterstützung für die Unicode-Werte von uFlags implementiert, da seit Windows 2000 nur diese im Windows-Explorer verwendet werden.

IFACEMETHODIMP CMenuExtension::GetCommandString(UINT idCommand, 
                                                UINT uFlags, 
                                                UINT *pReserved, 
                                                PSTR pszName, 
                                                UINT cchName)
{
    HRESULT hr = E_INVALIDARG;

    if (idCommand == IDM_DISPLAY)
    {
        switch (uFlags)
        {
            case GCS_HELPTEXTW:
                // Only useful for pre-Vista versions of Windows that 
                // have a Status bar.
                hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName), 
                                    cchName, 
                                    L"Display File Name");
                break; 

            case GCS_VERBW:
                // GCS_VERBW is an optional feature that enables a caller
                // to discover the canonical name for the verb passed in
                // through idCommand.
                hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName), 
                                    cchName, 
                                    L"DisplayFileName");
                break; 
        }
    }
    return hr;
}

IContextMenu::InvokeCommand-Methode

Diese Methode wird aufgerufen, wenn ein Benutzer oder eine Benutzerin auf ein Menüelement klickt, um den Handler anweisen, den zugehörigen Befehl auszuführen. Der Parameter pici zeigt auf eine Struktur, die die erforderlichen Informationen enthält.

Obwohl pici in Shlobj.h als CMINVOKECOMMANDINFO-Struktur deklariert ist, verweist er in der Praxis häufig auf eine CMINVOKECOMMANDINFOEX-Struktur. Diese Struktur ist eine erweiterte Version von CMINVOKECOMMANDINFO und enthält mehrere zusätzliche Member, mit deren Hilfe Unicode-Zeichenfolgen übergeben werden können.

Sehen Sie sich den cbSize-Member von pici an, um zu ermitteln, welche Struktur übergeben wurde. Wenn es sich um eine CMINVOKECOMMANDINFOEX-Struktur handelt und für den fMask-Member das Flag CMIC_MASK_UNICODE festgelegt ist, wandeln Sie pici in CMINVOKECOMMANDINFOEX um. Auf diese Weise kann Ihre Anwendung die Unicode-Informationen verwenden, die in den letzten fünf Membern der Struktur enthalten sind.

Zum Identifizieren des auszuführenden Befehls wird der Member lpVerb oder der Member lpVerbW der Struktur verwendet. Befehle werden auf eine der folgenden beiden Arten identifiziert:

  • Anhand der Verbzeichenfolge des Befehls
  • Anhand des Bezeichneroffsets des Befehls

Um zwischen diesen beiden Fällen zu unterscheiden, überprüfen Sie das High-Order-Wort von lpVerb im Fall von ANSI oder von lpVerbW im Fall von Unicode. Wenn das High-Order-Wort ungleich null ist, enthält lpVerb oder lpVerbW eine Verbzeichenfolge. Wenn das High-Order-Wort null ist, befindet sich der Befehlsoffset im Low-Order-Wort von lpVerb.

Das folgende Beispiel zeigt eine einfache Implementierung von IContextMenu::InvokeCommand, die den Beispielen IContextMenu::QueryContextMenu und IContextMenu::GetCommandString vor und nach diesem Abschnitt entspricht. Die Methode bestimmt zuerst, welche Struktur übergeben wird. Anschließend wird bestimmt, ob der Befehl anhand seines Offsets oder seines Verbs identifiziert wird. Wenn lpVerb oder lpVerbW ein gültiges Verb oder einen gültigen Offset enthält, zeigt die Methode ein Meldungsfeld an.

STDMETHODIMP CShellExtension::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
    BOOL fEx = FALSE;
    BOOL fUnicode = FALSE;

    if(lpcmi->cbSize == sizeof(CMINVOKECOMMANDINFOEX))
    {
        fEx = TRUE;
        if((lpcmi->fMask & CMIC_MASK_UNICODE))
        {
            fUnicode = TRUE;
        }
    }

    if( !fUnicode && HIWORD(lpcmi->lpVerb))
    {
        if(StrCmpIA(lpcmi->lpVerb, m_pszVerb))
        {
            return E_FAIL;
        }
    }

    else if( fUnicode && HIWORD(((CMINVOKECOMMANDINFOEX *) lpcmi)->lpVerbW))
    {
        if(StrCmpIW(((CMINVOKECOMMANDINFOEX *)lpcmi)->lpVerbW, m_pwszVerb))
        {
            return E_FAIL;
        }
    }

    else if(LOWORD(lpcmi->lpVerb) != IDM_DISPLAY)
    {
        return E_FAIL;
    }

    else
    {
        MessageBox(lpcmi->hwnd,
                   "The File Name",
                   "File Name",
                   MB_OK|MB_ICONINFORMATION);
    }

    return S_OK;
}

IContextMenu::QueryContextMenu-Methode

Die Shell ruft IContextMenu::QueryContextMenu auf, um dem Kontextmenühandler zu ermöglichen, Menüelemente zum Menü hinzuzufügen. Sie übergibt das Handle HMENU im Parameter hmenu. Der Parameter indexMenu wird auf den Index festgelegt, der für das erste hinzuzufügende Menüelement verwendet werden soll.

Alle vom Handler hinzugefügten Menüelemente müssen über Bezeichner verfügen, die zwischen den Werten in den Parametern idCmdFirst und idCmdLast liegen. In der Regel wird der erste Befehlsbezeichner auf idCmdFirst festgelegt, und dieser Parameter wird für jeden zusätzlichen Befehl um 1 erhöht. Diese Vorgehensweise hilft Ihnen, ein Überschreitenvon idCmdLast zu vermeiden und die Anzahl der verfügbaren Bezeichner zu maximieren, falls die Shell mehrere Handler aufruft.

Der Befehlsoffset eines Elementbezeichners ist der Unterschied zwischen dem Bezeichner und dem Wert in idCmdFirst. Speichern Sie den Offset jedes Elements, das der Handler dem Kontextmenü hinzufügt, da die Shell diesen möglicherweise zum Identifizieren des Elements verwenden kann, wenn sie anschließend IContextMenu::GetCommandString oder IContextMenu::InvokeCommand aufruft.

Außerdem sollten Sie jedem hinzugefügten Befehl ein Verb zuweisen. Ein Verb ist eine Zeichenfolge, die anstelle des Offsets verwendet werden kann, um den Befehl zu identifizieren, wenn IContextMenu::InvokeCommand aufgerufen wird. Es wird auch von Funktionen wie ShellExecuteEx zum Ausführen von Kontextmenübefehlen verwendet.

Es gibt drei Flags, die über den Parameter uFlags übergeben werden können und für Kontextmenühandler relevant sind. Diese werden in der folgenden Tabelle beschrieben.

Flag Beschreibung
CMF_DEFAULTONLY Ein Benutzer oder eine Benutzerin hat den Standardbefehl ausgewählt, in der Regel durch Doppelklicken auf das Objekt. IContextMenu::QueryContextMenu sollte die Steuerung an die Shell zurückgeben, ohne das Menü zu ändern.
CMF_NODEFAULT Kein Element im Menü sollte das Standardelement sein. Die Methode sollte dem Menü ihre Befehle hinzufügen.
CMF_NORMAL Das Kontextmenü wird auf normale Weise angezeigt. Die Methode sollte dem Menü ihre Befehle hinzufügen.

 

Verwenden Sie InsertMenu oder InsertMenuItem, um der Liste Menüelemente hinzuzufügen. Geben Sie dann einen HRESULT-Wert mit auf SEVERITY_SUCCESS festgelegtem Schweregrad zurück. Legen Sie den Codewert auf den Offset des größten zugewiesenen Befehlsbezeichners plus 1 fest. Ein Beispiel: idCmdFirst ist auf „5“ festgelegt, und Sie fügen dem Menü drei Elemente mit den Befehlsbezeichnern „5“, „7“ und „8“ hinzu. Der Rückgabewert sollte MAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 - 5 + 1) lauten.

Das folgende Beispiel zeigt eine einfache Implementierung von IContextMenu::QueryContextMenu, die einen einzelnen Befehl einfügt. Der Bezeichneroffset für den Befehl ist IDM_DISPLAY und auf null festgelegt. Die Variablen m_pszVerb und m_pwszVerb sind private Variablen, die zum Speichern der zugeordneten sprachunabhängigen Verbzeichenfolge sowohl im ANSI- als auch im Unicode-Format verwendet werden.

#define IDM_DISPLAY 0

STDMETHODIMP CMenuExtension::QueryContextMenu(HMENU hMenu,
                                              UINT indexMenu,
                                              UINT idCmdFirst,
                                              UINT idCmdLast,
                                              UINT uFlags)
{
    HRESULT hr;
    
    if(!(CMF_DEFAULTONLY & uFlags))
    {
        InsertMenu(hMenu, 
                   indexMenu, 
                   MF_STRING | MF_BYPOSITION, 
                   idCmdFirst + IDM_DISPLAY, 
                   "&Display File Name");

    
        
        hr = StringCbCopyA(m_pszVerb, sizeof(m_pszVerb), "display");
        hr = StringCbCopyW(m_pwszVerb, sizeof(m_pwszVerb), L"display");

        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_DISPLAY + 1));
    }

    return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
}

Weitere Aufgaben bei der Implementierung von Verben finden Sie unter Erstellen von Kontextmenühandlern.

Kontextmenüs und Kontextmenühandler

Verben und Dateizuordnungen

Auswählen eines statischen oder dynamischen Verbs für das Kontextmenü

Best Practices für Kontextmenühandler und Mehrfachauswahlverben

Erstellen von Kontextmenühandlern

Kontextmenüreferenz