AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi ContextMenuHandlers in eigene PopupMenüs einbinden
Thema durchsuchen
Ansicht
Themen-Optionen

ContextMenuHandlers in eigene PopupMenüs einbinden

Ein Thema von franz · begonnen am 29. Dez 2003 · letzter Beitrag vom 7. Apr 2006
Antwort Antwort
Seite 2 von 2     12   
franz

Registriert seit: 23. Dez 2003
Ort: Bad Waldsee
112 Beiträge
 
Delphi 5 Professional
 
#11

Re: ContextMenuHandlers in eigene PopupMenüs einbinden

  Alt 12. Feb 2004, 22:39
Das eine Problem ist noch nicht gelöst. So lange keine eigenen Menüeinträge hinzugefügt werden oder das Menü mit der Maus bedient wird, funktioniert es zumindest mit Windows 98.

Ich habe folgendes geschrieben:

Delphi-Quellcode:

type
  TShellMenuKind = (smkOnlyHandler, smkComplete, smkDefaultOnly, smkNoDefault);

type
  TForm1 = class(TForm)

  public
    { Public-Deklarationen }
    procedure WndProc(var Message: TMessage); override;


implementation

uses
  ShellApi, ShlObj, ComObj, FileCtrl, ActiveX, CommCtrl;
{$R *.DFM}

var
  mContextMenu: IContextMenu;
  mContextMenu2: IContextMenu2;
  iHandlerIndex, iHandlerCount: Integer;
  CanDraw, ValidCmdArea: Boolean;

