Shell 上下文菜单与 windows 资源管理器上下文菜单不同

Different Shell Context Menu versus windows explorer context menu

您好,当我从 shell 上下文菜单中获取所有项目时,我在我的 .NET 程序中实现了 IShellFolder com 接口。但是我有一个问题,我的跟踪 shell 上下文菜单与资源管理器 shell 上下文菜单有一些不同的项目。在下面的图片中,您已经看到在我的程序中我没有在程序子菜单中查看打开。我只有一项“在程序中打开”。在我的上下文菜单中缺少在 Notepad++ 中打开,我还有一些其他项目,如 7-zip 项目和子菜单以及来自 7-zip 程序的 CRC SHA。

第一张图片是来自 windows 资源管理器的 shell 上下文菜单,第二张图片来自我的 shell 上下文菜单代码。

你能告诉我哪里出错了吗?非常感谢。

这是我的代码:

private ContextMenu CreateFileContextMenu(FileInfo[] files, Point location)
{
    Win32APICaller.CoInitializeEx(IntPtr.Zero, COINIT.MULTITHREADED);
    IShellFolder parentFolder = GetParentFolder(files[0].DirectoryName);
    IntPtr[] pidls = this.GetPIDLs(parentFolder, files);

    IntPtr pMenu = IntPtr.Zero;
    IntPtr iContextMenuPtr = IntPtr.Zero;
    IntPtr iContextMenuPtr2 = IntPtr.Zero;
    IntPtr iContextMenuPtr3 = IntPtr.Zero;

    if (pidls != null)
    {
        IContextMenu contextMenu;
        if (this.GetContextMenuInterfaces(parentFolder, pidls, out contextMenu, out iContextMenuPtr))
        {
            pMenu = Win32APICaller.CreatePopupMenu();

            Marshal.QueryInterface(iContextMenuPtr, ref IID_IContextMenu2, out iContextMenuPtr2);
            Marshal.QueryInterface(iContextMenuPtr, ref IID_IContextMenu3, out iContextMenuPtr3);

            contextMenu2 = (IContextMenu2)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr2, typeof(IContextMenu2));
            contextMenu3 = (IContextMenu3)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr3, typeof(IContextMenu3));

            int nResult = contextMenu.QueryContextMenu(pMenu, 0, 1, 30000, CMF.EXPLORE | CMF.CANRENAME | CMF.NORMAL | CMF.INCLUDESTATIC | CMF.EXTENDEDVERBS);
            int count = Win32APICaller.GetMenuItemCount(pMenu);

            //contextMenu3.QueryContextMenu(pMenu, 0, 1, 30000, /*CMF.EXPLORE | CMF.NORMAL |*/ CMF.EXTENDEDVERBS);

            count = Win32APICaller.GetMenuItemCount(pMenu);
            Win32APICaller.SendMessage(this.Handle, WM_INITMENUPOPUP, pMenu, 0);
            uint nSelected = Win32APICaller.TrackPopupMenuEx(pMenu, 0x0100, location.X, location.Y, this.Handle, IntPtr.Zero);
       }
   }
}

private IntPtr[] GetPIDLs(IShellFolder parentFolder, FileInfo[] files)
{
    if (parentFolder != null)
    {
        IntPtr[] pidls = new IntPtr[files.Length];
        for (int index = 0; index < files.Length; index++)
        {
            FileInfo fileInfo = files[index];

            uint pchEaten = 0;
            SFGAO pdwAttributes = 0;
            IntPtr pPIDL = IntPtr.Zero;
            int nResult = parentFolder.ParseDisplayName(this.Handle, IntPtr.Zero, fileInfo.Name, ref pchEaten, out pPIDL, ref pdwAttributes);
            if (nResult == 0)
            {
                pidls[index] = pPIDL;
            }
        }

        return pidls;
    }

    return null;
}

private IShellFolder GetParentFolder(string folderName)
{
    IShellFolder desktopFolder = this.GetDektopFolder();
    if (desktopFolder != null)
    {
        IntPtr pPIDL = IntPtr.Zero;
        uint pchEaten = 0;
        SFGAO pdwAttributes = 0;
        int nResult = desktopFolder.ParseDisplayName(this.Handle, IntPtr.Zero, folderName, ref pchEaten, out pPIDL, ref pdwAttributes);
        if (nResult == 0)
        {
            IntPtr pStrRet = Marshal.AllocCoTaskMem(260 * 2 + 4);
            Marshal.WriteInt32(pStrRet, 0, 0);
            nResult = desktopFolder.GetDisplayNameOf(pPIDL, SHGNO.FORPARSING, pStrRet);
            StringBuilder strFolder = new StringBuilder(260);
            Win32APICaller.StrRetToBuf(pStrRet, pPIDL, strFolder, 260);
            Marshal.FreeCoTaskMem(pStrRet);
            pStrRet = IntPtr.Zero;

            IntPtr pUnknownParentFolder = IntPtr.Zero;
            nResult = desktopFolder.BindToObject(pPIDL, IntPtr.Zero, ref IID_IShellFolder, out pUnknownParentFolder);
            Marshal.FreeCoTaskMem(pPIDL);
            if (nResult == 0)
            {
                return (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnknownParentFolder, typeof(IShellFolder));
            }
        }
    }

    return null;
}

private IShellFolder GetDektopFolder()
{
    IntPtr pUnknownDesktopFolder = IntPtr.Zero;

    int nResult = Win32APICaller.SHGetDesktopFolder(out pUnknownDesktopFolder);
    if (nResult == 0)
        return (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnknownDesktopFolder, typeof(IShellFolder));
    else
        return null;
}

private bool GetContextMenuInterfaces(IShellFolder parentFolder, IntPtr[] pidls, out IContextMenu contextMenu, out IntPtr contextMenuPtr)
{
    int nResult = parentFolder.GetUIObjectOf(this.Handle, (uint)pidls.Length, pidls, IID_IContextMenu, IntPtr.Zero, out contextMenuPtr);
    contextMenu = null;

    if (nResult == 0)
    {
        contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(contextMenuPtr, typeof(IContextMenu));
        return true;
    }

    return false;
}

protected override void WndProc(ref Message m)
{
    if (this.contextMenu3 != null)
    {
        this.contextMenu3.HandleMenuMsg2((uint)m.Msg, m.WParam, m.LParam, m.Result);
    }
    else if (this.contextMenu2 != null)
    {
        this.contextMenu2.HandleMenuMsg((uint)m.Msg, m.WParam, m.LParam);
    }

    base.WndProc(ref m);
}

In the pictures below you have seen that in my program I do not view Open in program sub menu. I have only one item "Open in program".

原因是这些子菜单是delay-generated(这解释了为什么当你展开它们时它们不包含任何有趣的东西)和owner-drawn.

所以你需要handle messages associated with owner-drawn menu items.

之后你会得到你所期望的:

以下是Win32 C++示例代码,您可以参考:

#define SCRATCH_QCM_FIRST 1
#define SCRATCH_QCM_LAST  0x7FFF

IContextMenu2* g_pcm2;
IContextMenu3* g_pcm3;

//...

void OnContextMenu(HWND hwnd, int xPos, int yPos)
{
    WCHAR pszFilePath[] = L"C:\Users\me\Desktop\test1.txt";
    IShellFolder* psfDesktop = NULL;
    ITEMIDLIST* id = 0;
    LPCITEMIDLIST idChild = 0;
    IContextMenu* pcm = NULL;
    int iCmdTemp = 0;

    POINT pt = { xPos, yPos };
    if (pt.x == -1 && pt.y == -1) {
        pt.x = pt.y = 0;
        ClientToScreen(hwnd, &pt);
    }

    SHParseDisplayName(pszFilePath, 0, &id, 0, 0);
    SHBindToParent(id, IID_IShellFolder, (void**)& psfDesktop, &idChild);

    psfDesktop->GetUIObjectOf(hwnd, 1, (const ITEMIDLIST **)&idChild, __uuidof(IContextMenu), NULL, (void **)&pcm);

    if (pcm) {
        HMENU hmenu = CreatePopupMenu();
        if (hmenu) {
            if (SUCCEEDED(pcm->QueryContextMenu(hmenu, 0,
                SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST,
                CMF_NORMAL))) {

                pcm->QueryInterface(IID_IContextMenu2, (void**)& g_pcm2);
                pcm->QueryInterface(IID_IContextMenu3, (void**)& g_pcm3);

                int iCmd = TrackPopupMenuEx(hmenu, TPM_RETURNCMD,
                    pt.x, pt.y, hwnd, NULL);
                if (g_pcm2) {
                    g_pcm2->Release();
                    g_pcm2 = NULL;
                }
                if (g_pcm3) {
                    g_pcm3->Release();
                    g_pcm3 = NULL;
                }
                if (iCmd > 0) {
                    CMINVOKECOMMANDINFOEX info = { 0 };
                    info.cbSize = sizeof(info);
                    info.fMask = 0x00004000;
                    info.hwnd = hwnd;
                    iCmdTemp = iCmd - SCRATCH_QCM_FIRST;
                    info.lpVerb = MAKEINTRESOURCEA(iCmdTemp);
                    info.lpVerbW = MAKEINTRESOURCEW(iCmdTemp);
                    info.nShow = SW_SHOWNORMAL;
                    pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)& info);
                }

            }
            DestroyMenu(hmenu);
        }
        pcm->Release();
    }
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int xPos;
    int yPos;

    if (g_pcm3) {
        LRESULT lres;
        if (SUCCEEDED(g_pcm3->HandleMenuMsg2(message, wParam, lParam, &lres))) {
            return lres;
        }
    }
    else if (g_pcm2) {
        if (SUCCEEDED(g_pcm2->HandleMenuMsg(message, wParam, lParam))) {
            return 0;
        }
    }


    switch (message)
    {
    case WM_CONTEXTMENU:
        xPos = GET_X_LPARAM(lParam);
        yPos = GET_Y_LPARAM(lParam);
        OnContextMenu(hWnd, xPos, yPos);
        break;
//...

我找到了无法在上下文菜单中查看“使用 Notepad++ 编辑”项目的原因。我需要将我的代码编译为 x64 架构。当我完成此操作时,上下文菜单会查看使用 Notepad++ 编辑和缺少的 7-zip 项目。

当我在互联网上找到时我发现这取决于程序架构并且我已经在上下文菜单中将Notepad++注册为仅限x64架构.