Ir al contenido


Foto

Programa sin form (crear timer)


  • Por favor identifícate para responder
17 respuestas en este tema

#1 Caral

Caral

    Advanced Member

  • Moderador
  • PipPipPip
  • 4.266 mensajes
  • LocationCosta Rica

Escrito 03 diciembre 2009 - 08:29

Hola a todos
Estoy tratando de hacer mi primer programa sin form (nunca lo había hecho) y me tope con una de tantas dificultades, a ver si me dais un empujón.
Lo que quiero hacer es sencillo:
Tengo un programa (sin Form) que cierra una aplicación de windows.
Hasta ahí todo bien, el asunto es que quiero que use un timer para controlar el tiempo de cerrar dicho programa.
Normalmente con un form simplemente lo coloco y ya.
Si creara el componente tendría que ponerle un padre (creo) pero como no tengo form pues no puedo.

El código que tengo es sencillo:



delphi
  1. program Project1;
  2.  
  3. uses
  4.   windows,
  5.   Messages;
  6.  
  7. var
  8. Mango:integer;
  9. begin
  10. Mango:=FindWindow(nil,'Calculator');
  11. if mango=0
  12. then
  13. else PostMessage(FindWindow(Nil, Pchar('Calculator')), WM_QUIT, 0, 0);
  14.  
  15. end.



Si tuviese un form, colocaría un timer y lo haria asi:



delphi
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   Timer1.Enabled:= True;
  4. end;
  5.  
  6. procedure TForm1.Timer1Timer(Sender: TObject);
  7. var
  8. Mango:integer;
  9. begin
  10. Mango:=FindWindow(nil,'Calculator');
  11. if mango=0
  12. then
  13. else PostMessage(FindWindow(Nil, Pchar('Calculator')), WM_QUIT, 0, 0);
  14.  
  15. end;



Ahora la pregunta:
Como creo en el programa sin form el timer y le indico que si cumple con un determinado tiempo se ejecute la acción de cerrar la aplicación?.
Espero que me entiendan y ayuden amigos.
Gracias.
Saludos
  • 0

#2 Caral

Caral

    Advanced Member

  • Moderador
  • PipPipPip
  • 4.266 mensajes
  • LocationCosta Rica

Escrito 03 diciembre 2009 - 09:15

Hola
Otra cosa que se me ocurre es que se pueda definir si esta abierta o no la aplicación a cerrar, para que no genere ningún error.
Saludos
  • 0

#3 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 03 diciembre 2009 - 09:19

Hola amigo

A reserva de que los que saben de estos menesteres se pronuncien, debes de utilizar una aplicación de consola e incluir este código.



delphi
  1. program Project1;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. uses
  6.   SysUtils, windows, messages;
  7.  
  8. begin
  9.   PostMessage(FindWindow(Nil, Pchar('Calculator')), WM_QUIT, 0, 0);
  10. end.



Salud OS
  • 0

#4 axesys

axesys

    Advanced Member

  • Moderadores
  • PipPipPip
  • 640 mensajes
  • LocationLos Mochis

Escrito 03 diciembre 2009 - 10:07

No te sirve usar un repeat con un sleep() dentro y que se salga con un break algo así



delphi
  1. program Project1;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. uses
  6.   SysUtils, windows, messages;
  7. var
  8.   Count: integer;
  9. begin
  10.   Count:= 0;
  11.   repeat
  12.     if GetKeyState(VK_Escape) and 128 = 128 then
  13.       Break;
  14.     sleep(1);
  15.     Inc(Count);
  16.     if Count = 3000 then
  17.     begin
  18.       PostMessage(FindWindow(Nil, Pchar('Calculadora')), WM_QUIT, 0, 0);
  19.       Count:= 0;
  20.     end;
  21.   until false;
  22. end.




Saludos
  • 0

#5 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 04 diciembre 2009 - 07:54

Efectivamente un Timer no puede existir sin una ventana.

