Resolved Drag & Drop uwp app to winforms for AUIMD

Innww

Member
Joined
Aug 16, 2020
Messages
6
Programming Experience
10+
I am trying to "Drag & Drop" icons from the Start Menu in windows 10 to get it's Target; Naturally this works with .lnk but when dealing with a UWP . FileName, FileNameW and FileDrop is always "Nothing"

I can't figure this out. Is there a way to perhaps help me?
I have tried everything I could think of and I can't seem to get it to work.
I must have written and erase hundreds and hundreds of lines and started blank again (testing based on what little documentation I can find)

Getting the AUIMD from a app dragged on to the form or a control of the form is all I need.

dragging an app from start menu onto form and return the following (as you did)

VB.NET:
Microsoft.WindowsCalculator_8wekyb3d8bbwe!App

Would be perfect!

From what I was told I can use CFSTR_SHELLIDLIST which is a Clipboard Format. Meanwhile I have no idea how to do this.
Any help would be awesome!
 
According to another vb forum with exactly the same question and same username - this has been solved by the OP.
 
That was me and I did solve it. Marked it as Solved.
Thank you for searching google for an answer and then for not providing the answer and offer your 9th message to the post to tell everyone that I also asked this question elsewhere and then resolved it. That is MAJOR contribution.

For those who may one day need this EXACT source and because there is little to know .net documentation on it I will share it here.
If you need to be able to drop any app from start menu to form / control and get it's Name or Target or AUIMD. This will work. You will need to modify it a bit depending on your needs. But if you paste the source as it is in a new application it will work .

However a True thanks to Castorix from another forum for the help.

VB.NET:
Option Strict On

Imports System.Runtime.InteropServices

Imports System.Runtime.InteropServices.ComTypes



Public Class Form1



    '<ComImport>

    '<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>

    '<Guid("00000122-0000-0000-C000-000000000046")>

    'Interface IDropTarget

    '    Sub DragEnter(ByVal pDataObj As System.Runtime.InteropServices.ComTypes.IDataObject, ByVal grfKeyState As Integer, ByVal pt As Point, ByRef pdwEffect As Integer)

    '    Sub DragOver(ByVal grfKeyState As Integer, ByVal pt As Point, ByRef pdwEffect As Integer)

    '    Sub DragLeave()

    '    Sub Drop(ByVal pDataObj As System.Runtime.InteropServices.ComTypes.IDataObject, ByVal grfKeyState As Integer, ByVal pt As Point, ByRef pdwEffect As Integer)

    'End Interface



    <ComImport>

    <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>

    <Guid("00000122-0000-0000-C000-000000000046")>

    Interface IDropTarget

        Sub DragEnter(ByVal pDataObj As IDataObject, ByVal grfKeyState As Integer, ByVal pt As Point, ByRef pdwEffect As Integer)

        Sub DragOver(ByVal grfKeyState As Integer, ByVal pt As Point, ByRef pdwEffect As Integer)

        Sub DragLeave()

        Sub Drop(ByVal pDataObj As IDataObject, ByVal grfKeyState As Integer, ByVal pt As Point, ByRef pdwEffect As Integer)

    End Interface



    ' re-defined to use FORMATETC.cfFormat As UShort instead of Short (overflow otherwise)

    <ComImport, Guid("0000010e-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>

    Public Interface IDataObject

        Sub GetData(ByRef pformatetcIn As FORMATETC, ByRef pmedium As STGMEDIUM)

        Sub GetDataHere(ByRef formatetc As FORMATETC, ByRef pmedium As STGMEDIUM)

        Function QueryGetData(ByRef pformatetc As FORMATETC) As HRESULT

        Function GetCanonicalFormatEtc(ByRef pformatectIn As FORMATETC, ByRef pformatetcOut As FORMATETC) As HRESULT



        Sub SetData(ByRef pformatetc As FORMATETC, pmedium As STGMEDIUM, fRelease As Boolean)

        Function EnumFormatEtc(dwDirection As Integer, ByRef ppenumFormatEtc As IEnumFORMATETC) As HRESULT

        Function DAdvise(ByRef pformatetc As FORMATETC, advf As Integer, pAdvSink As IAdviseSink, ByRef pdwConnection As Integer) As HRESULT

        Sub DUnadvise(dwConnection As Integer)

        Function EnumDAdvise(ByRef ppenumAdvise As IEnumSTATDATA) As HRESULT

    End Interface



    Friend Class DropTarget

        Implements IDropTarget



        Public Sub DragEnter(pDataObj As IDataObject, grfKeyState As Integer, pt As Point, ByRef pdwEffect As Integer) Implements IDropTarget.DragEnter

            Console.Beep(1000, 100)

        End Sub



        Public Sub DragOver(grfKeyState As Integer, pt As Point, ByRef pdwEffect As Integer) Implements IDropTarget.DragOver

            'Console.Beep(5000, 10)

        End Sub



        Public Sub DragLeave() Implements IDropTarget.DragLeave

            Console.Beep(2000, 100)

        End Sub



        Public Sub Drop(pDataObj As IDataObject, grfKeyState As Integer, pt As Point, ByRef pdwEffect As Integer) Implements IDropTarget.Drop

            'Console.Beep(8000, 100)

            Dim cp_format_descriptor As UInteger = RegisterClipboardFormat(CFSTR_SHELLIDLIST)

            Dim descriptor_format As FORMATETC = New FORMATETC With {.cfFormat = CUShort(cp_format_descriptor), .dwAspect = DVASPECT.DVASPECT_CONTENT, .lindex = -1, .tymed = TYMED.TYMED_HGLOBAL}



            Dim hr As HRESULT = CType(pDataObj.QueryGetData(descriptor_format), HRESULT)

            If (hr = HRESULT.S_OK) Then

                Dim storage As STGMEDIUM = New STGMEDIUM()

                pDataObj.GetData(descriptor_format, storage)

                Dim pida As IntPtr = GlobalLock(storage.unionmember)

                Dim cida As CIDA = CType(Marshal.PtrToStructure(pida, GetType(CIDA)), CIDA)



                Dim nNbItems As UInteger = cida.cidl

                Dim nOffset As Integer = Marshal.SizeOf(GetType(UInteger))

                Dim pidlParent As IntPtr = CType((CInt(pida) + CType(Marshal.ReadInt32(pida, nOffset), UInteger)), IntPtr)

                For nIndice As UInteger = 1 To nNbItems

                    nOffset += Marshal.SizeOf(GetType(UInteger))

                    Dim pidlTarget As IntPtr = CType((CInt(pida) + CType(Marshal.ReadInt32(pida, nOffset), UInteger)), IntPtr)

                    Dim pidlAbs As IntPtr = ILCombine(pidlParent, pidlTarget)



                    'Dim sSBPath As System.Text.StringBuilder = New System.Text.StringBuilder(260)

                    'Dim bRet As Boolean = SHGetPathFromIDList(pidlAbs, sSBPath)

                    'Console.WriteLine("Path : {0}", sSBPath.ToString())



                    Dim psf As IShellFolder = Nothing

                    hr = SHGetDesktopFolder(psf)

                    Dim pidlChild As IntPtr

                    Dim IID_IShellFolder As Guid = New Guid("000214E6-0000-0000-C000-000000000046")

                    hr = SHBindToParent(pidlAbs, IID_IShellFolder, psf, pidlChild)

                    If (hr = HRESULT.S_OK) Then

                        Dim strRet As STRRET

                        Dim sDisplayName As String = Nothing

                        hr = psf.GetDisplayNameOf(pidlChild, SHGDNF.SHGDN_FORPARSING, strRet)

                        If (hr = HRESULT.S_OK) Then

                            Dim sbDisplayName As System.Text.StringBuilder

                            sbDisplayName = New System.Text.StringBuilder(256)

                            StrRetToBuf(strRet, pidlChild, sbDisplayName, CType(sbDisplayName.Capacity, UInteger))

                            sDisplayName = sbDisplayName.ToString()

                            Console.WriteLine("Name for parsing : {0}", sDisplayName)

                        End If

                        sDisplayName = Nothing

                        hr = psf.GetDisplayNameOf(pidlChild, SHGDNF.SHGDN_FORADDRESSBAR, strRet)

                        If (hr = HRESULT.S_OK) Then

                            Dim sbDisplayName As System.Text.StringBuilder

                            sbDisplayName = New System.Text.StringBuilder(256)

                            StrRetToBuf(strRet, pidlChild, sbDisplayName, CType(sbDisplayName.Capacity, UInteger))

                            sDisplayName = sbDisplayName.ToString()

                            Console.WriteLine("Name for address bar: {0}", sDisplayName)

                        End If

                    End If

                    ILFree(pidlAbs)

                Next

            End If

        End Sub

    End Class



    Public Enum HRESULT As Integer

        S_OK = 0

        S_FALSE = 1

        E_NOINTERFACE = &H80004002

        E_NOTIMPL = &H80004001

        E_FAIL = &H80004005

        E_UNEXPECTED = &H8000FFFF

        E_OUTOFMEMORY = &H8007000E

    End Enum



    <DllImport("Ole32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Public Shared Function RegisterDragDrop(hwnd As IntPtr, pDropTarget As IDropTarget) As HRESULT

    End Function



    <DllImport("Ole32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Public Shared Function OleInitialize(pvReserved As IntPtr) As HRESULT

    End Function



    <DllImport("Ole32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Public Shared Sub OleUninitialize()

    End Sub



    <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Public Shared Function RegisterClipboardFormat(lpszFormat As String) As UInteger

    End Function

    Public Const CFSTR_SHELLIDLIST As String = "Shell IDList Array" ' CF_IDLIST



    Public Enum tagDVASPECT

        DVASPECT_CONTENT = 1

        DVASPECT_THUMBNAIL = 2

        DVASPECT_ICON = 4

        DVASPECT_DOCPRINT = 8

    End Enum



    Public Enum tagTYMED

        TYMED_HGLOBAL = 1

        TYMED_FILE = 2

        TYMED_ISTREAM = 4

        TYMED_ISTORAGE = 8

        TYMED_GDI = 16

        TYMED_MFPICT = 32

        TYMED_ENHMF = 64

        TYMED_NULL = 0

    End Enum



    Public Structure FORMATETC

        Public cfFormat As UShort

        Public ptd As IntPtr

        Public dwAspect As DVASPECT

        Public lindex As Integer

        Public tymed As TYMED

    End Structure



    ' format of CF_IDLIST

    <StructLayout(LayoutKind.Sequential)>

    Public Structure CIDA

        Public cidl As UInteger

        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)>

        Public aoffset As UInteger()

    End Structure



    <DllImport("Kernel32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Public Shared Function GlobalLock(ByVal handle As IntPtr) As IntPtr

    End Function



    <DllImport("Kernel32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Public Shared Function GlobalUnlock(ByVal handle As IntPtr) As Boolean

    End Function



    <DllImport("Shell32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Public Shared Function ILCombine(pidl1 As IntPtr, pidl2 As IntPtr) As IntPtr

    End Function



    <DllImport("Shell32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Public Shared Sub ILFree(pidl As IntPtr)

    End Sub



    <DllImport("Shell32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Public Shared Function SHGetPathFromIDList(ByVal pidl As IntPtr, ByVal pszPath As System.Text.StringBuilder) As Boolean

    End Function



    <DllImport("Shell32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Friend Shared Function SHGetDesktopFolder(<Out(), MarshalAs(UnmanagedType.Interface)> ByRef ppshf As IShellFolder) As HRESULT

    End Function



    <DllImport("Shell32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Public Shared Function SHBindToParent(ByVal pidl As IntPtr, ByRef riid As Guid, ByRef ppv As IShellFolder, ByRef ppidlLast As IntPtr) As HRESULT

    End Function



    <DllImport("Shlwapi.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>

    Friend Shared Function StrRetToBuf(ByRef pstr As STRRET, ByVal pidl As IntPtr, ByVal pszBuf As System.Text.StringBuilder, <MarshalAs(UnmanagedType.U4)> ByVal cchBuf As UInteger) As HRESULT

    End Function



    <ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214E6-0000-0000-C000-000000000046")>

    Interface IShellFolder

        Function ParseDisplayName(ByVal hwnd As IntPtr, ByVal pbc As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> ByVal pszDisplayName As String, <[In], Out> ByRef pchEaten As UInteger, <Out> ByRef ppidl As IntPtr, <[In], Out> ByRef pdwAttributes As SFGAO) As HRESULT

        Function EnumObjects(ByVal hwnd As IntPtr, ByVal grfFlags As SHCONTF, <Out> ByRef ppenumIDList As IEnumIDList) As HRESULT

        Function BindToObject(ByVal pidl As IntPtr, ByVal pbc As IntPtr, <[In]> ByRef riid As Guid, <Out> <MarshalAs(UnmanagedType.[Interface])> ByRef ppv As Object) As HRESULT

        Function BindToStorage(ByVal pidl As IntPtr, ByVal pbc As IntPtr, <[In]> ByRef riid As Guid, <Out> <MarshalAs(UnmanagedType.[Interface])> ByRef ppv As Object) As HRESULT

        Function CompareIDs(ByVal lParam As IntPtr, ByVal pidl1 As IntPtr, ByVal pidl2 As IntPtr) As HRESULT

        Function CreateViewObject(ByVal hwndOwner As IntPtr, <[In]> ByRef riid As Guid, <Out> <MarshalAs(UnmanagedType.[Interface])> ByRef ppv As Object) As HRESULT

        Function GetAttributesOf(ByVal cidl As UInteger, ByVal apidl As IntPtr, <[In], Out> ByRef rgfInOut As SFGAO) As HRESULT

        Function GetUIObjectOf(ByVal hwndOwner As IntPtr, ByVal cidl As UInteger, ByRef apidl As IntPtr, <[In]> ByRef riid As Guid, <[In], Out> ByRef rgfReserved As UInteger, <Out> ByRef ppv As IntPtr) As HRESULT

        Function GetDisplayNameOf(ByVal pidl As IntPtr, ByVal uFlags As SHGDNF, <Out> ByRef pName As STRRET) As HRESULT

        Function SetNameOf(ByVal hwnd As IntPtr, ByVal pidl As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> ByVal pszName As String, ByVal uFlags As SHGDNF, <Out> ByRef ppidlOut As IntPtr) As HRESULT

    End Interface



    Public Enum SHCONTF

        SHCONTF_CHECKING_FOR_CHILDREN = &H10

        SHCONTF_FOLDERS = &H20

        SHCONTF_NONFOLDERS = &H40

        SHCONTF_INCLUDEHIDDEN = &H80

        SHCONTF_INIT_ON_FIRST_NEXT = &H100

        SHCONTF_NETPRINTERSRCH = &H200

        SHCONTF_SHAREABLE = &H400

        SHCONTF_STORAGE = &H800

        SHCONTF_NAVIGATION_ENUM = &H1000

        SHCONTF_FASTITEMS = &H2000

        SHCONTF_FLATLIST = &H4000

        SHCONTF_ENABLE_ASYNC = &H8000

    End Enum



    Public Enum SFGAO

        CANCOPY = &H1

        CANMOVE = &H2

        CANLINK = &H4

        STORAGE = &H8

        CANRENAME = &H10

        CANDELETE = &H20

        HASPROPSHEET = &H40

        DROPTARGET = &H100

        CAPABILITYMASK = &H177

        ENCRYPTED = &H2000

        ISSLOW = &H4000

        GHOSTED = &H8000

        LINK = &H10000

        SHARE = &H20000

        [READONLY] = &H40000

        HIDDEN = &H80000

        DISPLAYATTRMASK = &HFC000

        STREAM = &H400000

        STORAGEANCESTOR = &H800000

        VALIDATE = &H1000000

        REMOVABLE = &H2000000

        COMPRESSED = &H4000000

        BROWSABLE = &H8000000

        FILESYSANCESTOR = &H10000000

        FOLDER = &H20000000

        FILESYSTEM = &H40000000

        HASSUBFOLDER = &H80000000

        CONTENTSMASK = &H80000000

        STORAGECAPMASK = &H70C50008

        PKEYSFGAOMASK = &H81044000

    End Enum



    Public Enum SHGDNF

        SHGDN_NORMAL = 0

        SHGDN_INFOLDER = &H1

        SHGDN_FOREDITING = &H1000

        SHGDN_FORADDRESSBAR = &H4000

        SHGDN_FORPARSING = &H8000

    End Enum



    <StructLayout(LayoutKind.Explicit, Size:=264)>

    Public Structure STRRET

        <FieldOffset(0)>

        Public uType As UInteger

        <FieldOffset(4)>

        Public pOleStr As IntPtr

        <FieldOffset(4)>

        Public uOffset As UInteger

        <FieldOffset(4)>

        Public cString As IntPtr

    End Structure



    <ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F2-0000-0000-C000-000000000046")>

    Interface IEnumIDList

        <PreserveSig()>

        Function [Next](ByVal celt As UInteger, <Out> ByRef rgelt As IntPtr, <Out> ByRef pceltFetched As Integer) As HRESULT

        <PreserveSig()>

        Function Skip(ByVal celt As UInteger) As HRESULT

        Sub Reset()

        Function Clone() As IEnumIDList

    End Interface





    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        OleInitialize(IntPtr.Zero)

        Dim drop As New DropTarget()

        Dim hr As HRESULT = RegisterDragDrop(Me.Handle, drop)

    End Sub

End Class
 
Last edited:
That is MAJOR contribution.
It actually is a significant contribution because there are a lot of people who ask questions on multiple sites, get a solution at one of them and then abandon their other posts, ignoring the fact that other people may be spending their time trying to provide an unneeded solution on those other sites. Perhaps you weren't going to do that - I see that you have updated your question elsewhere - but no one else knew that. Maybe don't take offence at the drop of a hat. Read post #2 dispassionately. Is there even anything in there that is critical, never mind insulting? No, there is not. It's simply a statement of fact, probably intended to save anyone wasting time searching for or providing a solution that was not required.
 
I was a Global Moderator of one of the largest Forums for DEV/MODS there are . I am aware of your "concerns" on the matter; however I asked on 4 forums and had slowly worked my way through all of them with the solution to help others. You can "Assume" that is the case but it isn't necc factual. But now we are BOTH way off target and I would prefer this didn't turn into a "debate" or "Flame" thread. There are also 100's of ways (sometimes) of doing something. This method covers the entire form I am still hoping that another solution to the same problem can be offered and this communication can continue. This "concept" has little to no documentation and building a public storage for our knowledge is important for some developers (including myself) while the issue is/may be resolved for me (in a sense) I am still looking for a better way.

Secondly, You are being defensive. I never claimed you were being insulting at all. I simply used a "tone" that you must not have liked? In fact in my post I simply sarcastically said "Thank You" in a few words.

However I did label this as resolved because I was coming to post the source anyway and thats how I seen your post.

So Thank You.
Lets try and continue this conversation in a relevant helpful way :D
 
This uses Nuget package WindowsAPICodePack-Shell. It works fine for me when dragging icons from start menu, except if start menu is showing search results.
VB.NET:
Imports Microsoft.WindowsAPICodePack.Shell
Public Class Form1

    Private Sub Form1_DragEnter(sender As Object, e As DragEventArgs) Handles Me.DragEnter
        If e.Data.GetDataPresent("Shell IDList Array") Then
            e.Effect = e.AllowedEffect
        End If
    End Sub

    Private Sub Form1_DragDrop(sender As Object, e As DragEventArgs) Handles Me.DragDrop
        Dim coll = ShellObjectCollection.FromDataObject(e.Data)
        For Each item As ShellObject In coll
            Debug.WriteLine(item.Name) 'localized app name
            Debug.WriteLine(item.ParsingName) 'app id
            Debug.WriteLine(item.Properties.GetProperty(Of String)("System.AppUserModel.PackageInstallPath").Value) 'install path
        Next
    End Sub
End Class
 
Works Perfectly for what I needed it for. Had I know about this a long time ago would have saved a lot of time. So again! Thank you!

This uses Nuget package WindowsAPICodePack-Shell. It works fine for me when dragging icons from start menu, except if start menu is showing search results.

If this works (the way it looks like it will) It is deft better then the one I am currently using because it doesn't encapsulate the entire form and I can choose the Drag/Drop events. So THANK YOU!
 
Last edited:
Back
Top