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:
#define MSGFLT_ALLOW 1 #define WM_COPYGLOBALDATA 0x0049 typedef BOOL (__stdcall *PChangeWindowMessageFilterEx)(HWND hWnd, UINT message, DWORD dwFlag, PVOID pChangeFilterStruct); PChangeWindowMessageFilterEx _ChangeWindowMessageFilterEx; void __fastcall TForm1::FormCreate(TObject *Sender) { // Para usar WM_DROPFILES en Win 7 y Win8 como administrador _ChangeWindowMessageFilterEx = (PChangeWindowMessageFilterEx)GetProcAddress(GetModuleHandle("User32.dll"), "ChangeWindowMessageFilterEx"); if(_ChangeWindowMessageFilterEx){ _ChangeWindowMessageFilterEx(Handle, WM_DROPFILES, MSGFLT_ALLOW, 0); _ChangeWindowMessageFilterEx(Handle, WM_COPYGLOBALDATA, MSGFLT_ALLOW, 0); } // Para aceptar arrastrar archivos. DragAcceptFiles(Handle, true); } void __fastcall TForm1::DropFiles(TWMDropFiles& Msg) { char archivo[MAX_PATH]; // Para recibir todos los archivos arrastrados int Count = DragQueryFile((HDROP)Msg.Drop, 0xFFFFFFFF, archivo, sizeof(archivo)); for(int i=0; i<Count; i++){ // Recibo cada archivo... DragQueryFile((HDROP)Msg.Drop, i, archivo, sizeof(archivo)); // las acciones a realizar con los nombres obtenidos //......................... } DragFinish((HDROP)Msg.Drop); }
En esta ocasión he usado el sistema de mensajes similar a delphi, para que no se enfade seoane.
El equivalente en delphi sería lo siguiente:
const MSGFLT_ALLOW = 1; WM_COPYGLOBALDATA = $0049; var ChangeWindowMessageFilterEx: function(Handle: HWND; Msg: UINT; action: DWORD; pChangeFilterStruct: Pointer): BOOL; stdcall; procedure TForm1.FormCreate(Sender: TObject); begin // Para usar WM_DROPFILES en Win 7 y Win8 como administrador @ChangeWindowMessageFilterEx:= GetProcAddress(GetModuleHandle('user32.dll'),'ChangeWindowMessageFilterEx'); if Assigned(ChangeWindowMessageFilterEx) then begin ChangeWindowMessageFilterEx(Handle, WM_DROPFILES, MSGFLT_ALLOW, nil); ChangeWindowMessageFilterEx(Handle, WM_COPYGLOBALDATA, MSGFLT_ALLOW, nil); end; DragAcceptFiles(Handle, True); end; procedure TForm1.DropFiles(var Msg : TWMDROPFILES); var Count, i : longInt; Archivo : array[0..MAX_PATH] of char; begin // Para recibir todos los archivos arrastrados Count := DragQueryFile(Msg.Drop, $FFFFFFFF, nil, 0) ; if Count > 0 then begin for i := 0 to Count -1 do begin DragQueryFile(Msg.Drop, i, @Archivo, sizeof(Archivo)) ; // las acciones a realizar con los nombres obtenidos //.............................. end; end; end;
Subo los fuentes y binarios de un ejemplo sencillo.
Saludos.