Tenemos la API SetTimer para poner en marcha el timer y KillTimer para pararlo.

Precisamos un Handle para que funcione. Cada ves que se cumpla el tiempo dado en el SetTimer, se envía un mensaje a la ventana del tipo WM_TIMER y aquí estará el evento correspondiente.

Tenemos dos opciones, o pones un Form oculto con Visible:= false o creamos una aplicación a bajo nivel con la API.

El primer caso es sencillo. El segundo es mas complicado. Básicamente tenemos que crear una ventana no visible y un bucle de mensajes de windows. el resto es el manejo del mensaje que nos interesa (WM_TIMER).

Dejo una aplicación funcional que mata la calculadora cada tres segundos.



delphi
  1. program Timer;
  2.  
  3. uses
  4.   Windows,
  5.   Messages;
  6.  
  7. procedure Funcion;
  8. var
  9.   Mango:integer;
  10. begin
  11.   Mango:=FindWindow(nil,'Calculadora');
  12.   if mango<>0 then
  13.     PostMessage(Mango, WM_QUIT, 0, 0);
  14. end;
  15.  
  16. function WndProc(hWnd, uMsg, wParam, lParam: Cardinal): Integer; stdcall;
  17. begin
  18. Result := 0;
  19. case uMsg of
  20.   WM_TIMER:    Funcion;           
  21.   WM_DESTROY: PostQuitMessage(0);
  22. else
  23.   Result := DefWindowProc(hWnd, uMsg, wParam, lParam);
  24. end;
  25. end;
  26.  
  27. var
  28. Msg: TMsg;
  29. hWnd: Cardinal;
  30. WndClass: TWndClass = (lpfnWndProc: @WndProc; lpszClassName: &#39;Timer&#39;);
  31. begin
  32. // Ventana principal. Podemos configurar los estilos iniciales...
  33. hWnd := CreateWindow(PChar(RegisterClass(WndClass)), &#39;Timer&#39;,
  34.         WS_VISIBLE or WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX,
  35.         0, 0, 0, 0, 0, 0, hInstance, nil);
  36. SetTimer(hWnd, 0, 3000, 0);  // Evento OnTimer cada 3000 milisegundos.
  37.  
  38. // Bucle de mensajes:
  39. repeat GetMessage(Msg, 0, 0, 0);
  40.   DispatchMessage(Msg);
  41. until Msg.Message = WM_QUIT;
  42.  
  43. end.



Saludos.
  • 0

#6 seoane

seoane

    Advanced Member

  • Administrador
  • 1.259 mensajes
  • LocationEspaña

Escrito 04 diciembre 2009 - 08:11

Es verdad que un timer no funciona sin un bucle de mensajes, pero no es necesario tener una ventana:


delphi
  1. program Ejemplo;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. uses Windows, Messages, SysUtils;
  6.  
  7. var
  8.   Terminar: Boolean;
  9.  
  10. procedure ProcessMessages;
  11. var
  12.   Msg: TMsg;
  13. begin
  14.   while PeekMessage(Msg,0,0,0,PM_REMOVE) do
  15.   begin
  16.     if Msg.Message = WM_QUIT then
  17.     begin
  18.       Terminar:= TRUE;
  19.       break;
  20.     end else
  21.     begin
  22.       TranslateMessage(Msg);
  23.       DispatchMessage(Msg);
  24.     end;
  25.   end;
  26. end;
  27.  
  28. function HandlerRoutine(dwCtrlType: DWORD): BOOL; stdcall;
  29. begin
  30.   Result:= TRUE;
  31.   case dwCtrlType of
  32.     CTRL_C_EVENT:
  33.       Terminar:= TRUE;
  34.     CTRL_CLOSE_EVENT:
  35.       Terminar:= TRUE;
  36.     CTRL_LOGOFF_EVENT:
  37.       Terminar:= TRUE;
  38.     CTRL_SHUTDOWN_EVENT:
  39.       Terminar:= TRUE;
  40.     else
  41.       Result:= FALSE;
  42.   end;
  43. end;
  44.  
  45. procedure TimerProc(Wnd: HWnd; Msg, TimerID, SysTime: Longint); stdcall;
  46. begin
  47.   Windows.Beep(500,100);
  48. end;
  49.  
  50. begin
  51.   Terminar:= FALSE;
  52.   if SetConsoleCtrlHandler(@HandlerRoutine,TRUE) then
  53.   begin
  54.     Writeln('Pulsa Ctrl+C para salir ...');
  55.     // Aqui iniciamos el Timer
  56.     SetTimer(0,0,1000,@TimerProc);
  57.     while not Terminar do
  58.     begin
  59.       // Aqui haz lo que quieras
  60.       ProcessMessages;
  61.       if not Terminar then
  62.         Sleep(10);
  63.     end;
  64.   end;
  65. end.


  • 0

#7 Caral

Caral

    Advanced Member

  • Moderador
  • PipPipPip
  • 4.266 mensajes
  • LocationCosta Rica

Escrito 04 diciembre 2009 - 09:50

Hola
Muchas gracias amigos.
El concepto mas o menos lo entiendo pero me quedan dudas:
En el caso de axesys creo que se pretende dormir el tiempo y hacer un bucle, me parece interesante.
En el caso de escafandra lo que entiendo es que se podría crear el efecto en un form y ocultarlo.
En el caso de seoane lo que entiendo es que se crea un timer sin form pero no entiendo como aplicarlo, no veo la parte en que cierra la aplicación.
Amigos acordaros de quien pregunta es El novato. :s
Si me podéis ampliar un poco el concepto os lo agradecería, no lo termino de captar y me gustaría aprender a hacerlo. (y)
Saludos

  • 0

#8 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 04 diciembre 2009 - 10:23

Una opción mas sencilla, para nosotros los novatos es usar un Form normal, colocas tu Timer con tu procedimiento y usar un TIconTray de la paleta adicional, con este componente no se mostrará el form, solo un ícono en la bandeja la lado del reloj:



delphi
  1. object TrayIcon: TTrayIcon
  2.     Animate = True
  3.     Hint = 'Lo que quieras que aparezca'
  4.     BalloonHint = 'lo que quieras que aparezca'
  5.     BalloonTitle = 'lo que quieras que aparezca'
  6.     BalloonFlags = bfInfo
  7.     PopupMenu = PopupMenu
  8.     Visible = True
  9.     OnDblClick = TrayIconDblClick
  10.     Left = 435
  11.     Top = 164
  12.   end



en el poppupmenu puedes colocar varios items para manejar el icono que aperecerá en la bandeja al lado del reloj, como por ejemplo:


delphi
  1. Form1.show;
  2. Form1.Hide;
  3. Form1.Close;


Adicionalmente podrías crear una entrada en el registro de windows para que el programa se cargue al iniciar windows.

Saludos.
  • 0

#9 Caral

Caral

    Advanced Member

  • Moderador
  • PipPipPip
  • 4.266 mensajes
  • LocationCosta Rica

Escrito 04 diciembre 2009 - 10:35

Hola
Gracias amigo Wilson
Pensé en la opción de colocarlo donde dices, pero lo que quiero es que no se vea en ningún lado, en tal caso en tareas no me importaría.
Por eso pensé en el programa sin form, ahí si que se esconde bien. :D, ademas que aprender a hacerlo no me caería mal. :D (y)
Saludos

  • 0

#10 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 04 diciembre 2009 - 10:39

Hola
Gracias amigo Wilson
Pensé en la opción de colocarlo donde dices, pero lo que quiero es que no se vea en ningún lado, en tal caso en tareas no me importaría.
Por eso pensé en el programa sin form, ahí si que se esconde bien. :D, ademas que aprender a hacerlo no me caería mal. :D (y)
Saludos

La opción que te propongo mostrará el icono que asignes al TryIcon en la baandeja de iconos(al lado del reloj).

Bastaría con poner a False la propeidad Visible del TryIcon y ni siquiera aprecerá allí, solo lo hará en la lista de procesos.

Saludos
  • 0

#11 Caral

Caral

    Advanced Member

  • Moderador
  • PipPipPip
  • 4.266 mensajes
  • LocationCosta Rica

Escrito 04 diciembre 2009 - 10:58

Hola
Mira por donde, no lo sabia, esta muy interesante, lo probare amigo. :D
Saludos
  • 0

#12 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 04 diciembre 2009 - 11:09

No estoy seguro si TTrayIcon existe en Delphi 7, aquí te adjunto un proyecto hecho en Delphi 7 en donde hago lo mismo que te propongo pero por código sin usar dicho componente.

Saludos

Archivos adjuntos


  • 0

#13 pcicom

pcicom

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 267 mensajes
  • LocationMéxico

Escrito 04 diciembre 2009 - 11:19

Lo puedes dejar tal y como te funciona..  solo debes de agregar a tu dpr Y esta manera no te mostrara el FORM





delphi
  1.   Application.ShowMainForm := False;




saludos..


  • 0

#14 Caral

Caral

    Advanced Member

  • Moderador
  • PipPipPip
  • 4.266 mensajes
  • LocationCosta Rica

Escrito 04 diciembre 2009 - 11:33

Hola
Muchas gracias por vuestros aportes.
Buen ejemplo amigo Wilson , solo se tendria que tratar de tapar el espacio que ocupa en blanco, pero muy interesante el concepto, gracias. (y)
Gracias amigo pcicom (y)
Como estoy aprendiendo. (y) (b)
Saludos
  • 0

#15 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 04 diciembre 2009 - 11:55

Es verdad que un timer no funciona sin un bucle de mensajes, pero no es necesario tener una ventana.


Pues seoane tiene toda la razón  :D. Si queremos programa de consola podemos prescindir del mensaje WM_TIMER. En el caso del ejemplo que puse puede quedar así:



delphi
  1. program Timer;
  2. {$APPTYPE CONSOLE}
  3.  
  4. uses
  5.   Windows,
  6.   Messages;
  7.  
  8. var
  9.   Terminar: Boolean;
  10.  
  11. procedure Funcion;
  12. var
  13.   Mango:integer;
  14. begin
  15.   Mango:=FindWindow(nil,'Calculadora');
  16.   if mango<>0 then
  17.     PostMessage(Mango, WM_QUIT, 0, 0);
  18. end;
  19.  
  20. function HandlerRoutine(dwCtrlType: DWORD): BOOL; stdcall;
  21. begin
  22.   Result:= FALSE;
  23.   if dwCtrlType = CTRL_C_EVENT then
  24.   begin
  25.     Terminar:= TRUE;
  26.     Result:= TRUE;
  27.   end;
  28. end;
  29.  
  30. var
  31. Msg: TMsg;
  32. begin
  33. Terminar:= FALSE;
  34. // Evento OnTimer cada 3000 milisegundos.
  35. SetTimer(0, 0, 2000, @Funcion);
  36. SetConsoleCtrlHandler(@HandlerRoutine, TRUE);
  37. // Bucle de mensajes:
  38. repeat GetMessage(Msg, 0, 0, 0);
  39.   DispatchMessage(Msg);
  40. until (Msg.Message = WM_QUIT) or (Terminar = TRUE);
  41.  
  42. end.



...Pensé en la opción de colocarlo donde dices, pero lo que quiero es que no se vea en ningún lado, en tal caso en tareas no me importaría.
Por eso pensé en el programa sin form, ahí si que se esconde bien. :D, ademas que aprender a hacerlo no me caería mal...


Claro que si que que queremos es que esté escondido entonces lo simplificamos así:


delphi
  1. program Timer;
  2.  
  3. uses
  4.   Windows, Messages;
  5.  
  6. procedure Funcion;
  7. var
  8.   Mango:integer;
  9. begin
  10.   Mango:=FindWindow(nil,'Calculadora');
  11.   if mango<>0 then
  12.     PostMessage(Mango, WM_QUIT, 0, 0);
  13. end;
  14.  
  15. var
  16. Msg: TMsg;
  17. begin
  18. // Evento OnTimer cada 3000 milisegundos.
  19. SetTimer(0, 0, 2000, @Funcion);
  20. // Bucle de mensajes:
  21. repeat GetMessage(Msg, 0, 0, 0);
  22.   DispatchMessage(Msg);
  23. until Msg.Message = WM_QUIT;
  24.  
  25. end.



Aunque de esta forma lo difícil será que termine.  :p 
Pero nada es imposible...


Saludos.
  • 0

#16 Caral

Caral

    Advanced Member

  • Moderador
  • PipPipPip
  • 4.266 mensajes
  • LocationCosta Rica

Escrito 04 diciembre 2009 - 12:37

Hola
Voy entendiendo.
Ahora que pasa si la aplicación esta ejecutándose pero esta al lado del reloj?
Como haría para detener el aplicación si esta iconizada?.
Por ejemplo el control de volumen, este se inicia con windows y solo se ve el icono al lado del reloj, como hago para que se cierre, que no corra, detener el proceso?
No se si me explico.
Saludos
  • 0

#17 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 04 diciembre 2009 - 02:32

Hola
Voy entendiendo.
Ahora que pasa si la aplicación esta ejecutándose pero esta al lado del reloj?
Como haría para detener el aplicación si esta iconizada?.
Por ejemplo el control de volumen, este se inicia con windows y solo se ve el icono al lado del reloj, como hago para que se cierre, que no corra, detener el proceso?
No se si me explico.
Saludos


En el ejemplo que te envié basta con hacer clic derecho sobre el icono y aparecerá la opción cerrar que se configuró en el popup menu.

Saludos  pd: Code del programa que adjunté:



delphi
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs, Menus, ExtCtrls,shellapi;
  8. const
  9.   WM_ICONMSG = WM_USER + 2000;
  10. type
  11.   TForm1 = class(TForm)
  12.     Image1: TImage;
  13.     PopupMenu1: TPopupMenu;
  14.     Cerrar1: TMenuItem;
  15.     Timer1: TTimer;
  16.     procedure FormCreate(Sender: TObject);
  17.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  18.     procedure Cerrar1Click(Sender: TObject);
  19.     procedure Timer1Timer(Sender: TObject);
  20.   private
  21.     procedure WMIconMessage(var Msg: TMessage);
  22.         message WM_ICONMSG;
  23.  
  24.     { Private declarations }
  25.   public
  26.   procedure NotifyIcon(Value: Word; const Tip: string);
  27.     { Public declarations }
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.  
  33. implementation
  34.  
  35. {$R *.dfm}
  36.  
  37. procedure TForm1.NotifyIcon(Value: Word; const Tip: string);
  38. var
  39.   Data: TNotifyIconData;
  40. begin
  41.   Data.cbSize := SizeOf(Data);
  42.   Data.uId := 0;
  43.   Data.uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
  44.   Data.Wnd := Self.Handle;
  45.   Data.uCallbackMessage := WM_ICONMSG;
  46.   Data.hIcon := Image1.Picture.Icon.Handle;
  47.   StrPLCopy(Data.szTip, Tip, SizeOf(Data.szTip));
  48.   Shell_NotifyIcon(Value, @Data);
  49. end;
  50.  
  51. procedure TForm1.FormCreate(Sender: TObject);
  52. begin
  53. Application.ShowMainForm := False;
  54. ShowWindow(Application.Handle, SW_HIDE);
  55. NotifyIcon(NIM_ADD, Application.Title);
  56. end;
  57.  
  58. procedure TForm1.WMIconMessage(var Msg: TMessage);
  59. var
  60.   Pt: TPoint;
  61. begin
  62.   if Msg.LParam = WM_RBUTTONUP then
  63.   begin
  64.       GetCursorPos(Pt);
  65.       SetForegroundWindow(Application.Handle);
  66.       Application.ProcessMessages;
  67.       PopupMenu1.Popup(Pt.X, Pt.Y);
  68.   end;
  69. end;
  70.  
  71. procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
  72. begin
  73. NotifyIcon(NIM_DELETE, '');
  74. end;
  75.  
  76. procedure TForm1.Cerrar1Click(Sender: TObject);
  77. begin
  78. Close
  79. end;
  80. procedure TForm1.Timer1Timer(Sender: TObject);
  81. begin
  82. //lo que quieras hacer
  83. end;
  84.  
  85. end.


  • 0

#18 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 05 diciembre 2009 - 10:59

Tenemos dos opciones, o pones un Form oculto con Visible:= false o creamos una aplicación a bajo nivel con la API.


A pesar de que la opción mas aceptada es la primera, he seguido jugando un poco con la segunda, adaptándole un HOOK al teclado para poder terminar el proceso con ALT + F1.

Me he encontrado con que mi Antivirus ha protestado mucho mientras ensayaba y tuve que adaptar un poco el código. Parece que a mi AV no le gusta mucho la mezcla de delphi, API a bajo nivel y Hooks.

Como delphi no es mi lengua nativa, perdonar si no soy muy ortodoxo, hago lo que puedo. Bueno el resultado es este:


delphi
  1. program Timer;
  2.  
  3. uses
  4.   Windows,
  5.   Messages;
  6.  
  7. type
  8.   PUnhookWindowsHookEx = function(hhk: HHOOK): BOOL; stdcall;
  9.  
  10. var
  11.   WHookKeyboard: HHOOK = 0;
  12.   Terminate: BOOL = FALSE;
  13.   Unhook: PUnhookWindowsHookEx;
  14.  
  15. procedure Funcion;
  16. var
  17.   Mango:integer;
  18. begin
  19.   Mango:=FindWindow(nil,'Calculadora');
  20.   if mango<>0 then PostMessage(Mango, WM_QUIT, 0, 0);
  21. end;
  22.  
  23.  
  24. function KeyboardHook(Code, wParam, lParam: Integer): Integer; stdcall;
  25. begin
  26.   // Aquí el AV protesta según lo que pongamos como GetAsyncKeyState
  27.   if (Code = HC_ACTION) and LongBool(PDWORD(lParam + 8)^ and $20{LLKHF_ALTDOWN}) and (PDWORD(lParam)^ = VK_F1) then
  28.       Terminate:= TRUE;
  29.   Result:= CallNextHookEx(WHookKeyboard, Code, wParam, lParam);
  30. end;
  31.  
  32. var
  33. Msg: TMsg;
  34. begin
  35. // Hook al teclado: termina con ALT + F1
  36. // Si no importo así la función UnhookWindowsHookEx, el AV protesta :s
  37. Unhook:= GetProcAddress(GetModuleHandle('user32.dll'), 'UnhookWindowsHookEx');
  38. WHookKeyboard:= SetWindowsHookEx(13{WH_KEYBOARD_LL}, @KeyboardHook, HInstance, 0);
  39.  
  40. // Evento OnTimer cada 2000 milisegundos.
  41. SetTimer(0, 0, 2000, @Funcion);
  42.  
  43. // Bucle de mensajes:
  44. repeat GetMessage(Msg, 0, 0, 0);
  45.   DispatchMessage(Msg);
  46. until (Msg.Message = WM_QUIT) or (Terminate = TRUE);
  47.  
  48. Unhook(WHookKeyboard);
  49. end.



Os lo subo con fuente y compilado.

Saludos.
  • 0




IP.Board spam blocked by CleanTalk.