PDA

Ver la Versión Completa : cual es la mejor forma de trabajar con blob


anubis
10-09-2012, 00:04:37
Hola amigos,

Ya estoy leyendo y guardando imagenes jpg en archivos blob de un bd, pero estoy limitado a los jpg.
Cargo el archivo jpg en un stream y al reves e incluso los imprimo con fortesreport.
La única pega que tiene es que cuando la imagen ocupa toda una hoja, en el fortesreport preview, se dificulta hacer zoom o subir y bajar el scroll.
Las imagenes ocupan sobre 300kb.
No se podria hacer lo mismo con imagenes png o bien con pdf (que seria lo idoneo)?

Ya estuve buscando lo mismo que use para el jpg pero en png pero no lo encuentro.

como lo trabajais vosotros?

saludos y gracias

Casimiro Notevi
10-09-2012, 00:11:36
En un campo blob puedes guardar lo que quieras, ¿qué problema tienes?

anubis
10-09-2012, 00:15:40
Si, gracias. Mas que nada el poder abrir un pdf en lazarus y luego pasarlo a un blob tal y como lo hago con jpg.

O bien un un png,

el problema es trabajar con formatos, verlos en pantalla por ejemplo y luego guardarloCon jpg no tengo problema, se me dificultan los otros formatos. Lease png o pdf.

JPG:=TJPEGImage.Create;
if not OpenPictureDialog1.Execute then Exit;
jpg.LoadFromFile(OpenPictureDialog1.FileName);
Image1.Picture.Assign(JPG);
Stream := form1.ZQuery1.CreateBlobStream(form1.zquery1.FieldByName('factur'), bmWrite); // Asignamos el BLOB a un stream
jpg.SaveToStream(Stream); // y guardamos la imagen como JPEG (convertido), en el stream
Stream.Destroy;// liberamos

Esto es lo que uso.

saludos y gracias

Casimiro Notevi
10-09-2012, 01:04:23
Lo que quieres es verlo "dentro" de tu programa o abrirlo con el programa predeterminado del sistema, ejemplo, si es un html que se abra el firefox, si es un gif que se abra el visor de imágenes, etc.
¿Eso es lo que quieres?

anubis
10-09-2012, 01:07:23
con el jpg lo veo dentro de un timage,
un pdf o un png como lo puedo guardar en un blob desde dentro de mi programa?.

Casimiro Notevi
10-09-2012, 02:02:18
¿Guardarlo?, tienes varias formas, pero simplificando mucho:
dataset.campoblob.loadfromfile('elficheroquesea.pdf');
Haz una búsqueda por clubdelphi, encontrarás código para hacerlo, es un tema muy repetido.

Es más, creo recordar que tú mismo preguntaste esto hace unos meses. Me parece que con sqlite.

anubis
10-09-2012, 02:38:39
si, es cierto, pregunte, pero me faltaban esos detalles.

buscare en el foro, pero lo que me interesaba no es guardar sino poder abrir en mi aplicacion eso que se guarda, de ahi que use los jpg.

gracias

ecfisa
10-09-2012, 03:19:29
Hola anubis.

