Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > API de Windows
Registrarse FAQ Miembros Calendario Guía de estilo Buscar Temas de Hoy Marcar Foros Como Leídos

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 05-07-2004
Lorenzati Lorenzati is offline
Registrado
 
Registrado: Jul 2004
Posts: 1
Poder: 0
Lorenzati Va por buen camino
Ejecutar aplicacion externa y que este en primer plano

Hola, necesito ejecutar una aplicacion externa (lo estoy haciendo con WinExec('C:\winnt\notepad.exe',1) ) y que este siempre en primer plano sin importar las aplicaciones que se abran despues. Quiero hacer un timer que cada x segundos muestre la aplicacion pero no se como hacerlo.

Muchas gracias
Responder Con Cita
  #2  
Antiguo 06-07-2004
Avatar de roman
roman roman is offline
Moderador
 
Registrado: May 2003
Ubicación: Ciudad de México
Posts: 20.172
Poder: 10
roman Tiene un aura espectacularroman Tiene un aura espectacular
Puedes intentar encontrar el identificador de la ventana de la aplicación externa usando FindWindow. Una vez que lo obtengas usas la función SetWindowPos para colocarla siempre visible:

Código Delphi [-]
const
  Flags = SWP_NOMOVE or SWP_NOSIZE;

var
  NotePadWindow: HWnd;

begin
  NotePadWindow := FindWindow('Notepad', nil); 
  SetWindowPos(NotePadWindow, HWND_TOPMOST, 0, 0, 0, 0, Flags);
end;

// Saludos
Responder Con Cita
  #3  
Antiguo 06-07-2004
Avatar de roman
roman roman is offline
Moderador
 
Registrado: May 2003
Ubicación: Ciudad de México
Posts: 20.172
Poder: 10
roman Tiene un aura espectacularroman Tiene un aura espectacular
Hola, estuve revisando este tema. El problema con el método anterior es que el usuario podría tener abiertas otras instancias de la aplicación externa y no hay garantía de que FindWindow te regrese la ventana que tú abriste en lugar de otra.

Un método un poco más complicado consiste en abrir la aplicación directamente con CreateProcess para obtener el identificador del hilo de la aplicación:

Código Delphi [-]
{
  Abre la aplicación indicada en AppPath y
  devuelve el hilo de la nueva aplicación.
}
function ExecuteProcess(AppPath: String): Cardinal;
var
  StartInfo: TStartupInfo;
  ProcInfo: TProcessInformation;

begin
  FillChar(StartInfo, SizeOf(StartInfo), 0);
  StartInfo.cb := SizeOf(StartInfo);

  if not CreateProcess(
    PChar(AppPath), nil, nil, nil, false, 0, nil, nil, StartInfo, ProcInfo
  )
  then
    RaiseLastOSError;

  Result := ProcInfo.dwThreadId;
end;

Ahora la cuestión es obtener el identificador de la ventana principal asociada al hilo. El problema es que el concepto de ventana principal en realidad es poco claro. Hay aplicaciones, como Word, donde todas las ventanas abiertas son principales y simplemente la última en cerrarse es la que manda cerrar la aplicación.

De cualquier manera la siguiente función GetMainWindow que sigue te devolverá el identificador de la primera ventana que encuentre y que tenga "aspecto" de ser la principal:

Código Delphi [-]
function DoEnum(Handle: HWnd; Param: LParam): Bool; stdcall;
begin
  if
    IsWindowVisible(Handle)
    and IsWindowEnabled(Handle)
  then
  begin
    PInteger(Param)^:= Handle;
    Result := false;
  end
  else
    Result := true;
end;

{
  Recorre todas las ventanas principales asociadas
  al hilo hasta encontrar una que esté visible y activa.

  EnumThreadWindows llama a la función DoEnum para
  cada ventana encontrada.
}
function GetMainWindow(ThreadId: Cardinal): HWnd;
begin
  Result := 0;
  EnumThreadWindows(ThreadId, @DoEnum, Integer(Pointer(@Result)));
