Can I programmatically do drag-and-drop from my local file system to a 3rd party application?

Scott Jordan 21 Reputation points
2021-08-28T23:06:24.567+00:00

Hello!

I have a VB .NET WPF that searches a local directory structure to locate a few hundred files per month. These files need to be filed (drag and dropped) onto a 3rd party application that stores the files. The third party application has its own directory structure, and so the correct directory has to be picked before each file is dragged-and-dropped.

I am able to control the 3rd party program with SENDMESSAGEs. I can log in, select the correct directory, and read all the values from the 3rd party controls.

To manually do the upload, the user drags the file to the 3rd party control (which happens to be a ListView) and then the file gets copied to the 3rd party's file system.

Can I somehow simulate the drag-and-drop function after selecting the correct location in the 3rd party application? I tried WM_DROPFILES, but that didn't appear to do anything. However, I tried the same WM_DROPFILES into Notepad and it didn't do anything, either.

I found the Control.DoDragDrop method to initiate the drag-and-drop, but could not find a message to send to the 3rd party application to start the "drop" part of the process.

Thanks for any help!

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,423 questions
VB
VB
An object-oriented programming language developed by Microsoft that is implemented on the .NET Framework. Previously known as Visual Basic .NET.
2,575 questions
{count} votes

Accepted answer
  1. Castorix31 81,726 Reputation points
    2021-08-30T22:36:09.363+00:00

    From comments, a translation of C++ code which works with native DoDragDrop API (Control.DoDragDrop does not seem to work well)
    To be adapted/improved =>
    (I had to remove an 'e' from Sleep or I could not post ...)

    'Does not seem to work fine....
    'Dim filesList As System.Collections.Specialized.StringCollection = New System.Collections.Specialized.StringCollection()
    'filesList.Add("E:\test.txt")
    'filesList.Add("E:\hulk.jpg")
    'Dim dataObject As DataObject = New DataObject()
    'dataObject.SetData(DataFormats.FileDrop, filesList)
    'dataObject.SetFileDropList(filesList)
    
    OleInitialize(IntPtr.Zero)
    
    ' Test with 2 files, in a same directory for pidlParent
    Dim pItemIDL1 As IntPtr = ILCreateFromPath("E:\test.txt")
    Dim pItemIDL2 As IntPtr = ILCreateFromPath("E:\hulk.jpg")
    'Dim pItemIDL2 As IntPtr = ILCreateFromPath("E:\test2.txt")
    Dim arrayPidl = New IntPtr(255) {}
    Dim pidlParent As IntPtr = IntPtr.Zero, pidlItem As IntPtr = IntPtr.Zero
    
    Dim nPidlNumber As Integer = 0
    pidlItem = ILFindLastID(pItemIDL1)
    arrayPidl(0) = ILClone(pidlItem)
    nPidlNumber += 1
    ILRemoveLastID(pItemIDL1)
    pidlParent = ILClone(pItemIDL1)
    ILFree(pItemIDL1)
    
    pidlItem = ILFindLastID(pItemIDL2)
    arrayPidl(1) = ILClone(pidlItem)
    nPidlNumber += 1
    ILRemoveLastID(pItemIDL2)
    'pidlParent = ILClone(pItemIDL2)
    ILFree(pItemIDL2)
    
    Dim pDataObject As System.Runtime.InteropServices.ComTypes.IDataObject = Nothing
    Dim hr As HRESULT = SHCreateFileDataObject(pidlParent, nPidlNumber, arrayPidl, Nothing, pDataObject)
    If (hr = HRESULT.S_OK) Then
        ' Test with an IDropTarget app with window title = "Drop Target"
        Dim hWndDest As IntPtr = FindWindow(Nothing, "Drop Target")
        ' Notepad only works with 1 file
        'Dim hWndDest As IntPtr = FindWindow("Notepad", Nothing)
        If (hWndDest <> IntPtr.Zero) Then
            SwitchToThisWindow(hWndDest, True)
            System.Threading.Thread.Slep(500)
            Dim rcDest As New RECT()
            GetWindowRect(hWndDest, rcDest)
    
            ' Move mouse cursor in the middle of the window
            Dim nX As Integer = (rcDest.left + (rcDest.right - rcDest.left) / 2) * 65535 / GetSystemMetrics(SM_CXSCREEN)
            Dim nY As Integer = (rcDest.top + (rcDest.bottom - rcDest.top) / 2) * 65535 / GetSystemMetrics(SM_CYSCREEN)
    
            Dim mi() As INPUT = New INPUT(2) {}
            mi(0).type = INPUT_MOUSE
            mi(0).union.mi.dx = nX
            mi(0).union.mi.dy = nY
            mi(0).union.mi.dwFlags = MOUSEEVENTF_ABSOLUTE Or MOUSEEVENTF_MOVE
            'mi(1).union.mi.dwFlags = MOUSEEVENTF_LEFTDOWN
            mi(1).union.mi.dwFlags = MOUSEEVENTF_LEFTUP
            'mi(2).union.mi.dwFlags = MOUSEEVENTF_LEFTUP
            'SendInput(3, mi, Marshal.SizeOf(mi(0)))
            SendInput(2, mi, Marshal.SizeOf(mi(0)))
    
            Dim pDropSource As CDropSource = New CDropSource()
            Dim dwEffectDragDrop As UInteger = DragDropEffects.None
            hr = OleDoDragDrop(pDataObject, pDropSource, DragDropEffects.Link Or DragDropEffects.Copy, dwEffectDragDrop)
    
            Marshal.ReleaseComObject(pDataObject)
           'Dim dde As System.Windows.Forms.DragDropEffects = Me.DoDragDrop(dataObject, DragDropEffects.Copy Or DragDropEffects.Link)
          End If
    End If
    

    with declarations :

    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
        DRAGDROP_S_CANCEL = &H40101
        DRAGDROP_S_DROP = &H40100
        DRAGDROP_S_USEDEFAULTCURSORS = &H40102
    End Enum
    
    <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
    Public Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    End Function
    
    <DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
    Public Shared Function SwitchToThisWindow(hWnd As IntPtr, fAltTab As Boolean) As Boolean
    End Function
    
    <DllImport("User32.dll", SetLastError:=True)>
    Public Shared Function GetSystemMetrics(ByVal nIndex As Integer) As Integer
    End Function
    
    Public Const SM_CXSCREEN As Integer = 0
    Public Const SM_CYSCREEN As Integer = 1
    Public Const SM_XVIRTUALSCREEN As Integer = 76
    Public Const SM_YVIRTUALSCREEN As Integer = 77
    Public Const SM_CXVIRTUALSCREEN As Integer = 78
    Public Const SM_CYVIRTUALSCREEN As Integer = 79
    
    Public Const MOUSEEVENTF_MOVE As Integer = &H1
    Public Const MOUSEEVENTF_LEFTDOWN As Integer = &H2
    Public Const MOUSEEVENTF_LEFTUP As Integer = &H4
    Public Const MOUSEEVENTF_ABSOLUTE As Integer = &H8000
    
    <StructLayout(LayoutKind.Sequential)>
    Public Structure MOUSEINPUT
        Public dx As Integer
        Public dy As Integer
        Public mouseData As Integer
        Public dwFlags As Integer
        Public time As Integer
        Public dwExtraInfo As IntPtr
    End Structure
    
    <StructLayout(LayoutKind.Sequential)>
    Public Structure KEYBDINPUT
        Public wVk As Short
        Public wScan As Short
        Public dwFlags As Integer
        Public time As Integer
        Public dwExtraInfo As IntPtr
    End Structure
    
    <StructLayout(LayoutKind.Sequential)>
    Public Structure INPUT
        Public type As Integer
        Public union As INPUTUNION
    End Structure
    
    <StructLayout(LayoutKind.Explicit)>
    Public Structure INPUTUNION
        <FieldOffset(0)>
        Public mi As MOUSEINPUT
        <FieldOffset(0)>
        Public ki As KEYBDINPUT
        '<FieldOffset(0)>
        'Public hi As HARDWAREINPUT
    End Structure
    
    Public Const INPUT_MOUSE As Integer = 0
    Public Const INPUT_KEYBOARD As Integer = 1
    
    <DllImport("User32.dll", SetLastError:=True)>
    Public Shared Function SendInput(ByVal nInputs As Integer, <MarshalAs(UnmanagedType.LPArray)> ByVal pInputs() As INPUT, ByVal cbSize As Integer) As Integer
    End Function
    
    <StructLayout(LayoutKind.Sequential)>
    Public Structure RECT
        Public left As Integer
        Public top As Integer
        Public right As Integer
        Public bottom As Integer
        Public Sub New(
                  left As Integer,
                  top As Integer,
                  right As Integer,
                  bottom As Integer)
            Me.left = left
            Me.top = top
            Me.right = right
            Me.bottom = bottom
        End Sub
    End Structure
    
    <DllImport("user32", SetLastError:=True)>
    Public Shared Function GetWindowRect(hWnd As IntPtr, ByRef lpRect As RECT) As Boolean
    End Function
    
    <DllImport("Shell32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
    Public Shared Function ILCreateFromPath(pszPath As String) As IntPtr
    End Function
    
    <DllImport("Shell32.dll", SetLastError:=True, EntryPoint:="#740", CharSet:=CharSet.Unicode)>
    Public Shared Function SHCreateFileDataObject(ByVal pidlFolder As IntPtr, ByVal cidl As UInteger, ByVal apidl As IntPtr(), ByVal pdtInner As System.Runtime.InteropServices.ComTypes.IDataObject, <Out> ByRef ppdtobj As System.Runtime.InteropServices.ComTypes.IDataObject) As HRESULT
    End Function
    
    <DllImport("Shell32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
    Public Shared Function ILFindLastID(pidl As IntPtr) As IntPtr
    End Function
    
    <DllImport("Shell32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
    Public Shared Function ILClone(ByVal pidl As IntPtr) As IntPtr
    End Function
    
    <DllImport("Shell32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
    Public Shared Function ILRemoveLastID(ByVal pidl As IntPtr) As Boolean
    End Function
    
    <DllImport("Shell32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
    Public Shared Sub ILFree(pidl As IntPtr)
    End Sub
    
    <DllImport("Ole32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
    Public Shared Function OleInitialize(ByVal pvReserved As IntPtr) As HRESULT
    End Function
    
    <DllImport("Ole32.dll", EntryPoint:="DoDragDrop", SetLastError:=True, CharSet:=CharSet.Unicode, ExactSpelling:=True)>
    Public Shared Function OleDoDragDrop(<MarshalAs(UnmanagedType.Interface)> pDataObj As System.Runtime.InteropServices.ComTypes.IDataObject, <MarshalAs(UnmanagedType.Interface)> pDropSource As IDropSource, dwOKEffects As UInteger, ByRef pdwEffect As UInteger) As HRESULT
    End Function
    
    <ComImport, Guid("00000121-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    Public Interface IDropSource
        <PreserveSig>
        Function QueryContinueDrag(<MarshalAs(UnmanagedType.Bool)> fEscapePressed As Boolean, grfKeyState As UInteger) As HRESULT
        <PreserveSig>
        Function GiveFeedback(dwEffect As UInteger) As HRESULT
    End Interface
    
    Public Class CDropSource
        Implements IDropSource
        Private Function QueryContinueDrag(fEscapePressed As Boolean, grfKeyState As UInteger) As HRESULT Implements IDropSource.QueryContinueDrag
            If fEscapePressed Then
                Return HRESULT.DRAGDROP_S_CANCEL
            End If
            If Not (grfKeyState And MK_LBUTTON) Then
                Console.Beep(9000, 10) 'for test
                Return HRESULT.DRAGDROP_S_DROP
            End If
            Return HRESULT.S_OK
        End Function
    
        Public Function GiveFeedback(dwEffect As UInteger) As HRESULT Implements IDropSource.GiveFeedback
            Return HRESULT.DRAGDROP_S_USEDEFAULTCURSORS
        End Function
    End Class
    
    Public Const MK_LBUTTON = &H1
    Public Const MK_RBUTTON = &H2
    Public Const MK_SHIFT = &H4
    Public Const MK_CONTROL = &H8
    Public Const MK_MBUTTON = &H10
    Public Const MK_XBUTTON1 = &H20
    Public Const MK_XBUTTON2 = &H40
    
    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful