kontextmenü im explorer
-
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.
-
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