end;

Cuando quieras poner la aplicación como siempre visible usarías algo como

Código Delphi [-]
procedure MakeAppTopMost(ThreadId: Cardinal);
const
  Flags = SWP_NOMOVE or SWP_NOSIZE;

var
  WindowId: HWnd;

begin
  WindowId := GetMainWindow(ThreadId);

  BringWindowToTop(WindowId);
  if IsIconic(WindowId) then
    ShowWindow(WindowId, SW_RESTORE);

  SetWindowPos(WindowId, HWND_TOPMOST, 0, 0, 0, 0, Flags);
end;

pasándole el ThreadId obtenido con ExecuteProcess. La llamada a BringWindowToTop no parece ser necesaria para aplicaciones sencillas como el block de notas pero sí para aplicaciones como Word.

Posteriormente puedes usar el ThreadId obtenido para cerrar la aplicación externa:

Código Delphi [-]
procedure CloseApp(ThreadId: Cardinal);
var
  WindowId: HWnd;

begin
  WindowId := GetMainWindow(ThreadId);
  SendMessage(WindowId, WM_CLOSE, 0, 0);
end;

Nota que tanto MakeAppTopMost como CloseApp vuelven a calcular el identificador de ventana a partir del identificador de hilo. Esto es así porque el identificador de ventana puede cambiar a lo largo de la vida de la aplicación. Por ejemplo, en una aplicación hecha en Delphi, si durante la ejecución cambias el estilo del borde de la ventana, ésta se crea nuevamente dando un nuevo identificador. En cambio, el identificador de hilo permanece inmutable durante toda la ejecución de una aplicación.

// Saludos

Última edición por roman fecha: 06-07-2004 a las 16:51:38. Razón: Un identificador en código mal nombrado
Responder Con Cita
  #4  
Antiguo 06-07-2004
Avatar de delphi.com.ar
delphi.com.ar delphi.com.ar is offline
Federico Firenze
 
Registrado: May 2003
Ubicación: Buenos Aires, Argentina *
Posts: 5.874
Poder: 21
delphi.com.ar Va por buen camino
Cita:
Empezado por roman
Hola, estuve revisando este tema. El problema con el método anterior es que el usuario podría tener abiertas otras instancias de la aplicación externa y no hay garantía de que FindWindow te regrese la ventana que tú abriste en lugar de otra.

Un método un poco más complicado consiste en abrir la aplicación directamente con CreateProcess para obtener el identificador del hilo de la aplicación
Haberlo visto antes!... tengo código escrito al respecto...
Un detalle, en mi código cuando busco la ventana perteneciente al hilo de ejecución, manejo un timeout interno porque sucede que si el equipo no es muy veloz, es posible que el proceso esté creado pero no la ventana.

Saludos!
__________________
delphi.com.ar

Dedique el tiempo suficiente para formular su pregunta si pretende que alguien dedique su tiempo en contestarla.
Responder Con Cita
  #5  
Antiguo 06-07-2004
Avatar de roman
roman roman is offline
Moderador
 
Registrado: May 2003
Ubicación: Ciudad de México
Posts: 20.172
Poder: 10
roman Tiene un aura espectacularroman Tiene un aura espectacular
Cita:
Empezado por delphi.com.ar
Un detalle, en mi código cuando busco la ventana perteneciente al hilo de ejecución, manejo un timeout interno porque sucede que si el equipo no es muy veloz, es posible que el proceso esté creado pero no la ventana.
Tienes razón. Aún en máquinas rápidas si la aplicación que se abre tarda mucho, como Word. No me había percatado porque hacía las pruebas por separado: en un botón mandaba abrir la aplicación y en otro botón la ponía como siempre visible.

Intente modificar GetMainWindow así

