Windows Vista and above provide a shell interface to get a list of all handlers registered to open a particular file type that also returns where the icon is and what the friendly name is. Most importantly, it provider an interface to invoke that handler in a much better way than trying to make a command to launch it.
These things were just crying out to be made into an example of how to replicate the Open With menu in VB. There's even two groups: the recommended ones that show up on that menu in Explorer, or if so inclined you could list all the ones that appear on the actual Open With dialog.
Project Updated on April 25th, 2020 to Revision 3
This version massively simplifies the demo. It eliminates all pidl code just using a 2-line call to IShellItem instead, eliminates all icon code replacing it with a simple call to IShellItemImageFactory instead, and also provides a new button giving a simple text list of handlers instead of building a full menu.
Requirements
Windows Vista or newer oleexp v4.0 or higher (only required for the IDE, you do not need to distribute it with a compiled .exe)
Basic Outline
SHAssocEnumHandlers is called to get an object that can enumerate all the handlers for the extension passed- IEnumAssocHandlers.
That object lists IAssocHandler interfaces for each handler, and in the demo project we use the information provided by that to list them on a menu.
When one is chosen, the handlers are cycled through again to find the desired one to launch.
Main Code
(Note: Not copy-pastable, for overview of technique only. You need isiRes, which is an IShellItem of the selected file, and various functions/APIs declared in the included support module).
Code:
Dim sFile As String
Dim sExt As String
Dim MII() As MENUITEMINFO
Dim miiZ As MENUITEMINFO
Dim uRec() As AssocInfo
Dim i As Long, j As Long, k As Long
Dim ieah As IEnumAssocHandlers
Dim iah As IAssocHandler
Dim hr As Long
Dim lPtr As Long
Dim sApp As String
Dim sAppPath As String
Dim hBmp As Long
Dim PT As POINTAPI
Dim idCmd As Long
Dim hMenu As Long
Dim siif As IShellItemImageFactory
Dim si2 As IShellItem2
Const widBase As Long = 1000
Const sCP As String = "Choose program..."
j = -1
ReDim MII(0)
ReDim uRec(0)
isiRes.GetDisplayName SIGDN_DESKTOPABSOLUTEPARSING, lPtr
sFile = BStrFromLPWStr(lPtr)
Set si2 = isiRes
si2.GetString PKEY_FileExtension, lPtr
sExt = BStrFromLPWStr(lPtr)
'First, we use an API call to get the object that will list the handlers
'The other flag value will show all handlers- the recommended ones are the
'ones that show up in Explorer's right click open-with menu
hr = SHAssocEnumHandlers(StrPtr(sExt), ASSOC_FILTER_RECOMMENDED, ieah)
If hr <> S_OK Then Exit Sub
'now we're ready to start enumerating the handlers, in this project
'we're going to load them into a popup menu
hMenu = CreatePopupMenu()
'Most IEnum______ classes work exactly like this. .Next fills the IAssocHandler iface
Do While (ieah.Next(1, iah, 0) = 0)
If (iah Is Nothing) = False Then
j = j + 1
ReDim Preserve MII(j)
ReDim Preserve uRec(j) 'in case we need the info later
Call iah.GetUIName(lPtr)
sApp = BStrFromLPWStr(lPtr)
Call iah.GetName(lPtr)
sAppPath = BStrFromLPWStr(lPtr)
oleexp.SHCreateItemFromParsingName StrPtr(sAppPath), Nothing, IID_IShellItemImageFactory, siif
siif.GetImage 16&, 16&, SIIGBF_ICONONLY, hBmp
With MII(j)
.cbSize = Len(MII(j))
.fMask = MIIM_ID Or MIIM_STRING Or MIIM_BITMAP
.wID = widBase + j
.cch = Len(sApp)
.dwTypeData = sApp
.hbmpItem = hBmp
Call InsertMenuItem(hMenu, j, True, MII(j))
End With
Else
Debug.Print "iah=Nothing"
End If
Set iah = Nothing
Loop
'Add separator and open with other
miiZ.cbSize = Len(miiZ)
miiZ.fMask = MIIM_ID Or MIIM_TYPE
miiZ.fType = MFT_SEPARATOR
miiZ.wID = 9999
Call InsertMenuItem(hMenu, -1, False, miiZ)
miiZ.fMask = MIIM_ID Or MIIM_STRING
miiZ.wID = 3000
miiZ.cch = Len(sCP)
miiZ.dwTypeData = sCP
Call InsertMenuItem(hMenu, -1, False, miiZ)
Call GetCursorPos(PT)
PT.y = PT.y + 5
idCmd = TrackPopupMenu(hMenu, TPM_LEFTBUTTON Or TPM_RIGHTBUTTON Or TPM_LEFTALIGN Or TPM_TOPALIGN Or TPM_HORIZONTAL Or TPM_RETURNCMD, PT.x, PT.y, 0, Me.hWnd, 0)
Set ieah = Nothing
If idCmd Then
If idCmd = 3000 Then
OpenWith sFile, OAIF_ALLOW_REGISTRATION Or OAIF_EXEC, Me.hWnd
Else
k = idCmd - widBase
Dim ido As oleexp.IDataObject
Dim zc As Long
isiRes.BindToHandler 0&, BHID_DataObject, IID_IDataObject, ido
Dim invk As IAssocHandlerInvoker
hr = SHAssocEnumHandlers(StrPtr(sExt), ASSOC_FILTER_RECOMMENDED, ieah)
Do While (ieah.Next(1, iah, 0) = 0)
If (iah Is Nothing) = False Then
If zc = k Then
'theoretically, we could take the path to the executable and
'run a launch command, but the actual invoke interfacer is a
'far better choice
Call iah.CreateInvoker(ido, invk)
invk.Invoke
Exit Do
Else
zc = zc + 1
End If
End If
Set iah = Nothing
Loop
End If
End If
Set ido = Nothing
Set invk = Nothing
Set iah = Nothing
Set ieah = Nothing
End Sub
Included in ZIP
-All the core and supporting code required to generate a menu like that in the picture.
Future Goals
This is the very first release, and I do plan on trying to simplify things a bit as well as test out Unicode support. Please report any and all bugs so they can be fixed in the next version.
Last edited by fafalone; Apr 25th, 2020 at 03:32 AM.
Reason: Project updated
Re: [VB6] List/Execute File Handlers: IAssocHandler and IAssocHandlerInvoker (Vista+)
You know... I linked to this project lately, and was looking at the post, there's a lot of different methods I use now. You can eliminate all the manual pidl processing and IShellFolder stuff if you want to simplify... here's the idCmd block through the rest of the sub, just copy and paste over, no definitions are needed they're all in oleexp:
(code removed, new version is much simpler now)
Last edited by fafalone; Apr 25th, 2020 at 01:28 AM.
Re: [VB6] List/Execute File Handlers: IAssocHandler and IAssocHandlerInvoker (Vista+)
So I once again linked someone to this Demo, and in the years since I last posted, I've learned a lot of new shell programming methods that made it possible to *massively* simplify this project, even compared to the first revision, so...
Project Updated on April 25th, 2020 to Revision 3
This version massively simplifies the demo. It eliminates all pidl code just using a 2-line call to IShellItem instead, eliminates all icon code replacing it with a simple call to IShellItemImageFactory instead, and also provides a new button giving a simple text list of handlers instead of building a full menu.