PDA

Ver la Versión Completa : Excepcion incontrolable al detectar medios extraibles


champy
09-12-2012, 08:16:47
Buenos días ante todo. Tengo una consulta para los más manitas de por aquí.

Estoy tratando de hacer que mi aplicación detecte cuando se conecta al pc un teléfono Android para sincronizar varios datos de la tarjeta SD, para eso he utilizado algunos ejemplos que he encontré por el foro.


Procedure Buscar;
begin

Try
for Letra:= 'C' to 'J' do
begin
if GetDriveType(Pchar(Letra+':\')) = DRIVE_REMOVABLE then
begin
//Solo es una prueba de acceder a la SD
If FileExists(Letra+':\prueba.txt') then ShowMessage('Tengo la SD, ahora sincronizo');
end;
end;
Except;
End;

End;


Este ejemplo es fantástico para unidades de USB comunes, pero con las tarjetas SD o teléfonos (En solo cargar) tengo un problema, el sistema asigna la letra de la unidad aunque no este montada o no tenga medio insertado, así que cuando hago el FileExists o cualquier otro intento de acceder al contenido para ver si la tarjeta está presente, salta una excepción del sistema que no puedo controlar con Try-Except diciendo que (Inserte un disco en la unidad X: ).

La pregunta, ¿Existe alguna forma de verificar si el medio está insertado y montado antes de acceder a el para que no salte la excepción? o bien ¿Puedo controlar de alguna forma esa dichosa excepción?

champy
09-12-2012, 08:25:27
Ya probé tambien a comprobar con un...


If DiskSize(N) >= 1 then Sincronizar;


Y sí, me devuelve un -1 cuando el medio no está presente, pero esta acción tambien provoca que salte la maldita excepción.

dec
09-12-2012, 09:34:49
Hola,

Tal vez debería usarse algo distinto a "FileExists", pero, yo miraría a ver en el sentido de tratar de ser avisado cuando una nueva unidad se conecta. Encontrarás varias formas de hacerlo, por ejemplo, en el paquete TurboPower ShellShock (http://sourceforge.net/projects/tpshellshock/) cuentas con un componente que te avisará cuando una unidad sea añadida y/o quitada del sistema. De esta forma igual hasta el "FileExists" podría servirnos... puesto que al menos la unidad en cuestión existiría... Aunque tal vez podríamos usar un "DirectoryExists" contra la propia unidad para evitar la "excepción" que te estás encontrando.

dec
09-12-2012, 10:49:43
Hola de nuevo,

Estoy pensando que acaso con "DirectoryExists" pase lo mismo que con "FileExists". Existe la posibilidad de obtener todas las unidades disponibles en un momento dado, de manera que, primero de todo, veamos si la letra de la unidad que nos interesa está en la listad de unidades disponibles o no. Echa un vistazo a este hilo (http://www.clubdelphi.com/foros/showthread.php?t=43633), por ejemplo. De todas formas sigo pensando que un "avisador" de que se añade o quita una unidad podría ser útil para el programa...

champy
09-12-2012, 11:10:32
La función GetDriveType me retorna DRIVE_REMOVABLE tanto si la tarjeta está montada como si no.

De hecho si miras el ejemplo que puse al iniciar el hilo verás que eso es exactamente lo que estoy comprobando para distingir la tarjeta del resto de unidades.


if GetDriveType(Pchar(Letra+':\')) = DRIVE_REMOVABLE then
begin
//Solo es una prueba de acceder a la SD
If FileExists(Letra+':\prueba.txt') then ShowMessage('Tengo la SD, ahora sincronizo');
end;


Por cierto, he probado y sí, DirectoryExists tambien hace saltar la puñetera excepción de "Inserte un disco"

De todas formas se agradece la ayuda DEC.

Quizás la solución pase por el componente que posteaste hace un rato y que me notifique la inserción de la tarjeta pero me estoy debanando los sesos para tratar de compilar el Package por que me da un error de una clase no definida.

champy
09-12-2012, 11:13:31
De todas formas, es que estoy seguro que debe haber alguna función en el API de windows que me diga si la unidad extraible tiene un medio insertado o no. Pero por más que busco no encuentro nada.

dec
09-12-2012, 11:56:38
Hola,

No me refería yo tanto a la función "GetDriveType" con esto último que he dicho, sino a obtener la lista de unidades disponibles, por ejemplo, con un código similar a este (http://www.greatis.com/delphicb/tips/lib/drives-validdrives.html). De este modo, antes de comprobar que un archivo o directorio exista (con lo que nos exponemos al error que comentas), digo, podremos comprobar que existe realmente la unidad en cuestión. Es decir, tú obtienes una lista como:


C:\
D:\
E:\


Entonces, si tú esperas la unidad en "I:\", ya con la anterior lista puedes comprobar que dicha unidad no se encuentra entre las disponibles. En todo caso, podrías intentar también entrarle por evitar el error que obtienes, bien con alguna directiva de compilación de Delphi, bien utilizando alguna función del API de Windows... pero lamento no poder darte ahora mismo más información sobre esto último.

beginner01
09-12-2012, 15:48:12
Hola.

Prueba con este código.

var
Letra: Char;
OldErrorMode: word;
begin

OldErrorMode:= SetErrorMode(SEM_FAILCRITICALERRORS);
try

for Letra:= 'C' to 'J' do
begin

if GetDriveType(Pchar(Letra+':\')) = DRIVE_REMOVABLE then
begin

//Solo es una prueba de acceder a la SD
If FileExists(Letra+':\prueba.txt') then
ShowMessage('Tengo la SD, ahora sincronizo');
end;
end;

finally
SetErrorMode(OldErrorMode);
end;

end;

SetErrorMode MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx)

nlsgarcia
09-12-2012, 21:13:01
champy,

Revisa este código:

Procedure GetDrive(FileName : String);
var
Driver: Pchar;
i, Length: Integer;
SearchFile : Boolean;

begin

SearchFile := False;
GetMem(Driver, 100);
Length := GetLogicalDriveStrings(100, Driver);

for i := 0 to Length-1 do
begin
if GetDriveType(Pchar(Driver[i] + ':\')) = DRIVE_REMOVABLE then
begin
try
If FileExists(Driver[i] + ':\' + FileName) then
begin
MessageDlg('Archivo Sincronizado en Medio Removible', mtinformation, [mbok], 0);
FreeMem(Driver);
SearchFile := True;
break;
end;
except
MessageDlg('Error de I/O', mtinformation, [mbok], 0);
FreeMem(Driver);
exit;
end;
end;
end;

if not Searchfile then
MessageDlg('Archivo No Sincronizado en Medio Removible', mtinformation, [mbok], 0)

end;

El código anterior busca en todos los medios activos y de tipo removible un archivo determinado hasta hallar la primera ocurrencia o finalizar los drivers activos, fue probado con medios USB y Tarjetas MicroSD vía USB y funciona correctamente.

Espero sea útil :)

Nelson.

champy
09-12-2012, 21:40:47
Acabo de aterrizar en casa y voy a probarlo ahora mismo. En un ratito cuento algo. Gracias por el aporte

champy
09-12-2012, 22:35:17
No hay forma.

Con la comprobación de DEC, la unidad "E:\" en mi caso, aparece como disponible aunque el medio no esté presente.

y con los ejemplos de Beginner y nlsgarcia sigo obteniendo la maldita excepcion.

He encontrado una forma de averiguar si la unidad está o no montada sin obtener el mensaje de error, aunque me parece bastante cutre y no se muy bien como aplicarla.


WinExec( Pchar('COMMAND.COM C\ DIR '+Letra+':\ '), SW_SHOWNORMAL);


Si la unidad está montada, el dir hace lo debido.
Pero si no está presente o montada la tarjeta retorna el mensaje : El dispositivo no está listo

Si pudiese recuperar ese mensaje de salida del DIR para comprobarlo, aunque sea una solución cutre.... al menos tendría una solución temporal hasta encontrar algo mejor. ¿Alguna sujerencia?

He intentado hacer un "Dir e:\ >> rutadeunfichero.txt" para guardar la salida en texto y luego poder cargarla desde la aplicación pero por lo visto en el msdos de Vista-7 está opción no sirve, se me están acabando las ideas.

champy
09-12-2012, 22:42:02
champy,
fue probado con medios USB y Tarjetas MicroSD vía USB y funciona correctamente.


Lo estoy probando con un teléfono android, que a los efectos debería hacer el papel de un lector multitarjetas por USB pero en el momento de conectarlo la tarjeta está capturada por el teléfono (Solo cargar) y por tanto el medio lo da como que no está presente aunque ya le asigna una letra, E: en mi caso.

Una vez pongo el teléfono en modo "unidad de disco" la tarjeta se monta y todo va bien, pero para ese momento ya ha saltado la excepcion como 1 millon de veces.

De todos modos se agradece el aporte :)

beginner01
09-12-2012, 23:40:28
Hola.

Revisaste el enlace que te dejé? hay varios "flags" que se le pueden pasar a la función según el tipo de error que tengas. En mi caso funciono con SEM_FAILCRITICALERRORS pero en tu caso puedes probar con las demás o leer la explicación que dan a ver si te sirve.

ecfisa
10-12-2012, 02:47:59
Hola champy.

Proba de este modo:

function MicroSDPresent: Boolean;
var
i : char;
FName: string;
Error: LongWord;
begin
Error := SetErrorMode(SEM_FAILCRITICALERRORS);
Result:= False;
for i := 'D' to 'Z' do
begin
if GetDriveType(PChar(i+':\')) = DRIVE_REMOVABLE then
Result:= CreateFile(PChar(Format('\\.\%s:\%s',[i,'prueba.txt'])),
GENERIC_READ, 0, nil, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0) <> INVALID_HANDLE_VALUE;
end;
SetErrorMode(Error);
end;

// llamada:
...
begin
if MicroSDPresent then
ShowMessage('MicroSD presente')
else
ShowMessage('MicroSD ausente');
end;


Saludos.

nlsgarcia
10-12-2012, 02:52:25
champy,

Revisa este código:

Procedure GetDrive(FileName : String);
var
Driver: Pchar;
i, Length: Integer;
SearchFile : Boolean;
OldErrorMode: word;

begin

OldErrorMode:= SetErrorMode(SEM_NOOPENFILEERRORBOX);

SearchFile := False;
GetMem(Driver, 100);
Length := GetLogicalDriveStrings(100, Driver);

try
for i := 0 to Length-1 do
begin
if GetDriveType(Pchar(Driver[i] + ':\')) = DRIVE_REMOVABLE then
begin
If FileExists(Driver[i] + ':\' + FileName) then
begin
MessageDlg('Archivo Sincronizado en Medio Removible', mtinformation, [mbok], 0);
SearchFile := True;
break;
end;
end;
end;
finally
SetErrorMode(OldErrorMode);
FreeMem(Driver);
if not Searchfile then
MessageDlg('Archivo No Sincronizado en Medio Removible', mtinformation, [mbok], 0)
end;

end;

Esta variante incluye las recomendaciones de beginner01 con el flag SEM_NOOPENFILEERRORBOX.

Revisa esta información:

SEM_NOOPENFILEERRORBOX:
If this flag is set, the operating system does not display a message box when it fails to find a a file. Instead, the error is returned to the calling process.

Espero sea útil :)

Nelson.

ecfisa
10-12-2012, 03:06:46
Hola champy.

Mira... voy a retocar un poco la función que hice a las apuradas ya que deja el archivo temporal en la tarjeta de memoria.
Y creo que a otros, tanto como a mí, podría no gustarles ese detalle... :rolleyes:

Así que la nueva versión es:

function MicroSDPresent: Boolean;
var
i : char;
FName: string;
Error: LongWord;
Hnd : THandle;
begin
Error := SetErrorMode(SEM_FAILCRITICALERRORS);
Result:= False;
for i := 'D' to 'Z' do
if GetDriveType(PChar(i+':\')) = DRIVE_REMOVABLE then
begin
FName:= Format('\\.\%s:\%s',[i,'@@@prueba@@@.txt']);
Hnd:= CreateFile(PChar(FName),
GENERIC_READ, 0, nil, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0);
if Hnd <> INVALID_HANDLE_VALUE then
begin
Result := True;
CloseHandle(Hnd);
DeleteFile(PChar(FName));
end;
end;
SetErrorMode(Error);
end;


Saludos. :)

nlsgarcia
10-12-2012, 05:18:25
champy,

Revisa este código:

Procedure GetDrive(FileName : String);
var
Driver: Pchar;
i, Length: Integer;
SearchFile : Boolean;
OldErrorMode: word;
searchResult : TSearchRec;

begin

OldErrorMode:= SetErrorMode(SEM_NOOPENFILEERRORBOX or SEM_FAILCRITICALERRORS);

SearchFile := False;
GetMem(Driver, 100);
Length := GetLogicalDriveStrings(100, Driver);

try
for i := 0 to Length-1 do
begin
if GetDriveType(Pchar(Driver[i] + ':\')) = DRIVE_REMOVABLE then
begin
if FindFirst(Driver[i] + ':\' + FileName, faArchive, searchResult) = 0 then
begin
MessageDlg('Archivo Sincronizado en Medio Removible', mtinformation, [mbok], 0);
SearchFile := True;
break;
end;
end;
end;
finally
SetErrorMode(OldErrorMode);
FreeMem(Driver);
if not Searchfile then
MessageDlg('Archivo No Sincronizado en Medio Removible', mtinformation, [mbok], 0)
end;

end;

Esta variante combina dos flags de la función SetErrorMode y utiliza FindFirst para verificar si el archivo esta presente en la unidad.

Espero sea útil :)

Nelson.

champy
10-12-2012, 08:03:25
Al fin. Funcionó.

La función MicroSDPressent de eficsa me da True cuando la tarjeta está disponible sin sacar el mesajito del sistema.

SetErrorMode no estaba sirviendo de mucho, lo probe con todas las flags, supongo que por que no se trata de una excepcion propiamente dicha, es más bien un "Dialogo del sistema" indicando que la unidad no tiene disco.

http://imagenes.trucoswindows.net/images/imported/2008/07/error1-1.png

En cualquier caso, ya está solucionado :p

Solo modifiqué la función para que me haga de comprobación de una unidad concreta, y listo, problema resuelto :)


function MicroSDPresent(i : char): Boolean;
var
FName: string;
Error: LongWord;
Hnd : THandle;
begin
Error := SetErrorMode(SEM_FAILCRITICALERRORS);
Result:= False;
if GetDriveType(PChar(i+':\')) = DRIVE_REMOVABLE then
begin
FName:= Format('\\.\%s:\%s',[i,'@@@prueba@@@.txt']);
Hnd:= CreateFile(PChar(FName),
GENERIC_READ, 0, nil, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0);
if Hnd <> INVALID_HANDLE_VALUE then
begin
Result := True;
CloseHandle(Hnd);
DeleteFile(PChar(FName));
end;
End;
SetErrorMode(Error);
end;


Muchas gracias a todos los interesados, casi me sabe mal por cuanto me habeis ayudado.