Código Delphi [-]
function GetMainWindow(ThreadId: Cardinal): HWnd;
begin
  Result := 0;

  while (Result = 0) do
    EnumThreadWindows(ThreadId, @DoEnum, Integer(Pointer(@Result)));
end;

pero no resulta. Aun cuando se obtiene el identificador de la ventana principal si la llamada a MakeAppTopMost es inmediata no siempre funciona.

Finalmente cambié ExecuteProcess:

Código Delphi [-]
function ExecuteApp(AppPath: String; TimeOut: Integer = 2000): Cardinal;
var
  StartInfo: TStartupInfo;
  ProcInfo: TProcessInformation;

begin
  FillChar(StartInfo, SizeOf(StartInfo), 0);
  StartInfo.cb := SizeOf(StartInfo);

  if not CreateProcess(
    PChar(AppPath), nil, nil, nil, false, 0, nil, nil, StartInfo, ProcInfo
  )
  then
    RaiseLastOSError;

  Sleep(TimeOut);

  Result := ProcInfo.dwThreadId;
end;

Así, por defecto esperará 2 segundos antes de proseguir pero se le puede cambiar el valor en la llamada.

// Saludos
Responder Con Cita
  #6  
Antiguo 06-07-2004
Avatar de delphi.com.ar
delphi.com.ar delphi.com.ar is offline
Federico Firenze
 
Registrado: May 2003
Ubicación: Buenos Aires, Argentina *
Posts: 5.874
Poder: 21
delphi.com.ar Va por buen camino
Yo había creado unas funciones mas genéricas, tu "GetMainWindow" es algo así:
Código Delphi [-]
function FindThreadWindow(dwThreadId: DWORD; hParent: HWND; lpClassName, lpWindowName: PAnsiChar; wTimeOut: Word = 30000): HWND;
var
  AFindWindow: TFindWindow;
  ATickCount: DWORD;
begin
  AFindWindow.lpClassName := lpClassName;
  AFindWindow.lpWindowName := lpWindowName;
  AFindWindow.hResult := 0;
  ATickCount := GetTickCount + wTimeOut;
  repeat
    if hParent = 0 Then
      EnumThreadWindows(dwThreadId, @EnumThreadWndProc, Integer(@AFindWindow))
    else
      EnumChildWindows(hParent, @EnumThreadWndProc, Integer(@AFindWindow));
  until (AFindWindow.hResult <> 0) or (ATickCount < GetTickCount);

  Result := AFindWindow.hResult;
end;
Me parece que es un poco mas real el concepto de TimeOut, en tu código es un tiempo de espera. (Solo una cuestión semántica )
Vale aclarar que usar GetTickCount no es del todo seguro para estos casos, ya que si el equipo estuvo encendido durante 49.7 días, este vuelve a cero. Un poco extraño, pero en uno de los proyectos en que estoy trabajando ha sucedido .
__________________
delphi.com.ar

Dedique el tiempo suficiente para formular su pregunta si pretende que alguien dedique su tiempo en contestarla.
Responder Con Cita
  #7  
Antiguo 06-07-2004
Avatar de roman
roman roman is offline
Moderador
 
Registrado: May 2003
Ubicación: Ciudad de México
Posts: 20.172
Poder: 10
roman Tiene un aura espectacularroman Tiene un aura espectacular
Bueno, pero, básicamente es mi while Result = 0 do (creo ). El caso es que probé esto y aún habiendo "encontrado" la ventana, la llamada a MakeAppTopMost no tenía efecto. Yo supongo que, como es normal en las aplicaciones Windows, luego de creada la ventana viene una llamada a SetWindowPos y si no ha pasado suficiente tiempo puede ser que ésta se haga después de la que nosotros hacemos.

// Saludos
Responder Con Cita
  #8  
Antiguo 06-07-2004
Avatar de roman
roman roman is offline
Moderador
 