No utilizo Lazarus pero ví que en varios sitios citaban a la librería [MuPDF (http://en.wikipedia.org/wiki/MuPDF)], tal vez te sirva para lo que estas buscando.
Podes descargarlo de [aquí (http://www.ghostscript.com/download/mupdfdnld.html)] (GNU Public License).

Saludos.

anubis
10-09-2012, 03:52:49
Casimiro,
Ya resolvi una parte, la cuestion era que me empeñaba en usar el tjpegimage, cuando podia hacerlo como:


Image1.Picture.LoadFromStream(stream);


Asi me sirve para cualquier imagen soportada por timage.

lo unico que como png me ocupa 10 veces mas la imagen que con el jpg.

Como no voy a manipular imiagenes solo guardarlas en un blob y verlas.

Ahora me falta buscarle la forma con el pdf.

ecfisa, ya vi el componente pero no me sirve.

Lo que nose es si existe alguna forma de cargar el pdf de forma externa y pasarle el dato al stream para guardarlo?

Casimiro Notevi
10-09-2012, 10:07:44
Lo que nose es si existe alguna forma de cargar el pdf de forma externa y pasarle el dato al stream para guardarlo?
Normalmente lo que yo hago es extraer del blob y guardar el fichero en un directorio temporal, luego ejecuto una llamada para abrirlo y es el propio windows el que se encarga de abrirlo con el programa predeterminado que tenga según del tipo de fichero que sea.
O sea, que lo abro "externamente" a mi programa.

anubis
11-09-2012, 19:01:46
Gracias casimiro,

Ya estuve viendo el winexec, pero hace referencia a un archivo, lo que no entiendo como puedo, por ejemplo, es hacer una llamada para cargar una imagen en pdf (que lo abre con el acrobat p.e.) y de ahi pasarlo a un stream para guardarlo en un blob.
Porque me imagino que el proceso inverso, leer de un blob y pasarlo para visualizarlo con el acrobat.

Componentes para lazarus de pdf no he visto salvo powerpdf, pero solo para crear y en mi caso no he logrado que funcione bien.:(

gracias de nuevo.

Casimiro Notevi
11-09-2012, 19:25:38
Tú sólo has de decirle al sistema que lo abra, él sabrá con qué abrirlo.
Normalmente con ShellExecute, si mal no recuerdo.
Haz una búsqueda por esa palabra en los foros, encontrarás código de ejemplo.

mightydragonlor
11-09-2012, 20:17:48
TProcess para que sea multiplataforma con Lazarus.

Casimiro Notevi
11-09-2012, 20:38:40
TProcess para que sea multiplataforma con Lazarus.
¿La función como se llama así?

mightydragonlor
11-09-2012, 21:23:31
no, TProcess es una clase que permite ejecutar cualquier programa externo.
http://www.freepascal.org/docs-html/fcl/process/tprocess.html

roman
11-09-2012, 21:33:28
Pero ShellExecute sirve también para abrir el programa asociado al archivo que se le pasa. ¿TProcess también?

// Saludos

mightydragonlor
11-09-2012, 21:45:57
El ShellExecute sólo sirve para Windows, TProcess funciona con Win2, Linux y MacOS, pero depende mas de lo que quieras abrir y el como lo quieres abrir, es decir, puedes ejecutar excel y pasarle como parámetro la ruta del archivo con TProcess y funciona, ahora, si no sabes que ruta o que programa abre que, se puede usar la función OpenDocument, sólo debes poner en uses LCLIntf y LCLProc, y abrirá el documento con el programa asignado en el S.O.

roman
11-09-2012, 21:51:11
si no sabes que ruta o que programa abre que, se puede usar la función OpenDocument

Pues sí, es que esta funcionalidad es la que planteaba Casimiro desde un principio.

// Saludos

rretamar
11-09-2012, 23:28:09
Si son archivos muy grandes (y muchos) cuidadín con guardarlos en la base de datos. No sólo por el tamaño que alcanzará el archivo, sino porque si se daña la base de datos existe la posibilidad de que no se pueda recuperar la información. Cuesta un poco más (algunas líneas de código adicionales) pero en lo particular a los archivos de ese tipo (imágenes, video, audio) prefiero almacenarlos fuera de la base de datos, y colocando un campo donde almaceno la ubicación del archivo externo.

A lo mejor es que soy medio paranoico con la "pérdida de datos".

Como decía un profesor:

-"¿ Podés usar una consulta SQL que te de como resultado las morochas o las rubias en el campo de imágenes de la base de datos ?". -"!!! No !!!"...-"¿ Entonces para qué almacenas imágenes en una base de datos ?". :D

Casimiro Notevi
12-09-2012, 00:52:15
Como decía un profesor:
-"¿ Podés usar una consulta SQL que te de como resultado las morochas o las rubias en el campo de imágenes de la base de datos ?". -"!!! No !!!"...-"¿ Entonces para qué almacenas imágenes en una base de datos ?". :D

Pues si no guardas las imágenes... tampoco puedes saberlo ;)

No sé qué BD está usando, pero en los últimos 2012-1998=14 años nunca he tenido ningún problema de ese tipo con firebird y con BDs de muchos gigas.

roman
12-09-2012, 16:32:16
Pues además, ese argumento invalida todos los blobs, no sólo las imágenes.

// Saludos

rretamar
12-09-2012, 17:02:51
Pues además, ese argumento invalida todos los blobs, no sólo las imágenes.

// Saludos

Me refería a casos donde:

1) La cantidad de archivos a incluir en la base de datos sea muy grande (no hablo de decenas, sino de muchos miles por mes).

2) Los archivos sean de gran tamaño.

En esos dos casos me plantearía si realmente tiene sentido almacenarlos directamente dentro de un campo blob. Igual, insisto, es que soy un poco (¿ un poco ? :p ) paranoico con eso de la pérdida de datos, los riesgos, las copias de seguridad y todo eso.

Respondiendo a Casimiro: si bien no me dedico profesionalmente al desarrollo de software (es una actividad secundaria que tengo), las bases de datos que he usado son Firebird y SQLite.

Sobre las rubias, morochas o pelirrojas...al ritmo con que avanza todo (ver las funciones de reconocimiento de rostros que traen sitios como Google o Facebook...o muchas cámaras digitales) dentro de poco el estándar ANSI SQL incorporará cosas como:

Select Nombre, Edad, Estado_Civil, Foto From Empleados Where Foto = (Morochas Or Pelirrojas)

Será la era en que CUPIDO use una base de datos SQL para sus "trabajos".

roman
12-09-2012, 17:21:25
Select Nombre, Edad, Estado_Civil, Foto From Empleados Where Foto = (Morochas Or Pelirrojas)


Sería mejor así:

Select Nombre, Edad, Estado_Civil, Foto From Empleados Where Foto like :foto

Y proveemos la foto similar a la que queremos encontrar. O, ya por lo menos:

Select Nombre, Edad, Estado_Civil, Foto From Empleados Where Foto between :menor_expectativa and :mayorexpectativa

:D

// Saludos

anubis
13-09-2012, 00:38:08
Si es correcto, si lo pones en otra base de datos y se perdieran los blob, por lo menos el resto de los datos quedarian intactos, aunque tambien ha habido corrupcion de datos en firebird.

De todas formas, nose porque no hay un componente para leer pdf en lazarus. Segun habeis dicho en otros post, de excel es complicado que lo hubiera porque excel es privativo, pero esta libreoffice que tambien los lee, pdf seria privativo porqque es de acrobat, pero linux tambien tiene okular por ejemplo.

De ahi, siendo un componente que se suele usar, el pdf, y el excel (de excel si hay pero reducido), no lo haya para lazarus, teniendo en cuenta que hay bastantes componentes, lease el codetyphon que trae muchos.

lo del tprocess y el shellexecute los tengo que ver a fondo porque de momento no me entero muy bien como paso un pdf a un stream y de ahi a un blob y viceversa, pero seguire intentando.

Saludos

Casimiro Notevi
13-09-2012, 00:39:49
Ahora sí que no he entendido, ¿estás buscando un componente para leer archivos pdf?

mightydragonlor
13-09-2012, 01:16:37
Si es correcto, si lo pones en otra base de datos y se perdieran los blob, por lo menos el resto de los datos quedarian intactos, aunque tambien ha habido corrupcion de datos en firebird.

De todas formas, nose porque no hay un componente para leer pdf en lazarus. Segun habeis dicho en otros post, de excel es complicado que lo hubiera porque excel es privativo, pero esta libreoffice que tambien los lee, pdf seria privativo porqque es de acrobat, pero linux tambien tiene okular por ejemplo.

De ahi, siendo un componente que se suele usar, el pdf, y el excel (de excel si hay pero reducido), no lo haya para lazarus, teniendo en cuenta que hay bastantes componentes, lease el codetyphon que trae muchos.

lo del tprocess y el shellexecute los tengo que ver a fondo porque de momento no me entero muy bien como paso un pdf a un stream y de ahi a un blob y viceversa, pero seguire intentando.

Saludos
Pos de excel si hay y se llama TSpreadsheet, para leer archivos de pdf no conozco.

anubis
13-09-2012, 02:52:10
Casimiro, lo que propones puede ser una solucion, pero si existiera un componente como el timage para hacer lo mismo con los pdf estaria bien, pero no lo hay, el shellexecute y el tprocess todavia los estoy digeriendo ;), porque todavia no veo como abrir y pasar lo que he abierto (dentro de un tprocess o shellexecute, que todavia esta complicado) a un stream y de ahi guardarlo (el stream ya lo uso con las imagenes).

mightydragonlor, si, ese componente lo he usado, pero esta muy limitado, por ejemplo, combinar celdas no se puede, y determinadas fórmulas tampoco.

Casimiro Notevi
13-09-2012, 09:39:26
Esto abre la imagen con el programa que tenga asociado windows para hacerlo:

ShellExecute(0, 'open', 'c:\Windows\System32\rundll32.exe', 'c:\Windows\System32\shimgvw.dll,ImageView_Fullscreen c:\imagen.jpg', nil, SW_NORMAL);

Antes tienes que guardar la imagen en disco, claro.

anubis
13-09-2012, 14:40:08
Gracias casimiro por perder un poco de tu tiempo ;).

