Jump to content


Photo

Drag and Drop Files en Win7 y Win8


  • Please log in to reply
5 replies to this topic

#1 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4107 posts
  • LocationMadrid - España

Posted 07 June 2015 - 03:17 PM

Es un tema conocido el de arrastrar ficheros y que nuestra aplicación realice acciones con ellos, la API DragQueryFile se encarga de decirnos cuantos se arrastraron y de rellenar un buffer con el nombre de cada uno de ellos. Pero para ello debemos estar en disposición de recibir el mensaje WM_DROPFILES. La API DragAcceptFiles nos habilita para recibirlos.

El problema y el objeto de este truco es que desde Win7 los sistemas de seguridad de Windows filtran los mensajes entre aplicaciones y este es uno de ellos, esto es especialmente cierto cuando la aplicación se ejecuta como administrador, de forma que el código que funcionaba en WinXP, deja de hacerlo. Para solventar el problema debemos deshabilitar el filtro para los mensajes WM_DROPFILES y WM_COPYGLOBALDATA, de ello se encarga la nueva API ChangeWindowMessageFilterEx.

El código que muestro, funciona el WinXP y Win7-8:
 


cpp
  1. #define MSGFLT_ALLOW  1
  2. #define WM_COPYGLOBALDATA 0x0049
  3.  
  4. typedef BOOL (__stdcall *PChangeWindowMessageFilterEx)(HWND hWnd, UINT message, DWORD dwFlag, PVOID pChangeFilterStruct);
  5. PChangeWindowMessageFilterEx _ChangeWindowMessageFilterEx;
  6.  
  7.  
  8. void __fastcall TForm1::FormCreate(TObject *Sender)
  9. {
  10.   // Para usar WM_DROPFILES en Win 7 y Win8 como administrador
  11.   _ChangeWindowMessageFilterEx = (PChangeWindowMessageFilterEx)GetProcAddress(GetModuleHandle("User32.dll"), "ChangeWindowMessageFilterEx");
  12.   if(_ChangeWindowMessageFilterEx){
  13.     _ChangeWindowMessageFilterEx(Handle, WM_DROPFILES, MSGFLT_ALLOW, 0);
  14.     _ChangeWindowMessageFilterEx(Handle, WM_COPYGLOBALDATA, MSGFLT_ALLOW, 0);
  15.   }
  16.  
  17.   // Para aceptar arrastrar archivos.
  18.   DragAcceptFiles(Handle, true);
  19. }
  20.  
  21. void __fastcall TForm1::DropFiles(TWMDropFiles& Msg)
  22. {
  23.   char archivo[MAX_PATH];
  24.   // Para recibir todos los archivos arrastrados
  25.   int Count = DragQueryFile((HDROP)Msg.Drop, 0xFFFFFFFF, archivo, sizeof(archivo));
  26.   for(int i=0; i<Count; i++){
  27.     // Recibo cada archivo...
  28.     DragQueryFile((HDROP)Msg.Drop, i, archivo, sizeof(archivo));
  29.     // las acciones a realizar con los nombres obtenidos
  30.     //.........................
  31.   }
  32.  
  33.   DragFinish((HDROP)Msg.Drop);
  34. }

En esta ocasión he usado el sistema de mensajes similar a delphi, para que no se enfade seoane. :p :D

El equivalente en delphi sería lo siguiente:


delphi
  1. const
  2.   MSGFLT_ALLOW = 1;
  3.   WM_COPYGLOBALDATA = $0049;
  4.  
  5. var
  6.   ChangeWindowMessageFilterEx: function(Handle: HWND; Msg: UINT; action: DWORD; pChangeFilterStruct: Pointer): BOOL; stdcall;
  7.  
  8. procedure TForm1.FormCreate(Sender: TObject);
  9. begin
  10.   // Para usar WM_DROPFILES en Win 7 y Win8 como administrador
  11.   @ChangeWindowMessageFilterEx:= GetProcAddress(GetModuleHandle('user32.dll'),'ChangeWindowMessageFilterEx');
  12.   if Assigned(ChangeWindowMessageFilterEx) then
  13.   begin
  14.     ChangeWindowMessageFilterEx(Handle, WM_DROPFILES, MSGFLT_ALLOW, nil);
  15.     ChangeWindowMessageFilterEx(Handle, WM_COPYGLOBALDATA, MSGFLT_ALLOW, nil);
  16.   end;
  17.   DragAcceptFiles(Handle, True);
  18. end;
  19.  
  20. procedure TForm1.DropFiles(var Msg : TWMDROPFILES);
  21. var
  22.    Count, i : longInt;
  23.    Archivo : array[0..MAX_PATH] of char;
  24. begin
  25.    // Para recibir todos los archivos arrastrados
  26.    Count := DragQueryFile(Msg.Drop, $FFFFFFFF, nil, 0) ;
  27.    if Count > 0 then
  28.    begin
  29.      for i := 0 to Count -1 do
  30.      begin
  31.       DragQueryFile(Msg.Drop, i, @Archivo, sizeof(Archivo)) ;
  32.        // las acciones a realizar con los nombres obtenidos
  33.        //..............................
  34.      end;
  35.   end;
  36. end;

Subo los fuentes y binarios de un ejemplo sencillo.



Saludos.

Attached Files


  • 2

#2 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 posts
  • LocationArgentina

Posted 08 June 2015 - 12:29 AM

Brillante escafandra  :ap:  :ap:  :ap:


  • 0

#3 seoane

seoane

    Advanced Member

  • Administrador
  • 1259 posts
  • LocationEspaña

Posted 08 June 2015 - 02:05 AM

Mucho mas bonito el código en delphi, donde va a parar, jajaja

Excelente como siempre :ap: :ap:
  • 0

#4 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14448 posts
  • LocationMéxico

Posted 08 June 2015 - 10:21 AM

Está muy buena la función DragQueryFile(), solo tengo una duda, ¿no hay problema con versiones de sistema operativo?, me ha tocado que algunas API no funcionan en todos los Windows.

 

Saludos


  • 0

#5 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4107 posts
  • LocationMadrid - España

Posted 08 June 2015 - 03:16 PM

DragQueryFile funciona en WinXP y Win8 y seguirá funcionando. La que no está claro que siga funcionando es ChangeWindowMessageFilter, es por eso que usé ChangeWindowMessageFilterEx pues M$ asegura que es la que se debe usar. La clave para que DragQueryFile funcione bien en Win7 y Win8 es usar antes ChangeWindowMessageFilterEx y esto es lo que me motivó para publicar este truco, pues para WinXP y DragQueryFile hay mucha documentación, pero no tanta para su uso tras win7.

 

En https://msdn.microsoft.com tienes toda la documentación acerca de las APIs que se consideran obsoletas y las nuevas que deben usarse.

 

 

Saludos.

 


  • 1

#6 FerCastro

FerCastro

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 636 posts
  • LocationCiudad de México

Posted 15 June 2015 - 04:24 PM

Excelente explicación y ejemplo. Yo necesito hacer un drag an drop desde un TTreeView hasta un DBGrid, pero simplemente no doy pie con bola. Algun ejemplo?

 

Saludos!!


  • 0




IP.Board spam blocked by CleanTalk.