kontextmenü im explorer



  • ich möchte mein programm mit dem parameter der ausgewählten datei(en) vom kontextmenü des explorers aus aufrufen können.

    hat da jemand nen tipp?



  • Siehe FAQ, "Öffnen mit".



  • das meine ich nicht
    ich meine so ein eintrag im kontextenue des explorers ähnlich winzip.
    da steht dann z.b. "Mit winzip entpacken","Zu archiv hinzufürgen" etc



  • Hallo

    dann must du es noch mal genauer erklären, ich verstehe unter deiner Frage das gleiche wie Jansen

    bis bald
    akari



  • wie erzeuge ich diese einträge im explorer kontextmenü alá "mit winzip entpacken","zu archiv hinzufügen" etc?



  • Dazu brauchst du eine Shell Extension - einen ContextMenuHandler. Dazu gibt es diverse, ausführliche Beispiele bei codeproject.com und codeguru.com



  • hm..hat mir schonmal nen einblick gegeben aber auch nich wirklich geholfen, da das beispiel auf den seiten für vc++ ist.

    hat jemand was für den builder?

    grüße



  • Ich habe mir mal für ein anderes Programm, das ich so verwenden wollte, eine .reg-Datei geschrieben:

    Windows Registry Editor Version 5.00
    
    [HKEY_CLASSES_ROOT\*\shell]
    
    [HKEY_CLASSES_ROOT\*\shell\npp]
    @="&Bearbeiten mit Notepad++"
    
    [HKEY_CLASSES_ROOT\*\shell\npp\command]
    @="D:\Notepad++\notepad++.exe \"%1\""
    

    Wenn du den Pfad und den Namen anpasst, sollte das auch bei dir funktionieren.

    Getestet habe ich's aber nur unter Windows 2000 und XP. Evtl. funzt das unter Win9x nur mit ein paar Änderungen.

    Zum Installieren in die Registry einfach als *.reg abspeichern und Datei im Explorer öffnen.



  • Allerdings ist es bei dem klassischen Shell Extension-Eintrag in der Registrierung meines Wissens nach nicht möglich, mehrere markierte Dateien zu übergeben sondern es wird die Befehlszeile für jede markierte Datei separat ausgeführt.



  • Hallo an alle C++ Builder-Profis,

    dies ist leider der einzige Eintrag zum Thema "ContextMenuHandler" den ich in der SuFu dieses Forums gefunden habe und der zudem auch schon deutlich älter ist.

    Leider hat mir das intensive Suchen in diversen anderen Portalen keine passenden Lösungen gebracht, die sich für mich sinnvoll im C++ Builder verwenden bzw. übertragen lassen.

    Ich möchte gerne das Kontextmenü der rechten Maustaste vom Windows Explorer mit eigenen Menü-Einträgen sowie Untermenüs erweitern, die beim Klicken mein eigenes Programm steuern, ähnlich wie es auch z.B. WinRAR oder WinZIP machen.
    Das ganze möchte ich dann auch gerne ( eben wie bei WinZIP oder WinRAR auch ) in eine DLL einbinden, die ich dann in Windows über regsvr32.exe registriere.

    Kennt oder hat jemand von euch vielleicht ein brauchbares C++ Builder-Codebeispiel zum Thema "ContextMenuHandler".

    Vielen Dank für Eure Hilfe.

    Gruß
    Andreas



  • Hallo

    Da solltest du lieber im WinAPI-Forum nachfragen. Denn ich glaube nicht, das es dafür spezielle VCL-Wrapper gibt.

    bis bald
    akari



  • Ich habe mir mal Einträg ins Kontextmenü gemacht um Dateien mit einem Editor zu öffnen.
    Dies waren jedoch "nur" Einträge in der Registry.



  • @Stephan

    Ich habe mir mal Einträg ins Kontextmenü gemacht um Dateien mit einem Editor zu öffnen.
    Dies waren jedoch "nur" Einträge in der Registry.

    Ja, das ist soweit auch richtig. Du meinst den Eintrag

    HKEY_CLASSES_ROOT\*\Shell\
    

    So "behelfe" ich mir ja im Moment auch gerade.
    Jedoch habe ich jetzt schon 11 Einträge im Kontextmenü und es sollen noch einige mehr werden.
    Deswegen möchte ich gerne einen Eintrag im Kontextmenü vornehmen der noch ein weiteres Untermenü beinhaltet ( halt so wie bei WinZIP auch ).
    In diesem Untermenü sollen dann alle Einträge gelistet sein.
    Meines Wissens nach benötigt man dafür einen "ContextMenuHandler".

    Ich habe ja auch schon diverse Codebeispiele gefunden, aber meist in anderen Sprachen oder z.B. mit VC++ programmiert.

    Das Prinzip ist soweit auch eigentlich klar.
    Der Code muss in eine DLL die in Windows registriert werden muss.
    Dann gehören einige Methoden zum Handler dazu, wie z.B.

    QueryContextMenu
    InvokeCommand
    usw.
    

    Aber wie dass nun genau in Borland C++ auszusehen hat............ 😕

    Gruß
    Andreas



  • Hallo

    Das macht man im Builder genauso wie im jedem anderen C/C++ Compiler für Windows : über die WinAPI-Schnittstelle. Deshalb mein Hinweis auf das WinAPI-Forum.

    bis bald
    akari



  • Hallo Akari,

    dort habe ich natürlich auf Grund deines ersten Posts nachgeschaut.
    Habe auch die SuFu gequält. Aber das was ich dort gefunden habe, war noch ernüchternder als das was ich hier gefunden habe.

    Werde also nochmal dort meine Frage posten.

    Gruß
    Andreas



  • Hallo an ALLE,

    bin nun endlich fündig geworden. Die Lösung habe ich im "embarcadero developer network" gefunden. Für alle die es interessiert, stelle ich hier mal meine Lösung zur Verfügung.

    http://edn.embarcadero.com/article/26650

    Ist zwar alles im ersten Moment etwas unübersichtlich, wenn man das ganze dann aber erst mal durchschaut hat, funktioniert alles perfekt.
    Das im o.g. Beispiel notwendige Auskommentieren der Zeilen in der ShlObj.hpp war bei mir nicht notwendig.

    Stattdessen trat bei mir beim Compilieren immer ein Linkerfehler auf.

    [Linker Fehler] Error: Ungelöste externe 'Sysutils::EDivByZero::' referenziert von x:\BUILDER2006\LIB\xxxx.LIB|xx
    

    Hier half bei mir nur die rtl.lib und vcl.lib dem Projekt hinzuzufügen.

    Natürlich habe ich dann alles noch an meine Bedürfnisse angepasst und
    dem Ganzen dann noch ein Untermenü mit Icon verpasst.
    Dazu habe ich dann eine compilierte Ressourcen-Datei (*.res), die das Menü-Icon zur Verfügung stellt, dem Projekt hinzugefügt.
    Die Ressourcen-Datei habe ich mit dem "Resource Builder 3" erstellt. Der "Resource Builder 3" ist Freeware und kann unter http://www.resource-builder.com heruntergeladen werden.

    Mit der Initialize-Methode aus dem Beispiel lässt sich immer nur EINE Datei
    zeitgleich verarbeiten. Für mich war es aber notwendig, mehrere Dateien auf einmal zu verarbeiten.
    Der Code meiner neuen "Initialize"-Methode sieht jetzt wie folgt aus:

    //Dateinamen aller markierten Dateien auswerten und in einer globalen String-Liste ablegen
    STDMETHODIMP TMyContextMenuExtensionImpl::Initialize(LPCITEMIDLIST pidlFolder,
       LPDATAOBJECT lpdobj, HKEY hkeyProgID)
    {
    HDROP hDrop;
    HRESULT hResult;
    FORMATETC fmtEtc;
    STGMEDIUM medium;
    int uNumFiles = 0;
    int i = 0;
    String Filename = "";
    char cFilename[MAX_PATH] = {'\0'};
    
       fmtEtc.cfFormat = CF_HDROP;
       fmtEtc.ptd = NULL;
       fmtEtc.dwAspect = DVASPECT_CONTENT;
       fmtEtc.lindex = -1;
       fmtEtc.tymed = TYMED_HGLOBAL;
    
       medium.tymed = TYMED_HGLOBAL;
    
       // Get the CF_HDROP data
       if((hResult = lpdobj->GetData(&fmtEtc,&medium)) < 0)
          return E_INVALIDARG;
    
       if((hDrop = (HDROP)GlobalLock(medium.hGlobal))==NULL)
          return E_INVALIDARG;
    
       //Anzahl markierter Dateien zählen
       uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );
    
       //Globale StringList hier leeren
       g_slFilename->Clear();
    
       //Dateinamen aller markierten Dateien ermitteln und
       //in die StringList aufnehmen
       for(i = 0; i < uNumFiles; i++)
    	 {
    	       //Result auf OK
    		   hResult = S_OK;
    
    		   // Get the name of the file
    		   if(DragQueryFile(hDrop,i,cFilename,MAX_PATH) != 0)
    			 {
    				 //Von char-Array zu String
    				 Filename = cFilename;
    				 //String in die StringList aufnehmen
    				 g_slFilename->Add(Filename);
    			 }
    			 else
    				{
    				   hResult = E_INVALIDARG;
    				   break;
    				}
    	 }
    
       // Clean up
       GlobalUnlock(medium.hGlobal);
       ReleaseStgMedium(&medium);
    
       return hResult;
    };
    

    Meine neue "QuerryContextMenu"-Methode:

    STDMETHODIMP TMyContextMenuExtensionImpl::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idFirstCmd,
       UINT idLastCmd, UINT uFlags)
    {
    int i = 0;
    int ItemCount = 0;
    UINT cmd = idFirstCmd;
    HMENU hSubmenu = CreatePopupMenu();
    MENUITEMINFO mii = { sizeof(MENUITEMINFO) };
    
       //  Make sure we do nothing if default is in uFlags.
       if (uFlags & CMF_DEFAULTONLY)
          return MAKE_HRESULT(SEVERITY_SUCCESS,FACILITY_NULL,0);
    
       //Globale StringListen leeren
       g_DLLValues->Clear();
    
       //Hier fülle ich die globale String-List "g_DLLValues" zur Laufzeit 
       //mit Werten, da diese in meinem Programm immer variieren können.
       g_DLLValues->Add("Menüeintrag 1");
       g_DLLValues->Add("Menüeintrag 2");
       g_DLLValues->Add("Menüeintrag 3");
       g_DLLValues->Add("Menüeintrag 4");
    
       //Alle Kontextmenü-Einträge in das SubMenü aufnehmen
       for(i = 0; i < g_DLLValues->Count; i++)
    	 {
    		   //Add SubMenu-Item
    		   InsertMenu (hSubmenu,ItemCount,MF_BYPOSITION,cmd++,(g_DLLValues->Strings[i]).c_str());
    		   ItemCount++;
    	 }
    
       //SubMenü vervollständigen	 
       mii.fMask = MIIM_SUBMENU | MIIM_STRING | MIIM_ID;
       mii.wID = cmd++;
       mii.hSubMenu = hSubmenu;
       mii.dwTypeData = _T("Mein Programm-Menü");
    
       // Insert the submenu into the ctx menu provided by Explorer.
       InsertMenuItem ( hmenu, indexMenu, TRUE, &mii );
    
       //Icon für den Kontextmenü-Eintrag hinzufügen
       if (g_bmp->Handle != 0)
    	  SetMenuItemBitmaps(hmenu, indexMenu, MF_BYPOSITION, g_bmp->Handle, g_bmp->Handle);
    
       return MAKE_HRESULT(SEVERITY_SUCCESS,FACILITY_NULL, cmd - idFirstCmd);    //2
    };
    

    Meine angepasste Methode "InvokeCommand":

    STDMETHODIMP TMyContextMenuExtensionImpl::InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo)
    {
    HRESULT hResult;
    WORD cmdId;
    int intCMDID = -1;
    
       // We want to identify items using IDs and not Verbs.
       // So if lpVerb is pointing at a string, lets use the ID.
       if(HIWORD(pCmdInfo->lpVerb)==0)
    	  cmdId = LOWORD(pCmdInfo->lpVerb);
    
       intCMDID = cmdId;
    
       if(intCMDID > g_DLLValues->Count)
    	  return E_INVALIDARG;
    
       if(intCMDID > -1)
    	 {
    		 //Hier wird meine Methode aufgerufen, die in Abhängigkeit
             //der ID des gewählten Kontextmenü-Eintrages entsprechende
             //Arbeit durchführt
    		 MeineMethodeAufrufen(intCMDID);
    
             hResult = S_OK;
    	 }
    	 else
    		{
    			hResult = E_INVALIDARG;
    		}
    
         return hResult;
    }
    

    Die Initialisierungen der globalen Variablen habe ich in einer eigenen Methode
    vorgenommen, die ich im Konstruktor der DLL aufrufe.
    HINWEIS: Die Konstruktor-Methode befindet sich übrigens in der Header-Datei. Der Destruktor muss selber noch im Header definiert werden.

    void TMyContextMenuExtensionImpl::Initialisieren(void)
    {
    	//Alle globalen Variablen initialisieren
    	g_bmp = new Graphics::TBitmap();
    	try
    	  {       //Bild "CONTICON" aus der Ressourcen-Datei laden
    		  g_bmp->LoadFromResourceName(int(HInstance), "CONTICON");
    	  }
    	catch(...)
    	  {
    
    	  }
    
    	//g_bmp->LoadFromFile("C:\\Neues Bild.BMP"); //Alternativ kann das Bild auch direkt aus einer Datei geladen werden
    	g_DLLValues = new TStringList();
    	g_DLLParams = new TStringList();
    	g_slFilename = new TStringList();
    }
    

    Das Freigeben des Speichers entsprechend über den Destruktor aufrufen:

    void TMyContextMenuExtensionImpl::Deinitialisieren(void)
    {
    	//Alle globalen Variablen löschen und Speicher freigeben
    	delete g_bmp;
    	delete g_DLLValues;
    	delete g_DLLParams;
    	delete g_slFilename;
    }
    //--------------------------------------------------------------------
    

    Gruß
    Andreas


Anmelden zum Antworten