Creo ver por donde vas.
Si entendí bien con los campos blob, pueden contener cualquier cosa, en este caso, imagenes, pero los guarda en formato binario, y ya es cuestion de encontrar el componente que sea capaz de leerlos, en este caso timage.

Eso funciona bien, pero veo que en el caso del shellexecute, necesitas a fuerza una ruta y un nombre de archivo, cosa que en el blob no tengo.

si es asi verdad?

Casimiro Notevi
13-09-2012, 15:03:50
Gracias casimiro por perder un poco de tu tiempo ;).
Creo ver por donde vas.
Si entendí bien con los campos blob, pueden contener cualquier cosa, en este caso, imagenes, pero los guarda en formato binario, y ya es cuestion de encontrar el componente que sea capaz de leerlos, en este caso timage.
Eso funciona bien, pero veo que en el caso del shellexecute, necesitas a fuerza una ruta y un nombre de archivo, cosa que en el blob no tengo.
si es asi verdad?

Claro, un campo BLOB almacena lo que quieras.
Cuando se tiene un campo de ese tipo en el que se almacena "cualquier cosa" entonces es necesario tener otro campo que diga qué es, para poder guardarlo en disco y luego abrirlo, ejemplo:

create table tbAlmacen (
id integer not null,
descripcion varchar(64),
tipo varchar(16),
contenido blob sub_type 0,
primary key (id)
)

Y el contenido sería algo así:

1___Tutorial instalación Ubuntu___PDF___%$·/"%&%"%$·"%"··$
2___Foto de nubes_________________PNG___(&/$&$%&$·%$·"%$
3___Listado precios de NAS________TXT___Listado de precios de
4___Foto de moto__________________JPG___(/(//%&·&%$&%"·$

Así puedes extraer el que te interese a un directorio temporal, le pones un nombre temporal y la extensión que has guardado a la hora de crearlo (lo extraes del propio nombre del archivo cuando lo guardaste) y después lo abres como se ha indicado anteriormente.

anubis
21-09-2012, 16:23:44
Hola de nuevo,

Estaba probando lo que me Casimiro comentaba pero no me sale,

VAR
STREAM:TSTREAM;
A:TFILESTREAM;
begin
Stream := ZQuery1.CreateBlobStream(zquery1.FieldByName('facturA'), bmREAD); // Asignamos el BLOB a un stream
STREAM.Position:=0;

A:=TFILESTREAM.Create('D:\PADILLA.JPG',fmCreate);
A.CopyFrom(STREAM,STREAM.Size);


No se si va por ahi, el pasar el archivo a un directorio temporal, o hay otra forma de hacerlo.:confused:

saludos y gracias

Casimiro Notevi
21-09-2012, 16:34:15
Es que no tienes que crear ningún stream, del campo blob lo guardas directamente en el disco, para eso guardamos el tipo de fichero que es.
undataset.campoblob.savetofile('loquesea.xxx');

anubis
21-09-2012, 17:02:39
si, gracias, tienes razon se podria hacer asi pero intento hacer esto:


zquery1.FieldByName('factura')

pero no me deja ponerle asblob ni savetofile.

Seguramente sera una tonteria

Casimiro Notevi
21-09-2012, 17:39:29
si, gracias, tienes razon se podria hacer asi
¿Pero así te funciona?

anubis
21-09-2012, 17:58:20
Mas que nada porque no me deja poner mas porque no lo hay, nose cual es la instruccion para que haga l oque me dices.

gracias de nuevo

mightydragonlor
21-09-2012, 18:19:33
lo que dice casimiro no es un método de un FieldbyName('xxxxx').AsBlob, sino mas bien que al crear el DataSet, donde especifica que campo y que tipo de dato es, un TField si no estoy mal, pero esto no serviría para Dataset´s creados en tiempo de ejecución.

anubis
22-09-2012, 04:23:51
Buenas amigos,

Aplicando vuestros sabios consejos al final he conseguido algo de lo que pretendía, sacar una imagen de un blob y guardarlo en un archivo para poder visualizarlo externamente.
Lo saqué de otro post.

PROCEDURE TFORM1.LEERBLOB;
VAR
BLOB:TSTREAM;
BEGIN
blob := zquery1.CreateBlobStream(zquery1.FieldByName('factura'), bmRead);
try
blob.Seek(0, soFromBeginning);
with TFileStream.Create('PRUEBA.jpg', fmCreate) do
try
CopyFrom(blob, blob.Size)
finally
Free
end;
finally
blob.Free
end;
ShellExecute(0, 'open', PChar('prueba.jpg'), Nil,Nil, SW_SHOWNORMAL);
END;


En este caso, tenia almancenadas solo imagenes jpg, pero cambiando un poco las cosas posteriormente, podre meter finalmente los pdf o lo que sea (tal como decia casimiro, gracias).

El método que comentabais de no usar intermediarios sino guardarlo directamente no he sido capaz de realizarlo:(, pero este sirve.

Me falta ahora implementarlo al reves, que no tiene que ser dificil ;)

La última cuestión es relacionado con el shellexecute, funcionar funciona estupendamente, aunque me gustaba más el tprocess, tal y como he visto, pero le falla 1 punto:

- Se necesita un ejecutable a fuerza (si, opendocument funciona pero no es ni mas ni menos que un shellexecute).

Y la ventaja que si he visto es que si abres el archivo con tprocess, hasta que no cierras la aplicacion externa no vuelves a tu programa.

Entonces, de que otra forma se le puede decir a tprocess que abra un archivo, por ejemplo, un pdf, con el programa determinado en la máquina que lo estas corriendo.

saludos y gracias

ElMug
22-09-2012, 11:49:23
Puedes guardar lo que sea en un BLOB, pero MOSTRARLO como lo que es, en una aplicacion, solo lo podras hacer si tienes el driver especifico para el caso.

El asunto es que hay infinidad de formatos de archivos, y algunos no son necesariamente abiertos, o standard, al grado de que solo se abren, en lo practico, con su aplicacion, o aplicaciones, para el caso.

Para PDF necesitas controles de Adobe, que son gratuitos.

Una columna para todo tipo de BLOBS, por ejemplo, lo mas probable es que no sea algo muy practico. En mi opinion, tendria que estar muy justificado, por cuestiones practicas.

Una de esas cuestiones podria ser la magnitud de la data, aparte de el poder "mostrar" o USAR el BLOB directo de la aplicacion.

Mi uso de BLOBS se limita a imagenes, y como hay muchos formatos, he decidido limitar a JPEG y .bmp, por ejemplo (maximo dos TIPOS de blobs en una misma columna), con codigo para filtrar el almacenamiento, y codigo para descifrar el tipo y mostrarlas en la aplicacion. Y aun asi, lo practico es que las imagenes no sean de mucha resolucion, para que el tamaño del BLOB no afecte la fluidez del uso de ellos.

Si tu interes es en PDF, es recomedable que te aboques a resolver ESE caso en especifico, antes que tratar de generalizar.

Casimiro Notevi
22-09-2012, 12:40:17
En un BLOB puedes guardar lo que quieras, sin ningún problema, es simplemente una "cadena" de caracteres. Da igual si es una imagen, un pdf, un video, un texto, un mp3, un ejecutable, una base de datos completa, etc. cualquier cosa. La base de datos no va a hacer ningún tipo de control sobre lo que se guarda, le da igual.
Si se extrae lo almacenado en un campo blob y se guarda en disco, si intentamos abrirlo entonces el sistema operativo usará el programa que tenga predeterminado para el mismo, si lo tiene, y en caso contrario dirá que no puede abrirlo. No es ningún problema.
En mi extrabajo tenemos un módulo de seguimientos (para clientes/proveedores/artículos/vendedores/documentos/etc.) donde se almacena "cualquier cosa", y puede ser luego visionada, extraída, impresa, reproducida, etc. según el tipo de fichero del que se trate.
Por ejemplo, en la ficha de proveedores, en su apartado "Seguimientos" se pueden almacenar sus documentos en formato pdf, fotos de los representantes de la empresa, una imagen escaneada de un periódico donde hablan sobre esa empresa y que conviene tenerlo a mano, etc. cualquier cosa que sirva para tener información sobre ese proveedor.
Para este caso en concreto se usa una base de datos externa donde se almacena todo lo concerniente a "Seguimientos", para no recargar la base de datos principal de la gestión comercial.
O sea, que no hay que "restringirse" a uno o dos formatos de imágenes o documentos pdf. Que si hace falta almacenar más cosas, se almacenan y no pasa nada.
También (y aquí volvemos al eterno debate) se puede almacenar en directorios externos con sus pros y sus contras, que creo que no es necesario recordar.

Lo de almacenarlo en una base de datos aparte es porque ya llegan a tamaños bastante considerables, deben rondar ya los 50 gigas de media. Y no es necesario estar haciendo backup todos los días de esa información.

ElMug
22-09-2012, 13:27:38
Si, Casimiro, pero si para verlos hay que vaciar el Blob de la base de datos a un archivo temporal, y da casi lo mismo que tener ya los archivos originales en un folder, y almacenar en la base de datos solo la ubicacion del archivo.

De esa manera, la base de datos es mas ligera.

Lo unico es que la base de datos NO ES portatil. Pero si la base es fija, y los Blobs son grandes, pues en lo personal optaria por solo usar apuntadores a las ubicaciones.

Ademas no me es pausible que se guarde "todo tipo de Blobs", pues ya que Blobs pueden ser hasta peliculas, musica, etc, a la cual no le veo caso que se almacenen en base de datos, si no es que sea ESE el uso especifico.

Cada caso y uso, sin embargo, se debe de basar en lo que mas conviene.

Casimiro Notevi
22-09-2012, 13:54:45
Cada caso y uso, sin embargo, se debe de basar en lo que mas conviene.

Claro, cada caso es distinto y hay que estudiarlo por separado.

anubis
22-09-2012, 15:27:19
Estamos de acuerdo en que se puede guardar todo tipo de datos en un blob, la cuestión es que finalidad que le vayas a dar.
Por lo que entiendo, elmug prefiere limitar lo que se va a guardar por política de la empresa y casimiro guarda todo lo que sirve.
A fin de cuentas el que lleva el control es el programador, sino quieres que te almacenen archivos grandes, puedes limitar el tamaño también.
La aplicación que estoy haciendo, más que nada por utilidad y por seguir aprendiendo, porque al final son para uso propio, va a almacenar finalmente pdf, por una cuestión práctica (un pdf puede constar de varias páginas y en formato de imagen serian tantas imagenes como páginas).

Por otro lado, no es que quiera controlar los pdf y tuviera que usar controles adobe, porque estaria limitado a estos, porque yo no uso adobe reader sino foxitreader porejemplo, o bien, si quiero migrar la aplicacion a linux la cosa cambiaria.

Como no hay un componente pdf exclusivo en lazarus, la opcion de tprocess me parecia muy buena porque la de shellexecute es exclusiva para windows y además no puedes bloquear la aplicacion hasta que cierras, en este caso, el visor de pdf.

Ya no se si tengo que abrir otro post, porque ahora me estoy centrando en la apertura de documentos con el programa determinado en el SO para hacerlo con tprocess, pero esta muy complicado.
Opendocument, como digo, al final usa shellexecute.

Un saludo y gracias amigos

ElMug
22-09-2012, 15:44:55
Recuerda que en formato imagen puedes convenientemente, aunque con sus respectivas limitaciones, TAMBIEN mostrar un PDF (convirtiendolo en imagen), y es que el formato JPEG o el .bmp si estan muy universalizados.

Mis aplicaciones son portatiles, en las que uso imagenes, por esa sola razon, si guardo las imagenes en la base de datos.

anubis
22-09-2012, 16:18:49
Gracias.

Si, el problema de convertir un pdf a imagen es cuando el pdf tiene mas de 1 página, sino si lo dejaba en formato imagen.
De todas formas tanto los pdf como lo png o jpg no los voy a manipular sino mostrar o imprimir, de ahi que busque el shellexecute o el tprocess.

saludos

mightydragonlor
22-09-2012, 17:23:53
Recuerda que openDocument si es mucho mas que ShellExecute, ya que este último sólo funciona en Win2 y OpenDocument en Linux, MacOSx y Win2.
Saludos.