Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Explorer-Kontextmenüpunkt unsichtbar ausführen (https://www.delphipraxis.net/186217-explorer-kontextmenuepunkt-unsichtbar-ausfuehren.html)

CodeX 14. Aug 2015 14:12

Explorer-Kontextmenüpunkt unsichtbar ausführen
 
Ich möchte gerne einen Menüpunkt aus dem Kontextmenü des Windows Explorers ausführen.
Nach einigem Hin und Her bin ich bei diesem Beitrag gelandet, der eine sehr gute Grundlage dafür bietet. Ich habe den Code etwas besser aufbereitet und nachfolgend eingefügt.

Was mir noch fehlt: Mit dem Code kann ich das gewünschte Kontextmenü anzeigen und manuell auf den Menüpunkt klicken, aber eigentlich möchte ich das ja gar nicht anzeigen. Das gefundene Menü soll unsichtbar bleiben. Stattdessen möchte ich dessen Items durchiterieren und die Aktion eines bestimmten Items ausführen.

Nach einigen Tests bin ich irgendwie ratlos, wie das gehen könnte. Kann mir dabei bitte jemand weiterhelfen?

Delphi-Quellcode:
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  ComObj, ShlObj, ActiveX;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure WndProc(var Message: TMessage); override;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;
  aContextMenu: IContextMenu;
  aContextMenu2: IContextMenu2;

implementation

{$R *.dfm}

procedure TForm1.WndProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_INITMENUPOPUP,
    WM_DRAWITEM,
    WM_MENUCHAR,
    WM_MEASUREITEM:
      if Assigned(aContextMenu2) then
      begin
        If (aContextMenu2.HandleMenuMsg(Message.Msg, Message.wParam, Message.lParam) <> NOERROR) then
          inherited WndProc(Message)
        else
          Message.Result := 0;
      end
      else
      inherited WndProc(Message);
  else
    inherited WndProc(Message);
  end;
end;

function SlashDirName(ADir: String): String;
var
  s: String;
  bRootDir: Boolean;
begin
  if ADir<>'' then
  begin
    s := ADir;
    bRootDir := ((Length(s)=3) and (s[2]=':')) or (s='\');
    if not bRootDir then
      if s[Length(s)]<>'\' then
        s:=s+'\';
    Result := s;
  end;
end;

function SHGetIDListFromPath(Path: TFileName; var ShellFolder: IShellFolder): PItemIDList;
var
  TempPath, NextDir: TFileName;
  SlashPos: Integer;
  Folder, subFolder: IShellFolder;
  PIDL, PIDLbase: PItemIDList;
  ParseStruct: TStrRet;
  ParseNAme: String;
  EList: IEnumIDList;
  DidGet: Cardinal;
  ScanParam: Integer;
begin
  SHGetDesktopFolder(Folder);
  SHGetSpecialFolderLocation(0, CSIDL_DRIVES, PIDLbase);

  OLECheck(Folder.BindToObject(PIDLbase, nil, IID_IShellFolder, Pointer(SubFolder)));
  TempPath := Path;
  NextDir:='';

  while Length(TempPath)>0 do
  begin
    SlashPos := Pos('\', TempPath);
    if SlashPos > 0 then
    begin
      if Pos(':', TempPath) > 0 then
        NextDir := Copy(TempPath, 1, 3)
      else
        NextDir := SlashDirName(NextDir) + Copy(TempPath, 1, SlashPos-1);
      TempPath := Copy(TempPath, SlashPos+1, Length(TempPath));
    end
    else
      begin
      if NextDir='' then NextDir:=TempPath
        else NextDir := SlashDirName(NextDir)+TempPath;
      TempPath := '';
      end;
    PIDL := PidlBase;
    ScanParam := SHCONTF_FOLDERS or SHCONTF_INCLUDEHIDDEN;
    if (NextDir=Path) and (not DirectoryExists(Path)) then
      ScanParam := ScanParam or SHCONTF_NONFOLDERS;

    if S_OK=SubFolder.EnumObjects(0, ScanParam, EList) then
    begin
      while S_OK=EList.Next(1, pidl, DidGet) do
      begin
        OLECheck(SubFolder.GetDisplayNameOf(PIDL, SHGDN_FORPARSING, ParseStruct));
        case ParseStruct.uType of
          STRRET_CSTR:  ParseName :=ParseStruct.cStr;
          STRRET_WSTR:  ParseName :=WideCharToString(ParseStruct.pOleStr);
          STRRET_OFFSET: Parsename :=PChar(DWORD(Pidl)+ParseStruct.uOffset);
        end;
        if UpperCase(Parsename)=UpperCase(NextDir) then
          Break;
      end
    end
    else
    begin
      Folder := nil;
      Result := nil;
      Exit;
    end;

    if DidGet=0 then
    begin
      Folder := nil;
      Result := nil;
      Exit;
    end;
    PIDLBase := PIDL;
    Folder := subFolder;

    if not FileExists(NextDir) then
      OLECheck(Folder.BindToObject(Pidl, nil, IID_IShellFolder, Pointer(SubFolder)));
  end;
  ShellFolder := Folder;
  if ShellFolder = nil then
    Result := nil
  else
    Result := PIDL;
end;

procedure ContextMenuForFile(FileName: TFileName; X, Y: Integer; Handle: HWND);
var
  aPrgOut: Pointer;
  aPopup: HMENU;
  aCmd: Integer;
  aCmdInfo: TCMInvokeCommandInfo;
  PIDL: PItemIDList;
  ShellFolder: IShellFolder;
begin
  PIDL := SHGetIDListFromPath(FileName, ShellFolder);
  if not Assigned(PIDL) then
    Exit;
  aPrgOut := nil;
  OLECheck(ShellFolder.GetUIObjectOf(0, 1, PIDL, IID_IContextMenu, aPrgOut, Pointer(aContextMenu)));


  // Ab hier wird das Kontextmenü zusammengebaut und angezeigt
  // Stattdessen:
  // 1. Menüpunkte iterieren und gewünschten Eintrag erkennen (Text? ID?)
  // 2. Dessen Aktion ausführen

  aPopup := CreatePopUpMenu;
  if aPopup=0 then
    Exit;
  try
    OLECheck(aContextMenu.QueryContextMenu(aPopup, 0, 1, $7FFF, CMF_EXPLORE or CMF_CANRENAME));
    OLECheck(aContextMenu.QueryInterface(IID_IContextMenu2, aContextMenu2)); //To handle submenus.
    try
      aCmd := Integer(TrackPopupMenu(aPopup, TPM_LEFTALIGN or TPM_LEFTBUTTON or TPM_RIGHTBUTTON or TPM_RETURNCMD, X, Y, 0, Handle, nil));
      if aCmd<>0 then
      begin
        FillChar(aCmdInfo, Sizeof(aCmdInfo), 0);
        with aCmdInfo do
        begin
          cbSize := SizeOf(TCMInvokeCommandInfo);
          lpVerb := PAnsiChar(MakeIntResource(aCmd-1));
          nShow := SW_SHOWNORMAL;
        end;
        try
          aContextMenu.InvokeCommand(aCmdInfo);
        except
        end;
      end;
    finally
      aContextMenu2 := nil;
    end;
  finally
    DestroyMenu(aPopup);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ContextMenuForFile('C:\', 100, 100, Application.Handle);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  aContextMenu2 := nil;
end;


end.

Sir Rufo 14. Aug 2015 14:47

AW: Explorer-Kontextmenüpunkt unsichtbar ausführen
 
Oder mit anderen Worten gesagt:

Du möchtest gar nicht das Kontext-Menü, sondern nur den Inhalt (also die Aktionen) und dir daraus eine aussuchen und ausführen, so als ob diese über das Kontextmenü ausgelöst worden wäre.

So in etwa richtig?

CodeX 14. Aug 2015 14:51

AW: Explorer-Kontextmenüpunkt unsichtbar ausführen
 
Korrekt!

Aber um an diese Aktionen zu kommen, brauche ich ja erst das Kontextmenü, oder? Zumindest war das mein Gedankengang...

Sir Rufo 14. Aug 2015 15:01

AW: Explorer-Kontextmenüpunkt unsichtbar ausführen
 
Die Informationen, wie und wo was warum angezeigt/aufgerufen wird liegt zum einen Teil in der Registry.

Was für einen Menü-Eintrag - was für einen Aktion - soll denn ausgeführt werden? Meistens gibt es einen ganz anderen Weg dahin.

CodeX 14. Aug 2015 15:10

AW: Explorer-Kontextmenüpunkt unsichtbar ausführen
 
Hm, ich glaube, das würde hier zu weit weg von dem eigentlichen Wunsch wegführen. Das, was ich hier anfrage bzw. vorhabe, ist bereits mein letzter Stohhalm. Der normale API-Weg funktioniert in einer bestimmten Konstellation nur fehlerhaft. Über das Kontextmenü des Explorers geht es allerdings. Das Problem ist hier bereits durch mehrere Hände gegangen und es gibt dazu auch Diskussionen bei MSDN, die zu keiner Lösung führen.

Es wird also definitiv nur über einen Workaround führen und mit dem hier beschriebenen Weg, sehe ich die Möglichkeit für einen solchen Workaround.

Edit: Wenn es dich wirklich interessiert, beschreibe ich Dir das Problem gerne per PM bzw. sende Links dazu. Ich befürchte einfach, wenn ich das hier beschreibe, geht die eigentliche Frage komplett unter. ;)

Luckie 14. Aug 2015 15:45

AW: Explorer-Kontextmenüpunkt unsichtbar ausführen
 
Was soll der Code denn eigentlich machen? Ich werde aus der riesen Prozedur nicht so ganz schlau? Und ich kann mir nicht vorstellen, dass das nur über das Kontext Menü des Explorers gescheit funktionieren soll.

CodeX 14. Aug 2015 15:52

AW: Explorer-Kontextmenüpunkt unsichtbar ausführen
 
Zitat:

Zitat von Luckie (Beitrag 1312155)
Was soll der Code denn eigentlich machen? Ich werde aus der riesen Prozedur nicht so ganz schlau?

Der Code ist ein vollständiges Programm. Wenn Du ein neues Projekt anlegst, den Code einfügst und den einen Aufruf einem Button hinzufügst, kannst Du beim Klick auf den Button das Kontextmenü öffnen, als hättest Du gerade einen Rechtsklick auf eine Datei oder ein Laufwerk im Explorer durchgeführt. In dem Fall wäre es das Kontextmenü von C:

p80286 14. Aug 2015 17:18

AW: Explorer-Kontextmenüpunkt unsichtbar ausführen
 
Da ich mit der Materie nicht so vertraut bin, habe ich mal bei MSDN nachgeschaut:
Zitat:

Methods

The IContextMenu interface has these methods.


Method

Description

GetCommandString
Gets information about a shortcut menu command, including the help string and the language-independent, or canonical, name for the command.

InvokeCommand
Carries out the command associated with a shortcut menu item.

QueryContextMenu
Adds commands to a shortcut menu.



Das InvokeCommand sollte doch sein was Du willst? oder hab ich das falsch verstanden?

Gruß
K-H

Luckie 14. Aug 2015 18:55

AW: Explorer-Kontextmenüpunkt unsichtbar ausführen
 
Zitat:

Zitat von CodeX (Beitrag 1312156)
Der Code ist ein vollständiges Programm. Wenn Du ein neues Projekt anlegst, den Code einfügst und den einen Aufruf einem Button hinzufügst, kannst Du beim Klick auf den Button das Kontextmenü öffnen, als hättest Du gerade einen Rechtsklick auf eine Datei oder ein Laufwerk im Explorer durchgeführt. In dem Fall wäre es das Kontextmenü von C:

Ja das habe ich verstanden. Aber letztendlich soll doch was passieren und zwar mehr als nur das Kontextmenü aufklappen zu lassen.

CodeX 14. Aug 2015 21:33

AW: Explorer-Kontextmenüpunkt unsichtbar ausführen
 
Zitat:

Zitat von p80286 (Beitrag 1312158)
Das InvokeCommand sollte doch sein was Du willst? oder hab ich das falsch verstanden?

Ja, das stimmt schon. Aber ich muss zugeben, dass ich diese Shell-Programmierung teilweise nicht so ganz verstehe. Deshalb komme ich leider auch nicht drauf, wie ich von dem IContextMenu das gewünschte Item zum Ausführen finde.

Nach
Delphi-Quellcode:
OLECheck(ShellFolder.GetUIObjectOf(0, 1, PIDL, IID_IContextMenu, aPrgOut, Pointer(aContextMenu)));
ist das Kontextmenü in aContextMenu drin. Aber nun muss ich da noch irgendie durchiterieren und den gewünschten Eintrag finden (anhand vom Text oder irgendeiner ID). Danach kann ich das ausführen.

Zitat:

Zitat von Luckie (Beitrag 1312161)
Aber letztendlich soll doch was passieren und zwar mehr als nur das Kontextmenü aufklappen zu lassen.

Wie gesagt: Nur finden, nicht aufklappen. Dann einen Eintrag davon ausführen. Genau so als hätte man das händisch getan.


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:17 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz