如何获取“使用 Windows Defender 扫描...”shell 上下文菜单项的图标?

问题描述 投票:0回答:1

我正在尝试将从

HMENU
获得的
IContextMenu.QueryContextMenu()
转换为 WPF
ContextMenu
,这样我就可以拥有一个具有我在应用程序中定义的样式的上下文菜单,而不是普通的旧 Windows 菜单样式。

大部分功能都有效,除了两个项目“使用 Windows Defender 扫描...”和 Google Drive“同步或备份此文件夹”,它们不会在我从

MENUITEMINFO
获得的
GetMenuItemInfo()
结构中返回其图标。图标确实适用于其他项目。

我也尝试查看

MIIM_DATA
,但一无所获。

这是我的代码:

Public Function GetContextMenu(items As IEnumerable(Of Item)) As ContextMenu
    Dim pidls(items.Count - 1) As IntPtr
    Dim lastpidls(items.Count - 1) As IntPtr

    For i = 0 To items.Count - 1
        Functions.SHGetIDListFromObject(Marshal.GetIUnknownForObject(items(i).ShellItem2), pidls(i))
        lastpidls(i) = Functions.ILFindLastID(pidls(i))
    Next

    Dim ptr As IntPtr
    Me.ShellFolder.GetUIObjectOf(IntPtr.Zero, lastpidls.Length, lastpidls, GetType(IContextMenu).GUID, 0, ptr)
    Dim contextMenu As IContextMenu = Marshal.GetTypedObjectForIUnknown(ptr, GetType(IContextMenu))

    Marshal.QueryInterface(ptr, GetType(IContextMenu2).GUID, ptr)
    Dim contextMenu2 As IContextMenu2, contextMenu3 As IContextMenu3
    If Not ptr = IntPtr.Zero Then
        contextMenu2 = CType(Marshal.GetObjectForIUnknown(ptr), IContextMenu2)
    End If
    Marshal.QueryInterface(ptr, GetType(IContextMenu3).GUID, ptr)
    If Not ptr = IntPtr.Zero Then
        contextMenu3 = CType(Marshal.GetObjectForIUnknown(ptr), IContextMenu3)
    End If

    Dim hMenu As IntPtr = Functions.CreatePopupMenu()
    contextMenu.QueryContextMenu(hMenu, 0, 0, UInt32.MaxValue, CMF.CMF_NORMAL Or CMF.CMF_EXTENDEDVERBS)
    If _firstContextMenuCall Then
        ' somehow very first call doesn't return all items
        Functions.DestroyMenu(hMenu)
        hMenu = Functions.CreatePopupMenu()
        contextMenu.QueryContextMenu(hMenu, 0, 0, UInt32.MaxValue, CMF.CMF_NORMAL Or CMF.CMF_EXTENDEDVERBS)
    End If

    Dim getMenu As Func(Of IntPtr, List(Of Control)) =
        Function(hMenu2 As IntPtr) As List(Of Control)
            If Not contextMenu2 Is Nothing Then
                contextMenu2.HandleMenuMsg(WM.INITMENUPOPUP, hMenu2, IntPtr.Zero)
            End If
            If Not contextMenu3 Is Nothing Then
                Dim ptr2 As IntPtr
                contextMenu3.HandleMenuMsg2(WM.INITMENUPOPUP, hMenu2, IntPtr.Zero, ptr2)
            End If

            Dim result As List(Of Control) = New List(Of Control)()

            For i = 0 To Functions.GetMenuItemCount(hMenu2) - 1
                Dim mii As MENUITEMINFO
                mii.cbSize = CUInt(Marshal.SizeOf(mii))
                mii.fMask = MIIM.MIIM_STRING
                mii.dwTypeData = New String(" "c, 2048)
                mii.cch = mii.dwTypeData.Length
                Functions.GetMenuItemInfo(hMenu2, i, True, mii)
                Dim header As String = mii.dwTypeData.Substring(0, mii.cch)

                mii.fMask = MIIM.MIIM_BITMAP Or MIIM.MIIM_SUBMENU Or MIIM.MIIM_FTYPE
                Functions.GetMenuItemInfo(hMenu2, i, True, mii)

                Dim bitmapSource As BitmapSource
                If Not IntPtr.Zero.Equals(mii.hbmpItem) Then
                    bitmapSource = Interop.Imaging.CreateBitmapSourceFromHBitmap(mii.hbmpItem, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions())
                Else
                    bitmapSource = Nothing
                End If

                If mii.fType = MFT.SEPARATOR Then
                    result.Add(New Separator())
                Else
                    Dim menuItem As MenuItem = New MenuItem() With {
                        .Header = header.Replace("&", "_"),
                        .Icon = New Image() With {.Source = bitmapSource}
                    }

                    If mii.hSubMenu Then
                        Dim subMenu As List(Of Control) = getMenu(mii.hSubMenu)
                        For Each subMenuItem In subMenu
                            menuItem.Items.Add(subMenuItem)
                        Next
                    End If

                    result.Add(menuItem)
                End If
            Next

            Return result
        End Function

    Dim result2 As List(Of Control) = getMenu(hMenu)

    Dim menuResult As ContextMenu = New ContextMenu()
    For Each item In result2
        menuResult.Items.Add(item)
    Next

    Functions.DestroyMenu(hMenu)

    Return menuResult
End Function

编辑:它们不是所有者绘制的。我已将

TrackPopupMenuEx()
WndProc
连接起来,并检查了消息流,没有
WM_DRAWITEM
WM_MEASUREITEM
消息。

.net windows vb.net winapi com
1个回答
0
投票

我刚刚找到了如何获取这些项目的图标。

这是

MENUITEMINFO
结构:

<StructLayout(LayoutKind.Sequential)>
Public Structure MENUITEMINFO
    Public cbSize As UInteger
    Public fMask As UInteger
    Public fType As UInteger
    Public fState As MFS
    Public wID As Integer
    Public hSubMenu As IntPtr
    Public hbmpChecked As IntPtr
    Public hbmpUnchecked As IntPtr
    Public dwItemData As IntPtr
    Public dwTypeData As String
    Public cch As UInteger
    Public hbmpItem As IntPtr
End Structure

我刚刚发现它里面有 3 个图标:

hbmpItem
(我一开始检查的唯一一个),还有
hbmpChecked
hbmpUnchecked
。显然,一些上下文菜单处理程序使用
hbmpUnchecked
而不是
hbmpItem
。我在 7-zip 上下文菜单处理程序的源代码中找到了这个。

© www.soinside.com 2019 - 2024. All rights reserved.