function SHGetIDListFromPath(FileName: TFileName; var ShellFolder: IShellFolder): PItemIDList;
var
  sParseName: String;
  mTempPath, mNextDir: TFileName;
  iScanParam: Integer;
  iDidGet: Cardinal;
  mFolder, mSubFolder: IShellFolder;
  mPIDL, mPIDLbase: PItemIDList;
  mParseStruct: TStrRet;
  mEList: IEnumIDList;

  procedure GetDirs(var TempPath, NextDir: TFileName);
  var
    iSlashPos: Integer;

    function SlashDirName(ADir: String): String;
    var
      S: String;
      RootDir: Boolean;
    begin
      if ADir <> 'then
         begin
           S := ADir;
           RootDir := ((Length(S)=3) and (S[2]=':')) or (S='\');
           if not RootDir then
              if S[Length(S)] <> '\then
                 S := S + '\';
           Result := S;
         end;
    end;
  begin
    iSlashPos := Pos('\', TempPath);
    if iSlashPos > 0 then
       begin
         if Pos(':', TempPath) > 0 then
            NextDir := Copy(TempPath, 1, 3)
         else
           NextDir := SlashDirName(NextDir) + Copy(TempPath, 1, iSlashPos - 1);
         TempPath := Copy(TempPath, iSlashPos + 1, Length(TempPath));
       end
    else
      begin
        if NextDir = 'then
           NextDir := TempPath
        else
          NextDir := SlashDirName(NextDir) + TempPath;
        TempPath := '';
      end;
  end;
begin
  SHGetDesktopFolder(mFolder);
  SHGetSpecialFolderLocation(0, CSIDL_DRIVES, mPIDLbase);

  OLECheck(mFolder.BindToObject(mPIDLbase, nil, IID_IShellFolder, Pointer(mSubFolder)));
  mTempPath := FileName;
  mNextDir := '';

  while Length(mTempPath) > 0 do
    begin
      GetDirs(mTempPath,mNextDir);

      mPIDL := mPidlBase;
      iScanParam := SHCONTF_FOLDERS or SHCONTF_INCLUDEHIDDEN;
      if (mNextDir = FileName) and (not DirectoryExists(FileName)) then
         iScanParam := iScanParam or SHCONTF_NONFOLDERS;

      if S_OK = mSubFolder.EnumObjects(0, iScanParam, mEList) then
         while S_OK = mEList.Next(1, mPIDL, iDidGet) do
           begin
             OLECheck(mSubFolder.GetDisplayNameOf(mPIDL, SHGDN_FORPARSING, mParseStruct));
             case mParseStruct.uType of
               STRRET_CSTR: sParseName := mParseStruct.cStr;
               STRRET_WSTR: sParseName := WideCharToString(mParseStruct.pOleStr);
               STRRET_OFFSET: sParseName := PChar(DWORD(mPIDL) + mParseStruct.uOffset);
             end;

             if UpperCase(sParseName) = UpperCase(mNextDir) then
                Break;
           end
      else
        begin
          mFolder := nil;
          Result := nil;
          Exit;
        end;

      if iDidGet = 0 then
         begin
           mFolder := nil;
           Result := nil;
           Exit;
         end;

      mPIDLBase := mPIDL;
      mFolder := mSubFolder;

      if not FileExists(mNextDir) then
         OLECheck(mFolder.BindToObject(mPIDL, nil, IID_IShellFolder, Pointer(mSubFolder)));
    end;

  ShellFolder := mFolder;
  if ShellFolder = nil then
     Result := nil
  else
    Result := mPIDL;
end;

procedure ContextMenuForFile(FileName: TFileName; HandlerIndex, X, Y: Integer;
            Handle: HWND; PopupMenu: TPopupMenu; MenuType: TShellMenuKind);
var
  mPopup: HMENU;
  iCmd, iID, iRes, iCmdLast: Integer;
  iFlags: Cardinal;
  mCmdInfo: TCMInvokeCommandInfo;
  mPIDL: PItemIDList;
  mShellFolder: IShellFolder;
  aMenuBitmaps: array of TBitmap;

  function ExecMenuItemAction(Cmd: Integer; var ID: Integer; Item: TMenuItem): Boolean;
  var
    ix: Integer;
  begin
    Result := false;

    ix := 0;
    while ix < Item.Count do
      begin
        Application.ProcessMessages;

        if Cmd = ID then
           begin
             Item[ix].Click;
             Result := true;
             Exit;
           end;

        if Item.Items[ix].Count > 0 then
           ExecMenuItemAction(Cmd,ID,Item.Items[ix]);

        Inc(ID);
        Inc(ix);
      end;
  end;

  function AddVCLMenuItems(APIMenu: HMENU; var ID: Integer;
    Item: TMenuItem; CanAddLine: Boolean; ArrayPos: Integer): Integer;
  var
    ix: Integer;
    SubMenu: HMenu;
    mBitmap: TBitmap;

    function GetItemFlags(Item: TMenuItem): Cardinal;
    begin
      Result := MF_STRING;
      if Item.Checked then
         Result := Result or MF_CHECKED;
      if not Item.Enabled then
         Result := Result or MF_GRAYED;
      if Item.Caption = cLineCaption then
         Result := Result or MF_SEPARATOR;
    end;

    procedure AddMenuBitmap(MnuID: Integer; MnuItm: TMenuItem);
    begin
      SetLength(aMenuBitmaps,ArrayPos + 1);
      aMenuBitmaps[ArrayPos] := TBitmap.Create;
      aMenuBitmaps[ArrayPos].Width := 14;
      aMenuBitmaps[ArrayPos].Height := 14;

      if (Assigned(MnuItm.Bitmap)) and (not MnuItm.Bitmap.Empty) then
         begin
           aMenuBitmaps[ArrayPos].Canvas.StretchDraw(Rect(0,0,16,16),MnuItm.Bitmap);
           aMenuBitmaps[ArrayPos].TransparentColor := MnuItm.Bitmap.Canvas.Pixels[0,0];
           aMenuBitmaps[ArrayPos].Transparent := true;
         end
      else
        if (MnuItm.ImageIndex > -1) and (Assigned(PopupMenu.Images)) then
           begin
             mBitmap := TBitmap.Create;
             try
               PopupMenu.Images.GetBitmap(MnuItm.ImageIndex,mBitmap);
               aMenuBitmaps[ArrayPos].Canvas.StretchDraw(Rect(0,0,13,13),mBitmap);
             finally
               mBitmap.Free;
             end;
           end;

      SetMenuItemBitmaps(APIMenu,MnuID,MF_BYCOMMAND,aMenuBitmaps[ArrayPos].Handle,aMenuBitmaps[ArrayPos].Handle);
      Inc(ArrayPos);
    end;
  begin
    Result := 0;

    if CanAddLine then
       AppendMenu(APIMenu,MF_SEPARATOR,0,nil);

    ix := 0;
    while ix < Item.Count do
      begin
        Inc(Result);
        Application.ProcessMessages;

        if Item.Items[ix].Visible then
           begin
             // Item hinzufügen
             if Item.Items[ix].Count > 0 then
                begin // Untermenü erstellen, falls nötig
                  SubMenu := CreatePopupMenu;
                  Inc(Result,AddVCLMenuItems(SubMenu,ID,Item.Items[ix],false,ArrayPos));
                  AppendMenu(APIMenu,GetItemFlags(Item.Items[ix]) or MF_POPUP,SubMenu,PChar(Item.Items[ix].Caption));

                  // Check und RadioItem hinzufügen
                  if Item.Items[ix].Default then
                     SetMenuDefaultItem(APIMenu,ID,0);
                  if (Item.Items[ix].Checked) and (Item.Items[ix].RadioItem) then
                     CheckMenuRadioItem(APIMenu,ID,ID,ID,MF_BYCOMMAND);

                 // Bitmap hinzufügen
                 if ((Assigned(Item.Items[ix].Bitmap)) and (not Item.Items[ix].Bitmap.Empty)) or
                    ((Assigned(PopupMenu.Images)) and (Item.Items[ix].ImageIndex > -1)) and
                    (not Item.Items[ix].Checked) then
                    AddMenuBitmap(ID,Item.Items[ix]);

                  Inc(ID);
                end
             else
               begin // Menüeintrag hinzufügen
                 AppendMenu(APIMenu,GetItemFlags(Item.Items[ix]),ID,PChar(Item.Items[ix].Caption));

                 // Check und RadioItem hinzufügen
                 if Item.Items[ix].Default then
                    SetMenuDefaultItem(APIMenu,ID,0);
                 if (Item.Items[ix].Checked) and (Item.Items[ix].RadioItem) then
                    CheckMenuRadioItem(APIMenu,ID,ID,ID,MF_BYCOMMAND);

                 // Bitmap hinzufügen
                 if ((Assigned(Item.Items[ix].Bitmap)) and (not Item.Items[ix].Bitmap.Empty)) or
                    ((Assigned(PopupMenu.Images)) and (Item.Items[ix].ImageIndex > -1)) and
                    (not Item.Items[ix].Checked) then
                    AddMenuBitmap(ID,Item.Items[ix]);
               end;
           end;
        Inc(ID);
        Inc(ix);
      end;
  end;
begin
  mPIDL := SHGetIDListFromPath(FileName, mShellFolder);
  if not Assigned(mPIDL) then
     Exit;

  OLECheck(mShellFolder.GetUIObjectOf(Handle, 1, mPIDL, IID_IContextMenu, nil,
    Pointer(mContextMenu)));

  mPopup := CreatePopUpMenu;
  if mPopup = 0 then
     Exit;
  try
    // VCL Menüeinträge
    iID := 1;
    iRes := AddVCLMenuItems(mPopup,iID,PopupMenu.Items,true,0);
    iHandlerCount := GetMenuItemCount(mPopup);

    // Einfügeposition korrigieren
    // Wichtig! Da sonst die "Senden an" Menüeinträge in das falsche
    // Untermenü gezeichnet werden!
    if (HandlerIndex > PopupMenu.Items.Count) then
       HandlerIndex := PopupMenu.Items.Count;

    if (PopupMenu.Items.Count > 0) and (HandlerIndex < PopupMenu.Items.Count) then
       if PopupMenu.Items[HandlerIndex].Count > 0 then
          while (HandlerIndex < PopupMenu.Items.Count) and
                (PopupMenu.Items[HandlerIndex].Count > 0) do
                Inc(HandlerIndex);

    // Handler hinzufügen
    iCmdLast := 0;
    iFlags := CMF_NORMAL;
    case MenuType of
      smkOnlyHandler: begin
                        iCmdLast := 0;
                        iFlags := CMF_NORMAL;
                      end;
      smkComplete: begin
                        iCmdLast := $7FFF;
                        iFlags := CMF_NORMAL;
                      end;
      smkDefaultOnly: begin
                        iCmdLast := $7FFF;
                        iFlags := CMF_DEFAULTONLY;
                      end;
      smkNoDefault: begin
                        iCmdLast := $7FFF;
                        iFlags := CMF_NODEFAULT;
                      end;
    end;

    OLECheck(mContextMenu.QueryContextMenu(mPopup, HandlerIndex + 1, HandlerIndex + 1, iCmdLast, iFlags));
    OLECheck(mContextMenu.QueryInterface(IID_IContextMenu2, mContextMenu2));
    try
      iHandlerCount := GetMenuItemCount(mPopup) - iHandlerCount + 1;
      if PopupMenu.Items.Count > 0 then
         iHandlerIndex := HandlerIndex
      else
        iHandlerIndex := 0;

      iCmd := Integer(TrackPopupMenuEx(mPopup, TPM_LEFTALIGN or
                TPM_RIGHTBUTTON or TPM_HORIZONTAL or TPM_VERTICAL or TPM_RETURNCMD, X, Y, Handle, nil));

      if not (iCmd in [0..iRes]) then
         Dec(iCmd,HandlerIndex);

      // "OnClick" Ereignisse ausführen
      if (MenuType <> smkOnlyHandler) and (iCmd = 1) and
         (PopupMenu.Items.Count > 0) then
         PopupMenu.Items[0].Click
      else
        if iCmd <> 0 then
           begin
             if (ValidCmdArea) or (MenuType = smkDefaultOnly) or
                (MenuType = smkNoDefault) or (PopupMenu.Items.Count = 0) then
                begin // OnClick des Shell Menus
                  FillChar(mCmdInfo, SizeOf(mCmdInfo), 0);
                  with mCmdInfo do
                    begin
                      cbSize := SizeOf(TCMInvokeCommandInfo);
                      lpVerb := MakeIntResource(iCmd - 1);
                      nShow := SW_SHOWNORMAL;
                    end;
                  try
                    if not Succeeded(mContextMenu.InvokeCommand(mCmdInfo)) then
                       begin // Wenn die Ausführung fehlgeschlagen ist
                         iID := 1;
                         ExecMenuItemAction(iCmd,iID,PopupMenu.Items);
                       end;
                  except
                    // nichts tun
                  end;
                end
             else
               begin // VCL OnClick ausführen
                 iID := 1;
                 ExecMenuItemAction(iCmd,iID,PopupMenu.Items);
               end;
           end;
    finally
      mContextMenu := nil;
      mContextMenu2 := nil;
    end;
  finally
    DestroyMenu(mPopup);
  end;
end;

// Wenn der Benutzer mit der rechten Maustaste klickt Menü anzeigen
procedure TForm1.PopupMenu1Popup(Sender: TObject);
begin
  DestroyMenu((Sender as TPopupMenu).Handle);
  ContextMenuForFile('C:\Eigene Dateien\_Test\Test.txt',3,Mouse.CursorPos.x,
    Mouse.CursorPos.y,Handle,(Sender as TPopupMenu),smkComplete);
end;

procedure TForm1.WndProc(var Message: TMessage);
var
  iMenuPos: Integer;
begin
  case Message.Msg of
    WM_MENUSELECT: begin
                      // Prüfen, ob das Owner Draw Shell Popup Menü (mPopupMenu2) gezeichnet werden kann
                      iMenuPos := Integer(MenuItemFromPoint(Handle,HMENU(Message.LParam),TPoint(Mouse.CursorPos)));
                      if GetSubMenu(HMENU(Message.LParam),iMenuPos) > 0 then
                         CanDraw := iMenuPos in [(iHandlerIndex)..(iHandlerIndex + iHandlerCount)];
                      ValidCmdArea := CanDraw;
                      inherited WndProc(Message);
                    end;
    WM_INITMENUPOPUP,
    WM_DRAWITEM,
    WM_MENUCHAR,
    WM_MEASUREITEM: begin
                      // Owner Draw Shell Popup Menü zeichnen
                      if (Assigned(mContextMenu2)) and (CanDraw) then
                         begin
                            If (mContextMenu2.HandleMenuMsg(Message.Msg, Message.wParam, Message.lParam) <> NOERROR) then
                               inherited WndProc(Message)
                            else
                              Message.Result := 0;
                         end
                      else
                        inherited WndProc(Message);
                    end;
  else
    inherited WndProc(Message);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  mContextMenu2 := nil;
end;
Das Menü braucht nicht extra aufgerufen werden. Wenn der Benutzer die rechte Maustaste drückt wird das Menü angezeigt.

Allerdings muss folgende Ereignisbehandlungsroutine für „OnPopup“ (bei TPopupMenu) oder „OnBeforePopup“ (bei TFJFPopupMenu) geschrieben werden:

Delphi-Quellcode:
procedure TForm1.PopupMenu1Popup(Sender: TObject);
begin
  DestroyMenu((Sender as TPopupMenu).Handle);
  ContextMenuForFile('C:\Eigene Dateien\_Test\Test.txt',3,Mouse.CursorPos.x,
    Mouse.CursorPos.y,Handle,(Sender as TPopupMenu),smkOnlyHandler);
end;
Der letzte Parameter von „ContextMenuForFile“ gibt an, welcher Teil des Shell Menüs angezeigt werden soll.

Zugegeben es ist etwas viel geworden, aber wenigstens funktioniert es einigermaßen.

Der nächste Schritt ist die Erstellung einer Komponente. Allerdings muss die Komponente „WndProc“ von „TForm“ überschreiben, damit diese funktioniert. Ich habe schon einiges probiert. Mit „TApplication.OnMessage“ funktioniert es nicht und mit einem Hook stürzt Windows ab!

Vielleicht hat jemand eine Idee.

PS: Bitte lasst mich nicht wieder hängen. Auch wenn Ihr keine Lösung wisst bitte meldet euch, dann weiß ich wenigstens, dass dieses Thema von jemandem gelesen wird.
  Mit Zitat antworten Zitat
Assarbad
(Gast)

n/a Beiträge
 
#12

Re: ContextMenuHandlers in eigene PopupMenüs einbinden

  Alt 14. Feb 2004, 19:06
Gelesen, aber keine Idee.
  Mit Zitat antworten Zitat
franz

Registriert seit: 23. Dez 2003
Ort: Bad Waldsee
112 Beiträge
 
Delphi 5 Professional
 
#13

Re: ContextMenuHandler in eigene PopupMenüs einbinden

  Alt 16. Feb 2004, 22:50
Ich habe jetzt das letzte Problem mit „GetMenuItemInfo“ gelöst und außerdem einige Änderungen vorgenommen. Jetzt funktioniert alles, zumindest mit Windows 98, soweit. Bleibt noch die Komponente.

Ich habe mal das gesamte Testprojekt beigefügt, bei deren Verwendung die Angabe der Dateipfade zu beachten ist.
Angehängte Dateien
Dateityp: zip handler3.zip (7,7 KB, 38x aufgerufen)
  Mit Zitat antworten Zitat
franz

Registriert seit: 23. Dez 2003
Ort: Bad Waldsee
112 Beiträge
 
Delphi 5 Professional
 
#14

Re: ContextMenuHandlers in eigene PopupMenüs einbinden

  Alt 22. Feb 2004, 22:22
Damit die Komponente funktionierte, musste TApplication.HookMainWindow verwendet werden. Solange keine Untermenüs im eigenen Menü verwendet werden, funktioniert es.

Die restlichen kleineren Macken werde ich demnächst beheben.

Wer die Komponente haben will, kann Sie sich downloaden.
Angehängte Dateien
Dateityp: zip fjfshellpopupmenu.zip (18,3 KB, 30x aufgerufen)
  Mit Zitat antworten Zitat
franz

Registriert seit: 23. Dez 2003
Ort: Bad Waldsee
112 Beiträge
 
Delphi 5 Professional
 
#15

Re: ContextMenuHandlers in eigene PopupMenüs einbinden

  Alt 22. Mär 2004, 22:52
Die bisherige Komponente hat leider noch einige schwere Fehler.
Hier habt Ihr eine verbesserte Version.
Angehängte Dateien
Dateityp: zip shellpopupmenu.zip (18,8 KB, 95x aufgerufen)
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.105 Beiträge
 
Delphi 11 Alexandria
 
#16

Re: ContextMenuHandlers in eigene PopupMenüs einbinden

  Alt 23. Mär 2004, 00:11
Moin Franz,

würdest Du bitte mal Deinen überlangen Beitrag auch in ein Attachement verwandeln.
Danke.
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Benutzerbild von quirks
quirks

Registriert seit: 5. Sep 2004
Ort: Fischbachtal
46 Beiträge
 
Delphi 8 Professional
 
#17

Re: ContextMenuHandlers in eigene PopupMenüs einbinden

  Alt 20. Feb 2005, 18:17
Genau das hab ich ewig gesucht!

Hab aber noch etwas verbessert: Die Kompo hat immer überprüft, ob es die Datei gibt, deren Kontextmenü man zeigen will. Aber es gibt doch auch Verzeichnisse, auf die man das anwenden kann...
Also alle (FileExists(FileName)) durch ((FileExists(FileName)) or (DirectoryExists(Filename))) ersetzt. Jetzt kann man auch Kontextmenüs von Verzeichnissen anzeigen lassen.

Hab die veränderte Kompo-Datei mal angehängt.
Angehängte Dateien
Dateityp: zip fjfshellpopupmenu_153.zip (4,8 KB, 59x aufgerufen)
  Mit Zitat antworten Zitat
jonx

Registriert seit: 7. Apr 2006
2 Beiträge
 
#18

Re: ContextMenuHandlers in eigene PopupMenüs einbinden

  Alt 7. Apr 2006, 05:00
hallo,
wirklich netten component...
aber kennten sie nicht de itemprop component?
oder hab ich etwas nicht verchtanden?

so, warum bin ich hier?
weil ich auch etwas versuche und bring es nicht hin.

was ich gerne tun mochte, ist den popup menu zu meinen eigenen menu so anhängen...
schaut euch bitte meinen Beispiel an...

can mir hiemanden bitte helfen?

Vielen dank.

John.

ps:mein deutsch ist nicht sehr gut weil ich französisch bin
Miniaturansicht angehängter Grafiken
popup_571.jpg  
Angehängte Dateien
Dateityp: zip itemprop-3.5-dfs-build_right_click_context_menu_110.zip (17,2 KB, 39x aufgerufen)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:32 Uhr.
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