none
Drag'n'drop to explorer fails for large files

    Question

  • Hello.

    I have very strange problem with D'n'D. In two words, when I drag item from my application (.NET 3.5 Visual Studio 2012) to explorer it fails with message 'Not enough storage is available to complete operation' if file size is more then approximately 21 MB.

    D'n'd is implemented using own class that is inherited from DataObject.

    Imports System.Runtime.InteropServices
    Imports System.Runtime.InteropServices.ComTypes
    Imports System.IO
    Imports System.Security.Permissions
    Imports System.Windows.Forms
    
    
    
    ''' <summary>
    ''' DataObject capable of create files before drop to windows explorer
    ''' </summary>
    ''' <remarks></remarks>
    Public Class ShellDataObjectBase
        Inherits DataObject
        Implements System.Runtime.InteropServices.ComTypes.IDataObject
    
    
        Public FileContentMemoryStream As MemoryStream = Nothing
        Public FileDescriptorMemoryStream As MemoryStream
    
        Public FileName As String
    #Region "Types of media"
        ''' <summary>
        ''' Allowed types of media
        ''' </summary>
        ''' <remarks></remarks>
        Private Shared ReadOnly ALLOWED_TYMEDS As TYMED() = New TYMED() {TYMED.TYMED_ENHMF, TYMED.TYMED_GDI, TYMED.TYMED_FILE, TYMED.TYMED_HGLOBAL, TYMED.TYMED_ISTREAM, TYMED.TYMED_MFPICT}
    
        ''' <summary>
        ''' Is type of media allowed?
        ''' </summary>
        ''' <param name="tymed__1"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Shared Function GetTymedUseable(ByVal tymed__1 As TYMED) As [Boolean]
            For i As Int32 = 0 To ALLOWED_TYMEDS.Length - 1
                If (tymed__1 And ALLOWED_TYMEDS(i)) <> TYMED.TYMED_NULL Then
                    Return True
                End If
            Next
            Return False
        End Function
    #End Region
    
    #Region "Structures"
        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
        Private Structure FILEDESCRIPTOR
            Public dwFlags As UInt32
            Public clsid As Guid
            Public sizel As System.Drawing.Size
            Public pointl As System.Drawing.Point
            Public dwFileAttributes As UInt32
            Public ftCreationTime As System.Runtime.InteropServices.ComTypes.FILETIME
            Public ftLastAccessTime As System.Runtime.InteropServices.ComTypes.FILETIME
            Public ftLastWriteTime As System.Runtime.InteropServices.ComTypes.FILETIME
            Public nFileSizeHigh As UInt32
            Public nFileSizeLow As UInt32
            <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
            Public cFileName As [String]
        End Structure
    
        'Public Structure SelectedItem
        '    Public FileName As [String]
        '    Public WriteTime As DateTime
        '    Public FileSize As Int64
        'End Structure
    #End Region
    
    #Region "Properties"
    
    
    
    
    #End Region
    
    #Region "Constructors"
    
        Public Sub New()
    
            Me.SetData(NativeMethods.CFSTR_FILEDESCRIPTORW, Nothing)
            Me.SetData(NativeMethods.CFSTR_FILECONTENTS, Nothing)
            Me.SetData(NativeMethods.CFSTR_PERFORMEDDROPEFFECT, Nothing)
    
        End Sub
    
    #End Region
    
    
    
    #Region "File methods"
    
        Private _Lindex As Int32
    
        Private Function GetFileDescriptor() As MemoryStream
         
           
            FileDescriptorMemoryStream = New MemoryStream()
            ' Write out the FILEGROUPDESCRIPTOR.cItems value
            FileDescriptorMemoryStream.Write(BitConverter.GetBytes(1), 0, 4)
            Dim FileDescriptor As New FILEDESCRIPTOR()
            'Dim FileName As String = "image_with_barcodes.tif"
    
            FileDescriptor.cFileName = FileName
            Dim FileWriteTimeUtc As Int64 = 0
    
            Dim Cancel As Boolean = False
    
            Dim Key As String = String.Empty
    
        
            Dim FileSize As Long = New IO.FileInfo(FileName).Length
    
            FileDescriptor.nFileSizeHigh = CUInt(FileSize >> 32)
            FileDescriptor.nFileSizeLow = CUInt(FileSize And &HFFFFFFFFUI)
    
    
            FileDescriptor.ftLastWriteTime.dwHighDateTime = CInt(FileWriteTimeUtc >> 32)
            FileDescriptor.ftLastWriteTime.dwLowDateTime = CInt(FileWriteTimeUtc And &HFFFFFFFFUI)
    
            FileDescriptor.dwFlags = NativeMethods.FD_WRITESTIME Or NativeMethods.FD_FILESIZE Or NativeMethods.FD_PROGRESSUI
            ' Marshal the FileDescriptor structure into a 
            ' byte array and write it to the MemoryStream.
            Dim FileDescriptorSize As Int32 = Marshal.SizeOf(FileDescriptor)
            Dim FileDescriptorPointer As IntPtr = Marshal.AllocHGlobal(FileDescriptorSize)
            Marshal.StructureToPtr(FileDescriptor, FileDescriptorPointer, True)
            Dim FileDescriptorByteArray As [Byte]() = New [Byte](FileDescriptorSize - 1) {}
            Marshal.Copy(FileDescriptorPointer, FileDescriptorByteArray, 0, FileDescriptorSize)
            Marshal.FreeHGlobal(FileDescriptorPointer)
            FileDescriptorMemoryStream.Write(FileDescriptorByteArray, 0, FileDescriptorByteArray.Length)
    
            Return FileDescriptorMemoryStream
        End Function
    
        Private Function GetFileContents() As MemoryStream
          
    
            If _Lindex < 1 Then
    
    
                If Not File.Exists(FileName) Then Return Nothing
    
                Dim bBuffer As [Byte]() = File.ReadAllBytes(FileName)
                FileContentMemoryStream = New MemoryStream(bBuffer.Length)
                'Must send at least one byte for a zero length file to prevent stoppages.
                If bBuffer.Length = 0 Then
                    bBuffer = New [Byte](0) {}
                End If
                FileContentMemoryStream.Write(bBuffer, 0, bBuffer.Length)
    
    
               
            End If
            Return FileContentMemoryStream
        End Function
    #End Region
    
    #Region "DataObject methods"
      
    
        Public Overloads Overrides Function GetData(ByVal format As String, ByVal autoConvert As Boolean) As Object
            Console.WriteLine(format)
            If [String].Compare(format, NativeMethods.CFSTR_FILEDESCRIPTORW, StringComparison.OrdinalIgnoreCase) = 0 Then
                MyBase.SetData(NativeMethods.CFSTR_FILEDESCRIPTORW, GetFileDescriptor())
            ElseIf [String].Compare(format, NativeMethods.CFSTR_FILECONTENTS, StringComparison.OrdinalIgnoreCase) = 0 Then
    
                Dim Stream As MemoryStream = GetFileContents()
                If Not Stream Is Nothing Then MyBase.SetData(NativeMethods.CFSTR_FILECONTENTS, Stream)
                'TODO: Cleanup routines after paste has been performed
            ElseIf [String].Compare(format, NativeMethods.CFSTR_PERFORMEDDROPEFFECT, StringComparison.OrdinalIgnoreCase) = 0 Then
            End If
    
            Return MyBase.GetData(format, autoConvert)
        End Function
    
    #End Region
    End Class
    

    And I have small form with the next code:

    Public Class Form1
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
    
            ListBox1.Items.Add("Small image. Works fine")
            ListBox1.Items.Add("Large image. error occures")
    
        End Sub
    
    
    
        Private Sub ListBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles ListBox1.MouseDown
    
            Dim DataObject As ShellDataObjectBase = New ShellDataObjectBase()
            If ListBox1.SelectedIndex = 0 Then
                DataObject.FileName = "Small.bmp"
            End If
            If ListBox1.SelectedIndex = 1 Then
                DataObject.FileName = "Large.bmp"
            End If
    
            ListBox1.DoDragDrop(DataObject, DragDropEffects.All)
        End Sub
    
    End Class
    

    It works fine for 'Small.bmp' (wich is around 250kB) but fails for 'Large.bmp' which is 35 MB.

    I don't see any exception in Visual Studio, so I guess that error is raised somewhere in kernel libraries. Maybe there is some problem with .NET memory management/memory allocation, but I don't know how to deal with this error. Any ideas what's going wrong here?

    P.S. Can't add link to my test project: get message that my account is not verified. Do I need to perform any action to verify my account? Thanks.


    Friday, April 7, 2017 11:38 AM