Registrado: May 2003
Ubicación: Ciudad de México
Posts: 20.172
Poder: 10
roman Tiene un aura espectacularroman Tiene un aura espectacular
Cita:
Empezado por delphi.com.ar
ya que si el equipo estuvo encendido durante 49.7 días, este vuelve a cero. Un poco extraño, pero en uno de los proyectos en que estoy trabajando ha sucedido .
No tan extraño, de hecho está documentado y es que el valor de GetTickCount se guarda en una variable de 32 bits. En mi infinita ocisosidad ya hice las cuentas y, en efecto sólo caben 49.7 días.

Por cierto Federico, ¿podrías editar tu mensaje? Ya no cabe en el ancho de mi pantalla.

// Saludos
Responder Con Cita
  #9  
Antiguo 06-07-2004
Avatar de delphi.com.ar
delphi.com.ar delphi.com.ar is offline
Federico Firenze
 
Registrado: May 2003
Ubicación: Buenos Aires, Argentina *
Posts: 5.874
Poder: 21
delphi.com.ar Va por buen camino
Cita:
Empezado por roman
Bueno, pero, básicamente es mi while Result = 0 do (creo ).
Ahhh... y si el proceso ejecutado no es el correcto o una versión diferente??..

Para comparar, yo subí el ejemplo que tenía armado a mi página, no recuerdo para quien lo había hecho, me llama la atención que no lo haya puesto en el foro anteriormente.
El ejemplo hace otra cosa, pero creo que es muy aplicable a este caso.

Saludos!
__________________
delphi.com.ar

Dedique el tiempo suficiente para formular su pregunta si pretende que alguien dedique su tiempo en contestarla.
Responder Con Cita
  #10  
Antiguo 06-07-2004
Avatar de roman
roman roman is offline
Moderador
 
Registrado: May 2003
Ubicación: Ciudad de México
Posts: 20.172
Poder: 10
roman Tiene un aura espectacularroman Tiene un aura espectacular
Cita:
Empezado por delphi.com.ar
Ahhh... y si el proceso ejecutado no es el correcto o una versión diferente??
¿Cómo no va a ser el proceso correcto si yo lo mando llamar?

De cualquier forma, mi apunte del while Result = 0 do no iba encaminado a decir que estábamos haciendo lo mismo, sino, esencialmente lo mismo y que hasta donde veo persiste el problema de que las llamadas a otras funciones como MakeAppTopMost no sutan efecto.

¡Todo esto por el block de notas!
Responder Con Cita
  #11  
Antiguo 06-07-2004
Avatar de delphi.com.ar
delphi.com.ar delphi.com.ar is offline
Federico Firenze
 
Registrado: May 2003
Ubicación: Buenos Aires, Argentina *
Posts: 5.874
Poder: 21
delphi.com.ar Va por buen camino
Cita:
Empezado por roman
¿Cómo no va a ser el proceso correcto si yo lo mando llamar?
Nooo... supongamos que es una aplicación que distribuyes por la red, la pueden bajar usuarios con plataformas diferentes, con versiones y con "customizaciones" diferentes. Digamos que hasta pueden haber sobreescrito el Block de Notas, algo atípico pero estoy seguro de mas de un caso de esto.

¡¡Es para molestar un poco!!
__________________
delphi.com.ar

Dedique el tiempo suficiente para formular su pregunta si pretende que alguien dedique su tiempo en contestarla.
Responder Con Cita
  #12  
Antiguo 06-07-2004
Avatar de roman
roman roman is offline
Moderador
 
Registrado: May 2003
Ubicación: Ciudad de México
Posts: 20.172
Poder: 10
roman Tiene un aura espectacularroman Tiene un aura espectacular
Bueno, pero con tal paranoia mejor no enciendo mi PC.

Además estarás de acuerdo que podemos crear una aplicación que no sólo se llame notepad.exe sino que además la clase de su ventana principal se llame NOTEPAD.

Por otra parte, casi cualquier cosa que sustituya al note pad será mejor que tan infame aplicación

// Saludos
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 18:31:39.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi