PDA

Ver la Versión Completa : Aplicación con parámetro cuando ya hay una instancia igual funcionando


Neftali [Germán.Estévez]
12-06-2014, 15:57:08
En mi sistema tengo un "tipo nuevo" de ficheros que he asociado a mi aplicación (extensión .ccq -por ejemplo-); De forma que cuando el usuario pulsa doble click desde el explorador sobre un fichero de tipo .CCQ, se abre mi aplicación (y como parámetro le llega el path del fichero); Hasta aquí sin problemas.

ACTUALMENTE:
Si la aplicación ya está abierta, al pulsar doble click sobre un fichero .CCQ el sistema abre una segunda instancia de mi aplicación que recibe el correspondiente parámetro (nombre de fichero). Esto es la situación normal (no la que yo quiero).

ME GUSTARÍA:
Que el sistema no abriera una segunda instancia de la aplicación -AP2- (si ya hay una funcionando -AP1-). Es un tema que se puede solucionar fácil y además ya lo hemos hablado otras veces aquí en el club. El problema es que si no ejecuto/lanzo esa segunda instancia de la aplicación -AP2-, me gustaría que la primera -AP1- (que tengo funcionando) recibiera "de alguna forma" la información que llegaba a la segunda (el path del fichero que se ha seleccionado con doble click en el explorador).

¿Me explico?

Se me han ocurrido algunas cosas, pero tal vez hay alguna solución sencilla que se me está pasando de largo.
Espero vuestras ideas.

Un saludo.

egostar
12-06-2014, 17:26:44
Que tal amigo,

Yo pensaría en algo sencillito, usaría un socket en cada aplicación para enviarla si es que ya existe una instancia corriendo del programa en cuestión, la otra pues es la que a mi me da algo de problema, con los mensajes del API, pero eso se lo dejo a mis buenos amigos "API-ceros" que todos ya conocemos :)

Saludos

roman
12-06-2014, 18:06:35
Memory mapped files.

// Saludos

dec
12-06-2014, 21:34:46
Hola,

Yo uso con buenos resultados el componente cyAppInstances de Cindy Components (http://tcycomponents.sourceforge.net/). Creo que hace lo que necesitas y se trata de un proyecto de código abierto (apoyado por donaciones) así que puedes al menos echar un vistazo y hasta tal vez usarlo directamente.

Al González
13-06-2014, 02:09:30
Hola Germán.

Creo que parte de la solución sería con lo que dice Román:
Memory mapped files.
De lo cual por cierto, no sé si recuerdas, se desprende aquel tema (http://www.clubdelphi.com/foros/showthread.php?t=53146) de los objetos superglobales.

Creo que con eso (memoria compartida) y un evento de bajo nivel (usando funciones como CreateEvent, OpenEvent y WaitForSingleObject) podrías obtener el funcionamiento que deseas.

Sin embargo no está en mi experiencia un caso igual al que planteas, por lo que seguro vendrán mejores consejos en los siguientes mensajes.

Un saludo. :)

Neftali [Germán.Estévez]
13-06-2014, 09:48:42
Hola a todos.
Gracias por las respuestas. Bueno, están bastante cerca de lo que tenía en mente, así que veo que no voy mal encaminado...
;)


Yo pensaría en algo sencillito, usaría un socket en cada aplicación para enviarla

Gracias Egostar. Pensaba aun en algo más sencillo que sockets.

Memory mapped files.


Hola Germán.
Creo que parte de la solución sería con lo que dice Román:
De lo cual por cierto, no sé si recuerdas, se desprende aquel tema (http://www.clubdelphi.com/foros/showthread.php?t=53146) de los objetos superglobales.

Esa era una de las opciones. En concreto ya hay otros mensajes en el club que hablan de ellos (Román ya sales en algunos):
http://www.clubdelphi.com/foros/showthread.php?t=22626
http://www.clubdelphi.com/foros/showthread.php?t=24659

Al, no recordaba la unidad mencionada. Ahora que me lo has dicho le he echado un vistazo y como el resto de la librería tiene una pinta fantástica.

Finalmente no es la opción escogida, tal vez por simplicidad, he acabado usando mensajes (WM_COPYDATA).


Yo uso con buenos resultados el componente cyAppInstances de Cindy Components[/URL]. Creo que hace lo que necesitas y se trata de un proyecto de código abierto (apoyado por donaciones) así que puedes al menos echar un vistazo y hasta tal vez usarlo directamente.

Gracias David. He oído hablar en varias ocasiones de estos componentes, pero nunca me los había mirado en detalle.
Para el tema de la instancia se puede usar el componente cyAppInstances, como comentas, aunque he preferido utilizar la unit (UIApp) que Román tiene en su web, que hace el mismo trabajo (de esa forma me evito instalar un componente más).

Cuando miraba en enlace, he visto que justo debajo hay otro componente que se llama TCyCommunicate y que va más encaminado a la solución que finalmente he adoptado. La de utilizar Mensajes. En concreto WM_COPYDATA.

También hemos hablado otras veces en el club y hay buenos ejemplos de este método aquí:
http://clubdelphi.com/foros/showthread.php?t=81589
http://clubdelphi.com/foros/showthread.php?t=82410

Además de los hilos comentados había llegado a este enlace que también describe este sistema:
http://voiceofgeek.blogspot.com.es/2009/02/memory-mapped-file-in-delphi.html

Finalmente he añadido a la unit de Román (http://romansg.clubdelphi.com/index.php?pg=uiapp) un procedimiento más, que justo después de activar la "instancia previa" de la aplicación que ya está ejecutándose, le envía un mensaje con el parámetro (el path del fichero).
Algo así:


// Le envía el primer parámetro por mensaje (si existe)
procedure SendMessageParam;
var
copyDataStruct: TCopyDataStruct;
str:string;
receiverHandle: THandle;
res : integer;
begin
// Si no hay parámetros, salimos...
if (ParamCount <= 0) then Exit;

// rellenar la estructura con los datos
Str := ParamStr(1);
copyDataStruct.dwData := 9999; // Por seguridad para detectar que es el mensaje correcto
copyDataStruct.cbData := 1 + Length(Str) ;
copyDataStruct.lpData := PChar(Str);

// Buscar la aplicación para enviar datos
receiverHandle := FindWindow(PChar('TFormMainQC'), nil) ;
if receiverHandle = 0 then begin
Exit;
end;

// DEBUG!!!!
MessageDlg('Enviando Mensaje ' + IntToStr(receiverHandle), mtInformation, [mbOK], 0);

// enviar los datos
res := SendMessage(receiverHandle, WM_COPYDATA, Integer(Application.Handle),
Integer(@copyDataStruct)) ;
end;


Para completar el código, os pongo el de recepción:


// procedimiento para recibir mensajes (parametros)
procedure TFormMainQC.WMCopyData(var Msg: TWMCopyData);
var
str:string;
begin
// Recuperamos la parte de la cadena
str := PChar(Msg.CopyDataStruct.lpData);

// Si no es la misma aplicaciones, salimos...
if (Msg.CopyDataStruct.dwData <> 9999) then begin
Exit;
end;

// Ejecutar la acción necesaria
...



Finalmente me ha parecido la más sencilla y rápida.
Si le veís algun problema comentadlo, aun estoy a tiempo... ;-)

AÑADIDO: Antes de preguntar estuve realizando algunas búsquedas en el foro (y fuera de él) y llegué a bastantes hilos (algunos de ellos los he colocado arriba). No hice mal la búsqueda, pero está claro que no la hice bien al 100%.
Hoy revisando otra vez, he encontrado un hilo al que no llegué ayer y que habla justamente de este problema.
"Abrir fichero asociado si la aplicacion ya esta activa."
http://clubdelphi.com/foros/showthread.php?t=64015
Lo mejor de todo es que Román es justo quien da la solución uilizando el mensaje WM_COPYDATA
Y aporta este link como complemento:
http://www.delphidabbler.com/articles?article=13&part=2#cmdline
Donde ya está programado un procedimiento justo para lo que necesito (SendParamsToPrevInst)...


function SendParamsToPrevInst(Wdw: HWND): Boolean;
var
CopyData: TCopyDataStruct;
I: Integer;
CharCount: Integer;
Data: PChar;
PData: PChar;
begin
CharCount := 0;
for I := 1 to ParamCount do
Inc(CharCount, Length(ParamStr(I)) + 1);
Inc(CharCount);
Data := StrAlloc(CharCount);
try
PData := Data;
for I := 1 to ParamCount do
begin
StrPCopy(PData, ParamStr(I));
Inc(PData, Length(ParamStr(I)) + 1);
end;
PData^ := #0;
CopyData.lpData := Data;
CopyData.cbData := CharCount * SizeOf(Char);
CopyData.dwData := cCopyDataWaterMark;
Result := SendMessage(
Wdw, WM_COPYDATA, 0, LPARAM(@CopyData)
) = 1;
finally
StrDispose(Data);
end;
end;


Un saludo a todos.

Al González
13-06-2014, 17:08:44
Muchas gracias por el informe tan detallado, Germán. Le va a servir a muchos colegas que tengan la misma necesidad. :)

Ya había olvidado el mensaje wm_CopyData, me parece que es buena alternativa. Aunque yo usaría la clase TSuperGlobalObject, por simplicidad. :p

Un abrazo.

roman
13-06-2014, 17:48:04
Qué bueno que usaste uiapp :)

Sin embargo, creo recordar que algunas personas han reportado que con delphis y/o windows modernos ya no funciona del todo bien. Habría que verificar.

Por otro lado, está bien eso de ahorrar unidades pero los memory mapped files, te resolverían no sólo el paso del path sino también la ejecución de una sóla instancia.

Finalmente, me he vuelto flojo y yo usaría lo que te propone dec :)

Por cierto dec, no deja de sorprenderme tu sapiencia de software libre muy útil. No estaría de más si nos hicieras un compendio de todo el que conozcas :)

// Saludos

dec
13-06-2014, 20:19:41
Hola,

No creas Román... siempre "tiro" de Google y de Torry's (http://www.torry.net/), sobre todo. Lleva mucho tiempo rondándome la idea de hacer un sitio web del estilo de Torry's pero un poco más "moderno" (pa' meter la pata a lo peor) pero hasta el día de hoy no he hecho nada en absoluto. Por lo demás lo que pasa es que casualmente uso el componente que he mencionado en este hilo, y, claro, me he acordado enseguida de que igual podría ser útil para Germán también. :)

Neftali [Germán.Estévez]
16-06-2014, 10:33:51
Sin embargo, creo recordar que algunas personas han reportado que con delphis y/o windows modernos ya no funciona del todo bien. Habría que verificar.

Gracias Román.
Necesitaré verificarlo, pues el programa tiene que correr sin problemas en versiones nuevas.
Reportaré lo que encuentre.

Un saludo.