All replies

  • I see that you don't want to handle this as question but as discussion. 

    Therefore, what you want to discuss?

    If it is a question than change it back to that in the top of your original question.


    Success
    Cor

    Friday, April 7, 2017 12:19 PM
  • I'm not sure, but here's where I would first look:

    Is the unmanaged code involved?  Comment out everything that uses unmanaged code and repeat the test so that only the file content is present (ignore the file descriptor for now).  Does the error still occur?

    If so, I would factor out the memory streams in the data object.  All that is necessary are the actual bytes.  Copy the stream contents into an array, place the byte array on the data object, and close the memory stream.  Does the error still occur?

    Those would be my first two steps, personally.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Friday, April 7, 2017 1:00 PM
    Moderator
  • Let Explorer do the work for you.  Get an IShellItem interface for your file.  Then obtain an IDataObject interface by calling IShellItem::BindToHandler with BHID_DataObject.  Of course you would use the managed code equivalents.


    • Edited by RLWA32 Friday, April 7, 2017 1:37 PM
    Friday, April 7, 2017 1:35 PM
  • I want to discuss what's going wrong with my code or what is the source of error. In my opinion it's a little bit more then just questions. But maybe I didn't get basic idea of 'questions' and 'discussions'.
    Friday, April 7, 2017 1:36 PM
  • A discussion can be between regulars in this forum about how a problem the best can be solved. 

    However, then there is in fact seldom code involved. 

    A question is if something does not do as what persons expect and wants help with that. 

    But I see a lot of code, but not something like. I use A but think I can also use B, what do you think?

    It seems to me simply a question to solve a running code problem.


    Success
    Cor


    Friday, April 7, 2017 1:43 PM
  • Thank you for ideas.  I played with memory streams and array, moved File routine into main form, but it's still the same.
    Friday, April 7, 2017 1:43 PM
  • Thank you for ideas.  I played with memory streams and array, moved File routine into main form, but it's still the same.
    And what you think it  can be?

    Success
    Cor

    Friday, April 7, 2017 1:45 PM
  • Thanks for clarification. OK, I changed thread type.
    Friday, April 7, 2017 1:47 PM
  • Let Explorer do the work for you.  Get an  IShellItem interface for your file.  Then obtain an IDataObject interface....

    Sounds interesting, but I can't get how to use it in my application. Could you please provide small piece of code?

    Friday, April 7, 2017 2:12 PM
  • I want to discuss what's going wrong with my code or what is the source of error. In my opinion it's a little bit more then just questions. But maybe I didn't get basic idea of 'questions' and 'discussions'.

    As an aside, for future reference:

    The thread should be a question when it could have a definitive answer.  "What's wrong with my code?" is an example of a question which likely has one or more definitive answers.

    The thread should be a discussion when it could not have a single definitive answer, or otherwise does not pose a question of any kind.  "How could I achieve some goal" or "How to do a thing" are examples of threads which would likely be open-ended discussions.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Friday, April 7, 2017 2:20 PM
    Moderator
  • I'll preface this by saying I'm not a VB guy so the code may not be perfect, but the following had no problem dragging and dropping a 52 MB file

    The form had a listbox that contained 1 file name

    Imports System.Runtime.InteropServices
    Imports System.Runtime.InteropServices.ComTypes
    Public Class Form1
        Dim IShellItemGUID As New Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")
        Dim IDataObjectGUID As New Guid("0000010e-0000-0000-C000-000000000046")
        Dim BHID_DataObject As New Guid("B8C0BD9F-ED24-455c-83E6-D5390C4FE8C4")
        Public Enum SIGDN As UInteger
            NORMALDISPLAY = 0
            PARENTRELATIVEPARSING = &H80018001UI
            PARENTRELATIVEFORADDRESSBAR = &H8001C001UI
            DESKTOPABSOLUTEPARSING = &H80028000UI
            PARENTRELATIVEEDITING = &H80031001UI
            DESKTOPABSOLUTEEDITING = &H8004C000UI
            FILESYSPATH = &H80058000UI
            URL = &H80068000UI
        End Enum
        <ComImport()>
        <Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")>
        <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
        Public Interface IShellItem
            <PreserveSig> Function BindToHandler(ByVal pbc As IntPtr,
            <MarshalAs(UnmanagedType.LPStruct)> ByVal bhid As Guid,
            <MarshalAs(UnmanagedType.LPStruct)> ByVal riid As Guid,
            <Out(), MarshalAs(UnmanagedType.Interface, IidParameterIndex:=2)> ByRef ppv As IDataObject) As <MarshalAs(UnmanagedType.Error)> Integer
            <PreserveSig> Function GetParent(ByRef ppsi As IShellItem) As <MarshalAs(UnmanagedType.Error)> Integer
            <PreserveSig> Function GetDisplayName(ByVal sigdnName As SIGDN, ByRef ppszName As IntPtr) As <MarshalAs(UnmanagedType.Error)> Integer
            <PreserveSig> Function GetAttributes(ByVal sfgaoMask As UInt32, ByRef psfgaoAttribs As UInt32) As <MarshalAs(UnmanagedType.Error)> Integer
            <PreserveSig> Function Compare(ByVal psi As IShellItem, ByVal hint As UInt32, ByRef piOrder As Integer) As <MarshalAs(UnmanagedType.Error)> Integer
        End Interface
        <DllImport("shell32.dll", CharSet:=CharSet.Unicode, PreserveSig:=False)>
        Public Shared Sub SHCreateItemFromParsingName(
             <MarshalAs(UnmanagedType.LPWStr)> ByVal pszPath As String,
             ByVal pbc As IntPtr,
             <MarshalAs(UnmanagedType.LPStruct)> ByVal riid As Guid,
             <MarshalAs(UnmanagedType.Interface, IidParameterIndex:=2)> ByRef ppv As IShellItem)
        End Sub
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            ListBox1.Items.Add("C:\Users\RLWA32\Downloads\windows6.1-kb3197867-x86.msu")
        End Sub
    
        Private Sub ListBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles ListBox1.MouseDown
            Dim fileName As String
            Dim pbc As IntPtr
            Dim hRes As Integer
            Dim shellItem As IShellItem
            Dim dataObj As IDataObject
    
            fileName = ListBox1.Items(0)
    
            pbc = Nothing
            SHCreateItemFromParsingName(fileName, pbc, IShellItemGUID, shellItem)
    
            hRes = shellItem.BindToHandler(pbc, BHID_DataObject, IDataObjectGUID, dataObj)
    
            ListBox1.DoDragDrop(dataObj, DragDropEffects.All)
        End Sub
    End Class


    • Edited by RLWA32 Friday, April 7, 2017 4:20 PM
    Friday, April 7, 2017 4:19 PM
  • New version of VB code.  I changed it to use Exceptions instead of returning HRESULTS and also improved the way that interfaces were returned from the SHCreateItemFromParsingName and IShellItem::BindToHandler.  BTW, in this version the sample file was 285 MB. :)

    Imports System.Runtime.InteropServices
    Imports System.Runtime.InteropServices.ComTypes
    Public Class Form1
        Dim IShellItemGUID As New Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")
        Dim IDataObjectGUID As New Guid("0000010e-0000-0000-C000-000000000046")
        Dim BHID_DataObject As New Guid("B8C0BD9F-ED24-455c-83E6-D5390C4FE8C4")
        Public Enum SIGDN As UInteger
            NORMALDISPLAY = 0
            PARENTRELATIVEPARSING = &H80018001UI
            PARENTRELATIVEFORADDRESSBAR = &H8001C001UI
            DESKTOPABSOLUTEPARSING = &H80028000UI
            PARENTRELATIVEEDITING = &H80031001UI
            DESKTOPABSOLUTEEDITING = &H8004C000UI
            FILESYSPATH = &H80058000UI
            URL = &H80068000UI
        End Enum
        <ComImport()>
        <Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")>
        <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
        Public Interface IShellItem
            Function BindToHandler(ByVal pbc As IntPtr,
            <MarshalAs(UnmanagedType.LPStruct)> ByVal bhid As Guid,
            <MarshalAs(UnmanagedType.LPStruct)> ByVal riid As Guid,
            <Out(), MarshalAs(UnmanagedType.Interface, IidParameterIndex:=2)> ByRef ppv As Object)
            Function GetParent(ByRef ppsi As IShellItem) As <MarshalAs(UnmanagedType.Error)> Integer
            Function GetDisplayName(ByVal sigdnName As SIGDN, ByRef ppszName As IntPtr)
            Function GetAttributes(ByVal sfgaoMask As UInt32, ByRef psfgaoAttribs As UInt32)
            Function Compare(ByVal psi As IShellItem, ByVal hint As UInt32, ByRef piOrder As Integer)
        End Interface
        <DllImport("shell32.dll", CharSet:=CharSet.Unicode, PreserveSig:=False)>
        Public Shared Sub SHCreateItemFromParsingName(
             <MarshalAs(UnmanagedType.LPWStr)> ByVal pszPath As String,
             ByVal pbc As IntPtr,
             <MarshalAs(UnmanagedType.LPStruct)> ByVal riid As Guid,
             <Out(), MarshalAs(UnmanagedType.Interface, IidParameterIndex:=2)> ByRef ppv As Object)
        End Sub
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            ListBox1.Items.Add("C:\Users\RLWA32\Downloads\VS2013 Image Library.zip")
        End Sub
    
        Private Sub ListBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles ListBox1.MouseDown
            Dim fileName As String
            Dim pbc As IntPtr
            Dim hRes As Integer
            Dim shellItem As IShellItem
            Dim dataObj As IDataObject
    
            fileName = ListBox1.Items(0)
    
            pbc = Nothing
            shellItem = Nothing
            dataObj = Nothing
    
            Try
                SHCreateItemFromParsingName(fileName, pbc, IShellItemGUID, shellItem)
    
                shellItem.BindToHandler(pbc, BHID_DataObject, IDataObjectGUID, dataObj)
    
                ListBox1.DoDragDrop(dataObj, DragDropEffects.All)
    
                hRes = Marshal.ReleaseComObject(shellItem)
                hRes = Marshal.ReleaseComObject(dataObj)
    
            Catch ex As Exception
                MsgBox(ex.Message)
            End Try
    
        End Sub
    End Class
    

    Saturday, April 8, 2017 11:36 AM
  • New version of VB code.  I changed it to use Exceptions instead of returning HRESULTS and also improved the way that interfaces were returned from the SHCreateItemFromParsingName and IShellItem::BindToHandler.  BTW, in this version the sample file was 285 MB. :)

    Interesting approach, but I'm not sure that it's suitable for me. In my "real application" when user starts dragging usually I don't have file on disk, I have only it's description. I load file via webservice only in case user drops item to explorer. This is why I use so complicated d'n'd code. 

    Monday, April 10, 2017 10:43 AM
  • New version of VB code.  I changed it to use Exceptions instead of returning HRESULTS and also improved the way that interfaces were returned from the SHCreateItemFromParsingName and IShellItem::BindToHandler.  BTW, in this version the sample file was 285 MB. :)

    Interesting approach, but I'm not sure that it's suitable for me. In my "real application" when user starts dragging usually I don't have file on disk, I have only it's description. I load file via webservice only in case user drops item to explorer. This is why I use so complicated d'n'd code. 

    Well, I hope you can take advantage of what I provided at some point in the future so that my time and effort was not wasted.
    Monday, April 10, 2017 11:00 AM