【文章标题】: 为程序添加拖放功能
【文章作者】: Suyana
【作者邮箱】: suyasha@163.com
【参考信息】: pll621写的为W32dasm添加拖放文件功能

==========添加函数================
用PE编辑工具加入下面这3个函数(位于SHELL32.DLL):
DragAcceptFiles、DragFinish、DragQueryFile。记下它们的RVA。
在00401A08写入:
00401A08  jmp     [405074]  ;  SHELL32.DragAcceptFiles
00401A0E  jmp     [405078]  ;  SHELL32.DragFinish
00401A14  jmp     [40507C]  ;  SHELL32.DragQueryFileA
以后call 00401A08就是调用SHELL32.DragAcceptFiles函数了

DragAcceptFiles是设置窗口能否接受拖放消息,即能否发送WM_DROPFILES消息给这个窗口

=========设置窗口能否接受拖放消息=====================
1.设置窗口能否发送拖放消息。  查找CreateWindowExA"。有两个,一个是创建窗口,
  另一个是创建RichEdit。来到创建窗口的那条:

代码:

  004018BA  mov     [40302C], eax         ;主窗口的句柄
  004018BF  push    1                   ;改成jmp     00401A9B
  004018C1  push    dword ptr [40302C]  ;被覆盖

  在00401A9B写入:
代码:

  00401A9B  push    1             ; /Accept = TRUE
  00401A9D  push    dword ptr [40302C]  ; |hWnd = NULL
  00401AA3  call    00401A08      ; \DragAcceptFiles
  00401AA8  push    1
  00401AAA  push    dword ptr [40302C]
  00401AB0  jmp     004018C7

      -------------------------------------------------------
2.设置RichEdit能否发送拖放消息(可以不改,因为改了也没用,见本文最后一段)
  查找另一个"CreateWindowExA"。来到:

代码:

  004014D1  push    200                                ; |ExtStyle = WS_EX_CLIENTEDGE
  004014D6  call    <jmp.&user32.CreateWindowExA>    ; \CreateWindowExA
  004014DB  mov     [403034], eax         ;RichEdit的句柄
  ...
  00401547  call    00401990                         ; \SendMessageA=>jmp 00401A20
  0040154C  leave
  0040154D  retn

  在函数末把00401547  call    00401990改成jmp 00401A20
  在00401A20处写入:
 
代码:

 00401A20  pushad
  00401A21  push    1
  00401A23  push    dword ptr [403034]
  00401A29  call    00401A08    ;SHELL32.DragAcceptFiles
  00401A2E  popad
  00401A2F  call    00401990    ;user32.SendMessageA
  00401A34  jmp     0040154C

保存,打开程序,拖个文件到RichEdit,图标是不是已经改变了

=========加入判断拖放消息的代码===================
首先要找到消息处理的地方,加入判断拖放消息的代码。
查找"DefWindowProcA",双击来到:004017BF这里就是消息处理函数了。向上看,只有
  004017AA  jnz     short 004017B3跳到这里("DefWindowProcA"),把它改成jmp 00401A3D
在00401A3D写入:
  
代码:

00401A3D  jnz     short 00401A49
  00401A3F  call    0040154E
  00401A44  jmp     004017B1
  00401A49  cmp     eax, 233
  00401A4E  je      short 00401A55  ;是拖放消息跳
  00401A50  jmp     004017B3   ;转到默认的处理函数

  00401A55  nop               ;拖放消息,这只是临时的代码,以后会替换
  00401A56  jmp     004017B3   ;成打开文件的代码,现在运行才不会出错

现在程序能判断拖放消息,我们就需要根据这个消息做相关的动作了,也就是打开这个拖放文件。 
首先我们必须得到拖放文件的文件名,这就需要DragQueryFile 函数了 

 
引用:

 UINT DragQueryFile( 
    HDROP hDrop,  // handle to structure for dropped files -> 拖放的hdrop 
    UINT iFile,          // index of file to query                     -> 为0就可以了 
    LPTSTR lpszFile,      // buffer for returned filename          -> 存放文件名的缓冲区 
    UINT cch         // size of buffer for filename          -> 缓冲区的尺寸 
  ); 
现在需要找的就是hDrop参数,这个也就是msg的子参数:
  00401587  mov     eax, [ebp+C]                   ;ebp+C是msg,那么这个子参数就在ebp+10处

============下一步就是怎么打开文件======================
程序用CreateFileA打开文件,查找该函数,来到:

 
代码:

 004010E7  call    <jmp.&comdlg32.GetOpenFileNameA> ;显示打开文件对话框
  004010EC  or      eax, eax
  004010EE  je      0040117E      ;是否有选择一个文件
  004010F4  push    0            ; /hTemplateFile = NULL
  004010F6  push    80           ; |Attributes = NORMAL
  004010FB  push    3            ; |Mode = OPEN_EXISTING
  004010FD  push    0            ; |pSecurity = NULL
  004010FF  push    3            ; |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
  00401101  push    C0000000  ; |Access = GENERIC_READ|GENERIC_WRITE
  00401106  push    00403044  ; |FileName = ""
  0040110B  call    004019BA     ; \CreateFileA
  00401110  cmp     eax, -1
  00401113  jnz     short 0040112B

看上面的代码可以发现,在得到拖放的文件名后直接跳到004010F4,但不能执行上面的代码,否则会弹出对话框。所以我们自己写一个函数(不然会出错),跳到004010F4就可以了。然后,我写了代码并成功了。但是,要是文件有修改过,程序在打开其它文件前会问你是否保存或取消,但拖放的文件却不会,而是直接打开。所以,我们不直接跳到004010F4,而是先判断文件是否修改、是否要保存,才决定是否打开文件。看上去好像要写很多代码,实际上不用,因为原程序已经写好了。

在"CreateFileA"上,下断点,运行,打开一个文件,程序中断,按F8直到程序返回,返回到:

代码:

  004015D2  call    00401180  ;判断文件是否修改,若有,给出提示。
  004015D7  or      eax, eax
  004015D9  je      004017CB  ;按"取消",不打开新的文件
  004015DF  call    0040109F  ;打开文件,有文件对话框

开始写代码。知道写在哪吗?在00401A55,忘记的话看上面:《===加入判断拖放消息的代码===》

 
代码:

 00401A55  push    dword ptr [ebp+10]  ;hDrop
  00401A58  pop     dword ptr [403FFC]
  00401A5E  push    100                 ; /缓冲区大小,不要拖放路径大于255字符的文件
  00401A63  push    00403044         ; |Buffer = Richedit.00403044
  00401A68  push    0                  ; |FileIndex = 0
  00401A6A  push    dword ptr [403FFC]  ; |hDrop = NULL
  00401A70  call    00401A14           ; \DragQueryFile
  00401A75  call    00401AB9           ; 我们要写的自定义函数
  00401A7A  jmp     004017B3         ; 跳到消息循环

在00401AB9写入我们自定义的函数:

 
代码:

 00401AB9  push    ebp
  00401ABA  mov     ebp, esp
  00401ABC  add     esp, -58      ;以上不要的话好像会错,函数开头一般都是这样滴!
  00401ABF  call    00401180     ;判断文件是否修改,若有,给出提示。
  00401AC4  or      eax, eax
  00401AC6  je      004017CB    ;不打开文件
  00401ACC  jmp     004010F4  ;打开文件
  00401AD1  push    dword ptr [403FFC]    ; /hDrop = NULL
  00401AD7  call    00401A0E             ; \DragFinish
  00401ADC  retn

保存,试下吧,拖一个文件看一下吧。若上一个文件有修改过,还可以选择"保存"、"不保存"、
"不打开"等操作。反正我是成功了。这里有原程序和我修改过的程序。看一下吧。 

还有一点要说明,拖放文件时要拖放到程序的标题栏,这样才会打开文件。调试了一下发现拖放到RichEdit时,窗口不会发送"WM_DROPFILES"消息,自然也就无法打开文件了。 
可是我明明让RichEdit也能拖放文件,但为什么就是没有发送消息呢? 
谁能告诉我呢?  

========附件内容===============
Richedit原程序.exe : 《Windows环境下32位汇编语言程序设计》(著:罗云彬)的例子程序
Richedit.exe     : DIY PE的成果,添加了拖放文件的功能。by Suyana
pll621-01.htm     : 偶的参考资料