top
Loading...
采用API實現的文件拖放

借助API函數CallWindowProc,DragAcceptFiles,DragQueryFile,DragFinish同樣可以實現文件從資源管理器EXPLORE到應用程序的拖放,另一種方法是采用OLE拖放,可以參照趣味編程欄目的相關內容。下面我們一起來看看程序編寫的過程。

對于每個需要引用的API函數,我們需要先在程序中說明,如果只在某一窗體內聲明并使用它,則把它聲明為Private,而如果在模塊中聲明,且需要在整個工程內使用它,則需要把它聲明為Public。

Public Declare Function CallWindowProc& Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc&, ByVal hWnd&, ByVal Msg&, ByVal wParam&, ByVal lParam&)
Public Declare Sub DragAcceptFiles Lib "shell32.dll" (ByVal hWnd&, ByVal fAccept&)
Public Declare Function DragQueryFile& Lib "shell32.dll" Alias "DragQueryFileA" (ByVal hDrop&, ByVal iFile&,ByVal lpszFile$, ByVal cch&)
Public Declare Sub DragFinish Lib "shell32.dll" (ByVal hDrop&)
Private Declare Function SetWindowLong& Lib "user32" Alias "SetWindowLongA" (ByVal hWnd&,ByVal nIndex&, ByVal dwNewLong&)


SetWindowLong函數的作用是在窗口結構中為指定的窗口設置屬性,利用它我們來改變窗口的屬性參數,其中各個參數的意義如下表所示:

參數 意義
hwnd Long,欲為其取得信息的窗口的句柄
nIndex Long,這里取GWL_WNDPROC,表示設置該窗口的窗口函數的地址
dwNewLong Long,由nIndex指定的窗口信息的新值
返回值Long,指定數據的前一個值

在默認狀態下WINDOWS操作系統會指定一個窗口函數來接受和處理WINDOWS消息,而通過使用SetWindowLong函數改變窗口函數的地址使它指向我們自己寫的一個函數WindowProc,這樣WINDOWS消息就會轉由WindowProc函數接收和處理。于是我們就能判斷文件拖放的消息并進行處理。改變窗口屬性的語句如下:

procOld = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf WindowProc)


DragAcceptFiles、DragQueryFile、DragFinish三個函數的作用分別是:把某個窗口設置為接收拖放的容器、返回拖放的文件的數量和信息、終止拖放。

為了學習方便,以下提供了源代碼:

'-------------------------------------------
'          采用API實現的文件拖放
'-------------------------------------------
'程序說明:
'本例是采用子類派生技術實現的文件從EXPLORE到VB程序的拖放
'    通過三個API函數DragAcceptFiles、DragQueryFiles和
'DragFinish,并通過回調函數WindowProc,窗口屬性函數
'SetWindowLong、CallWindowProc的使用實現,而另一種同樣
'效果的實現方法:OLE拖放,你可以參照文件拖放的另外一個實
'例。
'-------------------------------------------
Option Explicit
 
Private Const GWL_WNDPROC As Long = (-4&)
   
  ' API call to alter the class data for this window
Private Declare Function SetWindowLong& Lib "user32" Alias "SetWindowLongA" (ByVal hWnd&, _
ByVal nIndex&, ByVal dwNewLong&)

Private Sub Form_Load()
 
  '定義 Picture1作為接收文件拖放的容器
  DragAcceptFiles Me.hWnd, 1&
 
  '整個procOld變量用來存儲窗口的原始參數,以便恢復
  ' 調用了 SetWindowLong 函數,它使用了 GWL_WNDPROC 索引來創建窗口類的子類,通過這樣設置
  '操作系統發給窗體的消息將由回調函數 (WindowProc) 來截取, AddressOf是關鍵字取得函數地址
  procOld = SetWindowLong(Me.hWnd, GWL_WNDPROC, AddressOf WindowProc)
'AddressOf是一元運算符,它在過程地址傳送到 API 過程之前,先得到該過程的地址
End Sub

Private Sub Form_Unload(Cancel As Integer)

  '此句關鍵,把窗口(不是窗體,而是具有句柄的任一控件,這里指Picture1)的屬性復原
  Call SetWindowLong(Me.hWnd, GWL_WNDPROC, procOld)

End Sub

Public Sub DropFiles(ByVal hDrop&)

  Dim sFileName$, IReturn&
  Dim nCount&, I As Integer
 
  '為sFileName分配存儲空間
  sFileName = String$(MAX_PATH, vbNullChar)
 
  '通過文件指針hDrop, DragQueryFile返回是否有文件拖放,nCount返回拖放文件的個數
  nCount = DragQueryFile(hDrop, -1, sFileName, MAX_PATH)
 
  '循環讀取每一個拖放的文件,把它在列表框中顯示出來
  For I = 0 To nCount - 1
  sFileName = String$(MAX_PATH, vbNullChar)
    '如果有文件拖放,接收文件名,并試圖把它在圖片框中打開
    'IReturn&
    IReturn& = DragQueryFile(hDrop, I, sFileName, MAX_PATH)
    List1.AddItem Left$(sFileName, IReturn)
  Next I
  '完成拖放操作
  DragFinish hDrop

End Sub
'以下代碼放在moudle中
 Option Explicit
 
  Public Const MAX_PATH As Long = 260&
 
  '標示我們要截獲的消息
  Public Const WM_DROPFILES As Long = &H233&


  '保存原 窗體屬性的變量,其實是默認的 窗體函數 的地址
  Public procOld As Long
 
  '
  Public Declare Function CallWindowProc& Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc&, _
                                                    ByVal hWnd&, ByVal Msg&, ByVal wParam&, ByVal lParam&)
                                                   
  '拖放操作相關的API函數

  Public Declare Sub DragAcceptFiles Lib "shell32.dll" (ByVal hWnd&, ByVal fAccept&)
                              
  Public Declare Function DragQueryFile& Lib "shell32.dll" Alias "DragQueryFileA" (ByVal hDrop&, ByVal iFile&, _
                                                                                  ByVal lpszFile$, ByVal cch&)
  Public Declare Sub DragFinish Lib "shell32.dll" (ByVal hDrop&)

'WARNING!!!!-----------------------------------------------------------

'注意這段代碼是不能用DEBUG一步步調試的,否則會造成錯誤(崩潰)

'對消息截獲的機制可以按下述理解:

'    這里要仔細理解一下,我們為窗體新指定了窗體函數地址,也就是說操作系統發送給窗體的

'消息將被 WindowProc函數 所截獲(而改變前消息是被默認的 窗體函數 所獲得并作相應處理的)

'    這樣我們在 WindowProc函數 中對所截獲的消息進行判斷,會有三種情況:
'<1>如果是需要通過程序來處理的消息就通過 WindowProc函數 中的相應語句處理;

'<2>如果是要原來的 窗體函數 來處理則把這個消息傳遞給原窗體函數(其實是指針指向的改變);

'<3>如果不是我們需要的消息,也傳遞給原 窗體函數 來處理。

'可以參見 改變系統菜單 中的源碼注釋
'WARNING!!!!-----------------------------------------------------------

'回調函數,用來截取消息
Public Function WindowProc(ByVal hWnd As Long, ByVal iMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
 
'確定接收到的是什么消息
Select Case iMsg
   
'如果是 通知文件放下 的消息,就攔截消息
Case WM_DROPFILES
'通知在FORM模塊中定義的DropFiles函數來接收 指向 放下的文件 的句柄
frmDragDropFiles.DropFiles wParam
     
'返回0并退出這個WindowProc
WindowProc = False
Exit Function
     
End Select
 
'如果不是我們需要的消息,則傳遞給原來的窗體函數處理
WindowProc = CallWindowProc(procOld, hWnd, iMsg, wParam, lParam)
End Function




相關內容

什么是子類派生技術

WINDOWS運行的基礎是“消息機制”,所謂的“消息”是一個唯一的值,這個值會被一個窗體或操作系統收到,它能告訴什么事件發生了以及需要采用什么樣的動作來響應。這與我們人類的神經系統將感知的信息傳遞給大腦,而大腦發出指令給我們的身體非常相似。于是每一個窗體都具有一個消息句柄,這個機制使得所有發自于WINDOWS操作系統的消息能被接收到需要強調的是每個窗體以及每個控件,包括按鈕、文本框、圖片框等都具有這樣的消息句柄。WINDOWS操作系統會跟蹤這些消息句柄,這稱為類結構中的一個WindowProc,所謂的類結構是于窗體句柄相關聯的。

當我們加入一個新的WindowProc函數而這個WindowProc與原始的窗體函數相符合的話,我們稱這個窗被子類化了。換言之,如果WINDOWS操作系統發給你所在的WindowProc一個消息,而你所在的WindowProc正在響應其它的動作,這時你必須將剩余的消息傳遞給一個默認的WindoProc。

如下所示: 操作系統消息-->你所在WindoProc-->默認的WindoProc而一個窗體是可以被子類化多次的,這樣就產生了如下的情況:

Windows Message Sender --> Your WindowProc --> Another WindowProc _--> Yet Another WindowProc --> Default WindowProc

What is subclassing anyway?

通過窗體子類化,你可以改變響應消息的順序,也就是說,你可以把消息傳遞到默認的WindowProc上而不立即響應。舉個例子:

如果我們要在接收到WM_PAINT 消息后,在窗體上畫出一些東西,可以用下面的語句實現:

Public Function WindowProc(Byval hWnd, Byval etc....)

Select Case iMsg'篩選出WM_PAINT消息
Case SOME_MESSAGE'如果是其他消息
DoSomeStuff

 Case WM_PAINT'如果是WM_PAINT 消息
'首先把消息傳遞給一個默認的WindowProc
 WindowProc = CallWindowProc(procOld, hWnd, iMsg, wParam, lParam)

DoDrawingStuff'進行畫圖操作

Exit Function
'因為我們已經把消息傳遞給默認的WindowProc,我們可以退出這個WindowProc
'
End Select
'
End Function


作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