---- 大家一定看过有一些应用程序支持从资源管理器把文件拖动到应用应用程序文本框,在文本框中自动打开该文件的方法,现在我把这种方法的实现原理给大家剖析一下。
---- 这里要用到如下几个函数:DragQueryFile()、DragAcceptFiles()、DragFinish()。
---- DragAcceptFiles(ByVal hwnd As Long, ByVal fAccept As Long)的作用是使一个窗口接收拖动过来的文件,hwnd是该窗口的句柄,fAccept在函数的C原型中是Bool类型,设为True表示使该窗口接收拖过来的文件,为False表示不接收。
---- DragQueryFiles(ByVal HDROP As Long, ByVal UINT As Long, ByVal lpStr As _ String, ByVal ch As Long)作用是查询拖动过来的文件名。HDOP是一个内部数据结构的地址,该结构中包含了Drag过来的文件名;UNIT是文件索引,若设为-1,则函数返回拖动过来的文件的数目;lpStr是以vbNULL结尾的字符串,用以接收返回的文件名;ch是lpStr 的缓冲区大小。
---- DragFinsish(ByVal hDrop As Long)释放Windows被分配用来向应用程序传送文件名的内存。
---- 拖动文件时应用程序会接收到WM_DROPFILES消息,该消息的wParam中的数据为hDrop。程序中还要用到函数SetWindowLong(ByVal hwnd As Long, ByVal nIndex As Long,_ ByVal dwNewLong As Long),这个函数的用法很多,取决于参数nIndex,这里用到的值是GWL_WNDPROC,作用是重新指定一个窗口的窗口函数,返回该窗口原来的窗口函数, hwnd为要指定窗口函数的窗口句柄,dwNewLong为新窗口函数的地址。这个过程即常说的子类(SubClass)过程。拦截需要的消息后要把其余的消息发往默认的窗口函数时要用到 CallWindowProc(ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As_ Long, ByVal wParam As Long, ByVal lParam As Long),lpPrevWndFunc是默认窗口函数的地址,最后三个参数是消息参数。这个发送其余消息的过程非常重要,否则程序会不响应你的其它任何操作。
---- 下面是一段示例代码,这是一个多文档记事本程序中的一部分:
模块中声明:
Public Declare Sub DragAcceptFiles Lib "shell32.dll" (ByVal hwnd As Long, ByVal fAccept As Long)
Public Declare Sub DragFinish Lib "shell32.dll" (ByVal hDrop As Long)
Public Declare Function DragQueryFile Lib "shell32.dll" Alias "DragQueryFileA" (ByVal hDrop As Long, ByVal UINT As Long, ByVal lpStr As String, ByVal ch As Long) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Const WM_DROPFILES = &H233
Public Const GWL_WNDPROC = (-4)
Public OldWndProc&
Public Sub NewtxtWndFunc(ByVal Hwndl&)
OldWndProc = SetWindowLong(Hwndl, GWL_WNDPROC, AddressOf txtWndProc)’返回 窗口的默认窗口函数的地址,以便恢复
End Sub
Public Const GWL_WNDPROC = (-4)
Public OldWndProc&
Public Sub NewtxtWndFunc(ByVal Hwndl&)
OldWndProc = SetWindowLong(Hwndl, GWL_WNDPROC, AddressOf txtWndProc)’返回 窗口的默认窗口函数的地址,以便恢复
End Sub
Public Sub OldtxtWndFunc(ByVal Hwndl&)' 恢复窗口的默认窗口函数
SetWindowLong Hwndl, GWL_WNDPROC, OldWndProc
End Sub
SetWindowLong Hwndl, GWL_WNDPROC, OldWndProc
End Sub
Public Function txtWndProc&(ByVal NewHwnd&,ByVal uMsg&, ByVal wParam&, ByVal lParam&)
Select Case uMsg
Select Case uMsg
Case WM_DROPFILES
Dim Handle&, FileNameBuff$, Result&, hDrop&, FileNum&
Dim fLine$
hDrop = wParam
FileNameBuff = Space(100)
Result = DragQueryFile(hDrop, (-1), FileNameBuff, 100)'获得拖动的文件的数目
For I& = 0 To Result - 1
DragQueryFile hDrop, I, FileNameBuff, 100
xyOpenFile Trim(FileNameBuff)
Next
DragFinish hDrop
txtWndProc = 1
Case Else
txtWndProc = CallWindowProc(OldWndProc, NewHwnd, uMsg, wParam, lParam)
End Select
End Function
Dim Handle&, FileNameBuff$, Result&, hDrop&, FileNum&
Dim fLine$
hDrop = wParam
FileNameBuff = Space(100)
Result = DragQueryFile(hDrop, (-1), FileNameBuff, 100)'获得拖动的文件的数目
For I& = 0 To Result - 1
DragQueryFile hDrop, I, FileNameBuff, 100
xyOpenFile Trim(FileNameBuff)
Next
DragFinish hDrop
txtWndProc = 1
Case Else
txtWndProc = CallWindowProc(OldWndProc, NewHwnd, uMsg, wParam, lParam)
End Select
End Function
Public Function xyOpenFile(ByVal FN$) As Boolean
If BeUsed(MDIForm1.ActiveForm) Then
MDIForm1.MunNew_Click
End If
Dim FileNum&, fLine$
FileNum = FreeFile()
MDIForm1.ActiveForm.Caption = FN
Open FN For Input As #FileNum
fLine = StrConv(InputB(LOF(FileNum), #FileNum), vbUnicode)
MDIForm1.ActiveForm.Text1 = fLine
Close
End Function
If BeUsed(MDIForm1.ActiveForm) Then
MDIForm1.MunNew_Click
End If
Dim FileNum&, fLine$
FileNum = FreeFile()
MDIForm1.ActiveForm.Caption = FN
Open FN For Input As #FileNum
fLine = StrConv(InputB(LOF(FileNum), #FileNum), vbUnicode)
MDIForm1.ActiveForm.Text1 = fLine
Close
End Function
在子窗口Form1中有一个Text1文本框,Form1的Load过程中代码如下:
Private Sub Form_Load()
DragAcceptFiles Text1.hwnd, True
NewtxtWndFunc Text1.hwnd
End Sub
Private Sub Form_Load()
DragAcceptFiles Text1.hwnd, True
NewtxtWndFunc Text1.hwnd
End Sub
习惯上要在窗口的退出过程中恢复窗口的默认窗口函数:
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
OldtxtWndFunc Text1.hwnd
End Sub
OldtxtWndFunc Text1.hwnd
End Sub
---- 这个程序支持多个文件的拖动,BeUsed(Byval hwnd1)是一个自己编的函数,用于测试hwnd1这个窗口是否已用于打开其他的文件。MDIForm1是一个多文档窗口。DragQueryFile中的文件名缓冲区一定要足够大,否则VB会出现“一般保护性错误(GPF)”(Global Protection Failure)而把程序连同VB一起关掉,当初我设为20,就老是出现这样的现象,还无法通过调试查其确切原因,VB中使用API经常会因为传入参数类型不对或缓冲区太小而出现GPF,大家使用的时候要小心注意,调试前及时存盘。
---- 这个小程序只是一个引子,和拖动相关的函数还有:DragObject()、DragDelect()、DragQueryPoint(),第一个是拖动一个对象,后两个是测试拖动点的,这样可以实现Netants的链接拖动功能。希望这个程序能给你一点小的提示。