DragHelper.vb 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. '来源:https://blog.csdn.net/simpleman2000/article/details/140952294
  2. Imports System.ComponentModel
  3. Imports System.Runtime.InteropServices
  4. Imports System.Windows.Interop
  5. Public Class DragHelper
  6. Public Event DragDrop As EventHandler
  7. Public Property DropFilePaths As String()
  8. Get
  9. Return _DropFilePathsBackingField
  10. End Get
  11. Private Set(value As String())
  12. _DropFilePathsBackingField = value
  13. End Set
  14. End Property
  15. Private _DropFilePathsBackingField As String()
  16. Public Property DropPoint As POINT
  17. Get
  18. Return _DropPointBackingField
  19. End Get
  20. Private Set(value As POINT)
  21. _DropPointBackingField = value
  22. End Set
  23. End Property
  24. Private _DropPointBackingField As POINT
  25. Public Property HwndIntPtrSource As HwndSource
  26. Public Sub AddHook()
  27. RemoveDragHook()
  28. HwndIntPtrSource.AddHook(AddressOf WndProc)
  29. Dim handle As IntPtr = HwndIntPtrSource.Handle
  30. If IsUserAnAdmin() Then RevokeDragDrop(handle)
  31. DragAcceptFiles(handle, True)
  32. ChangeMessageFilter(handle)
  33. End Sub
  34. Public Sub RemoveDragHook()
  35. HwndIntPtrSource.RemoveHook(AddressOf WndProc)
  36. DragAcceptFiles(HwndIntPtrSource.Handle, False)
  37. End Sub
  38. Private Function WndProc(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr
  39. Dim filePaths As String() = Nothing
  40. Dim point As New POINT()
  41. If TryGetDropInfo(msg, wParam, filePaths, point) Then
  42. DropPoint = point
  43. DropFilePaths = filePaths
  44. RaiseEvent DragDrop(Me, EventArgs.Empty)
  45. handled = True
  46. End If
  47. Return IntPtr.Zero
  48. End Function
  49. <DllImport("user32.dll", SetLastError:=True)>
  50. Private Shared Function ChangeWindowMessageFilterEx(hWnd As IntPtr, msg As UInteger, action As UInteger, ByRef pChangeFilterStruct As CHANGEFILTERSTRUCT) As Boolean
  51. End Function
  52. <DllImport("user32.dll", SetLastError:=True)>
  53. Private Shared Function ChangeWindowMessageFilter(msg As UInteger, flags As UInteger) As Boolean
  54. End Function
  55. <DllImport("shell32.dll")>
  56. Private Shared Sub DragAcceptFiles(hWnd As IntPtr, fAccept As Boolean)
  57. End Sub
  58. <DllImport("shell32.dll", CharSet:=CharSet.Unicode)>
  59. Private Shared Function DragQueryFile(hWnd As IntPtr, iFile As UInteger, lpszFile As StringBuilder, cch As Integer) As UInteger
  60. End Function
  61. <DllImport("shell32.dll")>
  62. Private Shared Function DragQueryPoint(hDrop As IntPtr, ByRef lppt As POINT) As Boolean
  63. End Function
  64. <DllImport("shell32.dll")>
  65. Private Shared Sub DragFinish(hDrop As IntPtr)
  66. End Sub
  67. <DllImport("ole32.dll")>
  68. Private Shared Function RevokeDragDrop(hWnd As IntPtr) As Integer
  69. End Function
  70. <DllImport("shell32.dll")>
  71. Private Shared Function IsUserAnAdmin() As Boolean
  72. End Function
  73. <StructLayout(LayoutKind.Sequential)>
  74. Public Structure POINT
  75. Public X As Integer
  76. Public Y As Integer
  77. End Structure
  78. <StructLayout(LayoutKind.Sequential)>
  79. Private Structure CHANGEFILTERSTRUCT
  80. Public cbSize As UInteger
  81. Public ExtStatus As UInteger
  82. End Structure
  83. Private Const WM_COPYGLOBALDATA As UInteger = &H49
  84. Private Const WM_COPYDATA As UInteger = &H4A
  85. Private Const WM_DROPFILES As UInteger = &H233
  86. Private Const MSGFLT_ALLOW As UInteger = 1
  87. Private Const MSGFLT_ADD As UInteger = 1
  88. Private Const MAX_PATH As Integer = 260
  89. Private Shared Sub ChangeMessageFilter(handle As IntPtr)
  90. Dim ver As Version = Environment.OSVersion.Version
  91. Dim isVistaOrHigher As Boolean = ver >= New Version(6, 0)
  92. Dim isNt61OrHiger As Boolean = ver >= New Version(6, 1)
  93. If isVistaOrHigher Then
  94. Dim status As New CHANGEFILTERSTRUCT With {.cbSize = 8}
  95. For Each msg As UInteger In New UInteger() {WM_DROPFILES, WM_COPYGLOBALDATA, WM_COPYDATA}
  96. Dim [error] As Boolean = False
  97. If isNt61OrHiger Then
  98. [error] = Not ChangeWindowMessageFilterEx(handle, msg, MSGFLT_ALLOW, status)
  99. Else
  100. [error] = Not ChangeWindowMessageFilter(msg, MSGFLT_ADD)
  101. End If
  102. If [error] Then Throw New Win32Exception(Marshal.GetLastWin32Error())
  103. Next
  104. End If
  105. End Sub
  106. Private Shared Function TryGetDropInfo(msg As Integer, wParam As IntPtr, ByRef dropFilePaths As String(), ByRef dropPoint As POINT) As Boolean
  107. dropFilePaths = Nothing
  108. dropPoint = New POINT()
  109. If msg <> WM_DROPFILES Then Return False
  110. Dim fileCount As UInteger = DragQueryFile(wParam, UInteger.MaxValue, Nothing, 0)
  111. ReDim dropFilePaths(CInt(fileCount) - 1)
  112. For i As UInteger = 0 To CInt(fileCount) - 1
  113. Dim sb As New StringBuilder(MAX_PATH)
  114. Dim result As UInteger = DragQueryFile(wParam, i, sb, sb.Capacity)
  115. If result > 0 Then dropFilePaths(i) = sb.ToString()
  116. Next
  117. DragQueryPoint(wParam, dropPoint)
  118. DragFinish(wParam)
  119. Return True
  120. End Function
  121. End Class