-
Notifications
You must be signed in to change notification settings - Fork 117
Expand file tree
/
Copy pathModDragHelper.vb
More file actions
152 lines (122 loc) · 5.26 KB
/
Copy pathModDragHelper.vb
File metadata and controls
152 lines (122 loc) · 5.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
Imports System
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Windows.Interop
Public Class DragFileHelper
Public Event DragDrop As EventHandler
Public Property DropFilePaths As String()
Get
Return _DropFilePathsBackingField
End Get
Private Set(value As String())
_DropFilePathsBackingField = value
End Set
End Property
Private _DropFilePathsBackingField As String()
Public Property DropPoint As POINT
Get
Return _DropPointBackingField
End Get
Private Set(value As POINT)
_DropPointBackingField = value
End Set
End Property
Private _DropPointBackingField As POINT
Public Property HwndIntPtrSource As HwndSource
Public Sub AddHook()
Me.RemoveDragHook()
Me.HwndIntPtrSource.AddHook(AddressOf WndProc)
Dim handle As IntPtr = Me.HwndIntPtrSource.Handle
If IsUserAnAdmin() Then RevokeDragDrop(handle)
DragAcceptFiles(handle, True)
ChangeMessageFilter(handle)
End Sub
Public Sub RemoveDragHook()
Me.HwndIntPtrSource.RemoveHook(AddressOf WndProc)
DragAcceptFiles(Me.HwndIntPtrSource.Handle, False)
End Sub
Private Function WndProc(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
Dim filePaths As String() = Nothing
Dim point As POINT = New POINT()
If TryGetDropInfo(msg, wParam, filePaths, point) Then
DropPoint = point
DropFilePaths = filePaths
RaiseEvent DragDrop(Me, EventArgs.Empty)
handled = True
End If
Return IntPtr.Zero
End Function
<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function ChangeWindowMessageFilterEx(ByVal hWnd As IntPtr, ByVal msg As UInteger, ByVal action As UInteger, ByRef pChangeFilterStruct As CHANGEFILTERSTRUCT) As Boolean
End Function
<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function ChangeWindowMessageFilter(ByVal msg As UInteger, ByVal flags As UInteger) As Boolean
End Function
<DllImport("shell32.dll")>
Private Shared Sub DragAcceptFiles(ByVal hWnd As IntPtr, ByVal fAccept As Boolean)
End Sub
<DllImport("shell32.dll", CharSet:=CharSet.Unicode)>
Private Shared Function DragQueryFile(ByVal hWnd As IntPtr, ByVal iFile As UInteger, ByVal lpszFile As StringBuilder, ByVal cch As Integer) As UInteger
End Function
<DllImport("shell32.dll")>
Private Shared Function DragQueryPoint(ByVal hDrop As IntPtr, ByRef lppt As POINT) As Boolean
End Function
<DllImport("shell32.dll")>
Private Shared Sub DragFinish(ByVal hDrop As IntPtr)
End Sub
<DllImport("ole32.dll")>
Private Shared Function RevokeDragDrop(ByVal hWnd As IntPtr) As Integer
End Function
<DllImport("shell32.dll")>
Private Shared Function IsUserAnAdmin() As Boolean
End Function
<StructLayout(LayoutKind.Sequential)>
Public Structure POINT
Public X As Integer
Public Y As Integer
End Structure
<StructLayout(LayoutKind.Sequential)>
Private Structure CHANGEFILTERSTRUCT
Public cbSize As UInteger
Public ExtStatus As UInteger
End Structure
Private Const WM_COPYGLOBALDATA As UInteger = &H49
Private Const WM_COPYDATA As UInteger = &H4A
Private Const WM_DROPFILES As UInteger = &H233
Private Const MSGFLT_ALLOW As UInteger = 1
Private Const MSGFLT_ADD As UInteger = 1
Private Const MAX_PATH As Integer = 260
Private Shared Sub ChangeMessageFilter(ByVal handle As IntPtr)
Dim ver As Version = Environment.OSVersion.Version
Dim isVistaOrHigher As Boolean = ver >= New Version(6, 0)
Dim isNt61OrHiger As Boolean = ver >= New Version(6, 1)
If isVistaOrHigher Then
Dim status As CHANGEFILTERSTRUCT = New CHANGEFILTERSTRUCT With {.cbSize = 8}
For Each msg As UInteger In New UInteger() {WM_DROPFILES, WM_COPYGLOBALDATA, WM_COPYDATA}
Dim [error] As Boolean = False
If isNt61OrHiger Then
[error] = Not ChangeWindowMessageFilterEx(handle, msg, MSGFLT_ALLOW, status)
Else
[error] = Not ChangeWindowMessageFilter(msg, MSGFLT_ADD)
End If
If [error] Then Throw New Win32Exception(Marshal.GetLastWin32Error())
Next
End If
End Sub
Private Shared Function TryGetDropInfo(ByVal msg As Integer, ByVal wParam As IntPtr, ByRef dropFilePaths As String(), ByRef dropPoint As POINT) As Boolean
dropFilePaths = Nothing
dropPoint = New POINT()
If msg <> WM_DROPFILES Then Return False
Dim fileCount As UInteger = DragQueryFile(wParam, UInteger.MaxValue, Nothing, 0)
ReDim dropFilePaths(CInt(fileCount) - 1)
For i As UInteger = 0 To fileCount - 1
Dim sb As New StringBuilder(MAX_PATH)
Dim result As UInteger = DragQueryFile(wParam, i, sb, sb.Capacity)
If result > 0 Then dropFilePaths(CInt(i)) = sb.ToString()
Next
DragQueryPoint(wParam, dropPoint)
DragFinish(wParam)
Return True
End Function
End Class