FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour Creando Servicios con Harbour y xHarbour
Posts: 1818
Joined: Wed Oct 26, 2005 02:49 PM
Re: Creando Servicios con Harbour y xHarbour
Posted: Thu May 29, 2025 09:41 PM
Antonio buenas tardes como estas?

Que pena molestarte, pero es que necesitamos solucionar el tema de los servicios, hay que crear un servicio que haga una tarea especifica sobre la base de datos, compilamos este ejemplo y funciona, pero en el ejemplo, la tarea del servicio es escribir la hora en un archivo de texto cada 1 segundo.

El problema que se nos presenta es que en todas las ocasiones, el tiempo de ejecución de la consulta toma mas de un segundo, un tiempo indeterminado, que no conocemos, la idea es poder pausar el servicio mientras ejecutamos la tarea y luego si reiniciar la tarea.

No se si se pueda pausar el sleep o el servicio, o si podemos agregar un TIMER, que sabemos que si se puede pausar.
    if (InitService() == 0)
    {
        ServiceStatus.dwCurrentState = SERVICE_RUNNING;
        SetServiceStatus(hStatus, &ServiceStatus);

        // Aquí va el bucle principal del servicio
        while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
        {
            // Realiza las tareas del servicio
            hb_vmPushSymbol( hb_dynsymSymbol( hb_dynsymFindName( "TRABAJOSERVICIO" ) ) );
            hb_vmPushNil();
            hb_vmDo( 0 );

            Sleep(1000); //COMO PAUSAMOS ESTE SLEEP?
        }
    }
El código de compilamos
// Once you build the EXE open a CMD window as Administrator, then:
// sc create servicio binPath=C:\fwh64_2501\samples\servicio.exe
// sc start servicio
// when you want to stop it: sc stop servicio
// when you want to remove it: sc delete servicio
// DON'T USE ANY GUI FROM IT !!!

#include "FiveWin.ch"

function Main()

   Servicio()

return nil

function TrabajoServicio()

   if ! File( "C:\fwh64_2501\samples\servicio.txt" )
      memoWrit( "C:\fwh64_2501\samples\servicio.txt", Time() )    
   else  
      memoWrit( "C:\fwh64_2501\samples\servicio.txt", memoRead( "C:\fwh64_2501\samples\servicio.txt" ) + CRLF + Time() )    
   endif  

return nil    

#pragma BEGINDUMP

#include <windows.h>
#include <tchar.h>
#include <hbvm.h>

SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;

void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
int InitService( void );

HB_FUNC( SERVICIO )
{
    SERVICE_TABLE_ENTRY ServiceTable[2];
    ServiceTable[0].lpServiceName = _T("Servicio");
    ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
    ServiceTable[1].lpServiceName = NULL;
    ServiceTable[1].lpServiceProc = NULL;

    StartServiceCtrlDispatcher(ServiceTable);
}

#pragma warn -8057

void ServiceMain(int argc, char** argv )
{
    ServiceStatus.dwServiceType        = SERVICE_WIN32;
    ServiceStatus.dwCurrentState       = SERVICE_START_PENDING;
    ServiceStatus.dwControlsAccepted   = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
    ServiceStatus.dwWin32ExitCode      = 0;
    ServiceStatus.dwServiceSpecificExitCode = 0;
    ServiceStatus.dwCheckPoint         = 0;
    ServiceStatus.dwWaitHint           = 0;

    hStatus = RegisterServiceCtrlHandler(_T("Servicio"), (LPHANDLER_FUNCTION)ControlHandler);

    if (InitService() == 0)
    {
        ServiceStatus.dwCurrentState = SERVICE_RUNNING;
        SetServiceStatus(hStatus, &ServiceStatus);

        // Aquí va el bucle principal del servicio
        while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
        {
            // Realiza las tareas del servicio
            hb_vmPushSymbol( hb_dynsymSymbol( hb_dynsymFindName( "TRABAJOSERVICIO" ) ) );
            hb_vmPushNil();
            hb_vmDo( 0 );

            Sleep(1000); //COMO PAUSAMOS ESTE SLEEP
        }
    }

    ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    SetServiceStatus(hStatus, &ServiceStatus);
}

void ControlHandler(DWORD request)
{
    switch(request)
    {
        case SERVICE_CONTROL_STOP:
            ServiceStatus.dwWin32ExitCode = 0;
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            SetServiceStatus(hStatus, &ServiceStatus);
            MessageBox( 0, "service stopped", "ok", 0 );
            return;
        case SERVICE_CONTROL_SHUTDOWN:
            ServiceStatus.dwWin32ExitCode = 0;
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            SetServiceStatus(hStatus, &ServiceStatus);
            return;
        default:
            break;
    }
    SetServiceStatus(hStatus, &ServiceStatus);
}

int InitService()
{
    // Inicialización del servicio
    return 0;
}

#pragma ENDDUMP
Gracias de antemano.
Saludos
LEANDRO AREVALO
Bogotá (Colombia)
https://hymlyma.com
https://hymplus.com/
leandroalfonso111@gmail.com
leandroalfonso111@hotmail.com

[ Turbo Incremental Link64 6.98 Embarcadero 7.70 ] [ FiveWin 25.01 ] [ xHarbour 64 bits) ]
Posts: 6755
Joined: Wed Feb 15, 2012 08:25 PM
Re: Creando Servicios con Harbour y xHarbour
Posted: Fri May 30, 2025 06:14 AM
Al decir "pausar el servicio", no sería lo mismo que "no se ejecutase" la funcion que se invoca desde el mismo?
Si es asi, create una variable estatica ( supongamos nExecService ) a modo de semaforo ( valores 0 o 1 ) en el modulo de "C", y una function que te permita modificar el valor
Por lo que tu function quedaría:
int nExecService = 1;
HB_FUNC( SETEXECSERVICE )
{
    nExecService = hb_parni( 1 );
}

...
while (ServiceStatus.dwCurrentState == SERVICE_RUNNING && nExecService )
Entonces podrías pausar o reanudar la ejecucion de la accion seteando esa variable desde tu programa a 0 o a 1
El codigo que te he puesto lo estoy haciendo "al vuelo", prueba a ver.
Cristobal Navarro

Hay dos tipos de personas: las que te hacen perder el tiempo y las que te hacen perder la noción del tiempo

El secreto de la felicidad no está en hacer lo que te gusta, sino en que te guste lo que haces
Posts: 6984
Joined: Fri Oct 07, 2005 07:07 PM
Re: Creando Servicios con Harbour y xHarbour
Posted: Fri May 30, 2025 06:35 AM
Sleep(1000); //COMO PAUSAMOS ESTE SLEEP?


Hello Leandro,
since this topic also fits well with EasyReport() and microservices using FiveWin, I’ve been looking into it too:

I’ve always wanted to comment on `Sleep()`.
Calling `Sleep(x)` blocks the entire thread — and with it, the Harbour VM. During this waiting period, no further processing takes place. This includes timers, event handling, or any parallel logic in Harbour code. For services that should run regularly or respond to external triggers, an event-driven or thread-based model may be more suitable.

Instead of a rigid `Sleep()` call every second, the processing could be triggered via an event handle, external signal, or system timer. This allows more precise control and reduces idle CPU cycles.

I’ve been reading about how to implement such an event-driven model with `WaitForSingleObject` or a custom event handle — essentially, you wait for work to arrive instead of sleeping idly.

Best regards,
Otto


🧱 Teil 1: Dienstprogramm (Service)
Das ist das Programm mit SERVICIO() und WaitForSingleObject(...), welches du:

einmalig kompilierst (als .exe)

dann mit z. B.:

sc create ServicioEvent binPath="C:\pfad\zum\dienst.exe"
sc start ServicioEvent
als echten Windows-Dienst installierst

👉 Dieses Programm wartet auf ein benanntes Windows-Event, und wenn es ausgelöst wird, führt es TRABAJOSERVICIO() aus.

🧪 Teil 2: Event-Trigger-Tool (Harbour-Programm)
Das ist ein einfaches .prg, das du:

ebenfalls als .exe kompilierst (oder direkt aus dem GUI, z. B. via Button aufrufst)

Es ruft TriggerService() auf, öffnet das Event Global\\ServicioEventTrigger und sendet ein Signal via SetEvent()

👉 Dieses Programm simuliert die Eingabe: „Mach was!“ – an den wartenden Dienst.

🧠 Warum diese Trennung?
Weil das zwei völlig unterschiedliche Rollen sind:

Rolle Dienstprogramm Event-Trigger (Client)
Startart Als Windows-Dienst (sc create) Normale .EXE / GUI / Kommandozeile
Aufgabe Wartet im Hintergrund auf Arbeit Löst Verarbeitung aus
Steuerung Reagiert auf SetEvent() Ruft SetEvent() auf
Ausführung ruft TRABAJOSERVICIO() auf gibt nur das Startsignal
-----------------------------------------------
[🤖 AI Insight]
-----------------------------------------------

### You can do this in Harbour/FiveWin via C (`BEGINDUMP`) and low-level Windows APIs.

Harbour doesn’t offer high-level abstractions here, but using `CreateEvent` and `WaitForSingleObject` lets you implement clean event-based service logic — especially useful for services or background tasks.

What you can do with `CreateEvent` and `WaitForSingleObject`:

* Create an event handle (`CreateEvent`)
* Put your service in a wait loop using `WaitForSingleObject`
* Only execute processing when the event is triggered
* Or use a timeout: `WaitForSingleObject(hEvent, 1000)` → max. 1 sec wait

This avoids hardcoded `Sleep()` calls and gives you full control.

--
-

### 🧱 Sample structure (C / BEGINDUMP):

```c
HANDLE hEvent;

int InitService()
{
    hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset, not signaled
    return 0;
}

void ServiceMain(int argc, char** argv)
{
    InitService();

    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(hStatus, &ServiceStatus);

    while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
    {
        DWORD result = WaitForSingleObject(hEvent, 1000);

        if (result == WAIT_OBJECT_0)
        {
            // Event triggered → run task
            hb_vmPushSymbol(hb_dynsymSymbol(hb_dynsymFindName("TRABAJOSERVICIO")));
            hb_vmPushNil();
            hb_vmDo(0);
        }
        else if (result == WAIT_TIMEOUT)
        {
            // Timeout passed, can check regularly for other conditions
        }
    }

    CloseHandle(hEvent);
}
```
🔁 **How do you trigger the event?**
From another thread, pipe, file watcher, or an external tool (e.g., `SetEvent()` from DLL or other process).

🧠 **Advantages:**

* No blocking `Sleep()`
* Service reacts immediately when work arrives
* More efficient than busy-loops

If you want, I can build you a full working example using `CreateEvent`, `WaitForSingleObject`, and a Harbour trigger (e.g., via button or command).

---
## ✅ Full Example Setup

### 🧩 Goal:

A service waits **non-blockingly** for an event and runs `TRABAJOSERVICIO()` once it’s triggered.

---

### 🔧 1. **Service foundation using `CreateEvent()` + `WaitForSingleObject()`**

(*C code in `BEGINDUMP` — see original*)

---

### 🛠 2. **Trigger function in Harbour**

You can call this from a button, menu action, or timer:

```harbour
function TriggerService()

   LOCAL hEvent := OpenEvent( 0x0010 /* EVENT_MODIFY_STATE */, .F., "Global\\ServicioEventTrigger" )

   if hEvent != 0
      ? "Sending event to service..."
      DllCall( "kernel32.dll", DLL_STDCALL, "SetEvent", hEvent )
      DllCall( "kernel32.dll", DLL_STDCALL, "CloseHandle", hEvent )
   else
      ? "Could not open event!"
   endif

return nil
```
Advantages:

* No `Sleep()`, no unnecessary CPU usage
* Service reacts **only** when work is available
* Supports targeted triggering from outside

-
--

## Bonus: `StopUntil()` with `SetTimer()` in GUI

Here's a complete FiveWin GUI example using `StopUntil()` and `SetTimer()` — no `Sleep()` needed:

```harbour
#include "FiveWin.ch"

static lTimerDone := .f.

function Main()
   local oWnd

   DEFINE WINDOW oWnd TITLE "Timer Wait Example"

   @ 2, 2 BUTTON "Wait 3 Seconds" ACTION Wait3Seconds( oWnd )

   ACTIVATE WINDOW oWnd

return nil

function Wait3Seconds( oWnd )
   local nTimerID

   lTimerDone := .f.
   nTimerID := SetTimer( oWnd:hWnd, 1, 3000, NIL )
   StopUntil( { || lTimerDone } )
   MsgInfo( "3 seconds passed!" )
   KillTimer( oWnd:hWnd, nTimerID )

return nil

function StopUntil( bBlock )
   do while !Eval( bBlock )
      WaitMessage()
      SysRefresh()
   enddo
return nil

function AppEvent( nMsg, nWParam, nLParam )
   if nMsg == WM_TIMER
      lTimerDone := .t.
   endif
return NIL
```
Works great in GUI apps — keeps the UI responsive, no CPU waste.

---

## 🔎 Excursion: `msgwait()`

FiveWin uses the combination of `MsgWait()`, `StopUntil()`, and `WaitMessage()` — a classic Windows message wait loop. It’s an elegant way to avoid blocking while still responding regularly to user actions or events.

🔹 `WaitMessage()`
This Windows API suspends the thread until a message arrives (key press, mouse click, timer, etc.).
It blocks efficiently, keeps the message loop alive:

* Windows remain responsive
* Timers, input, and events still processed
* Ideal for dialogs, progress windows, etc.

🔹 `StopUntil(bBlock)`
An active wait loop that relies on `WaitMessage()` and `SysRefresh()`:

```harbour
DO WHILE !Eval( bBlock )
WaitMessage()
SysRefresh()
ENDDO
```

Ideal for:

* GUI applications that need to wait without freezing
🚫 Not suitable for services without a message queue

🔹 `MsgWait()` and `MsgRun()`
User-friendly helpers — show a modal “please wait…” dialog and run a task (`bAction`) while showing progress.

```harbour
MsgWait( "Loading data...", "Please wait", 3 )
```

Internally uses:

```harbour
MsgRun( ..., { || WaitSeconds( 3 ) } )
```

So it’s essentially a tiny wait loop — with GUI.
Posts: 1818
Joined: Wed Oct 26, 2005 02:49 PM
Re: Creando Servicios con Harbour y xHarbour
Posted: Fri May 30, 2025 02:46 PM

Amigos, gracias por las respuestas.

Lo que dice Otto es verdaderamente lo que necesitamos, lo que pasa es que no sabemos como hacerlo, y el código que se publica ahí, no esta dentro de mi conocimiento.

Lo que necesitamos hacer es algo similar al funcionamiento de una API, que esta esperando a que se cumpla una condición o se genere un evento y esto desencadena una acción, eso lo sabemos hacer con PHP y APACHE, pero desconocemos totalmente como hacerlo en FW, xharbour y windows, pensamos que la solución son los servicios, por que asumimos que era algo que esta siempre escuchando y esperando que se cumpla el evento o la acción, pero según el ejemplo que publica Antonio, este ejecuta una acción cada segundo pero sin detenerse.

Estuve leyendo también algo sobre los socktes y compile algunos ejemplos, pero sigo sin entender el funcionamiento y si seria la solución a nuestro requerimiento, también algo sobre los hilos, que quizás podamos combinar para hacer la tarea que requerimos.

Alguien sabe en donde podemos encontrar documentación referente a este tema? o pueda explicarnos, les agradeceríamos, aun si tiene costo.

Gracias de antemano

PDTA. La tarea debe ejecutarse sin UI.

Saludos
LEANDRO AREVALO
Bogotá (Colombia)
https://hymlyma.com
https://hymplus.com/
leandroalfonso111@gmail.com
leandroalfonso111@hotmail.com

[ Turbo Incremental Link64 6.98 Embarcadero 7.70 ] [ FiveWin 25.01 ] [ xHarbour 64 bits) ]
Posts: 6984
Joined: Fri Oct 07, 2005 07:07 PM
Re: Creando Servicios con Harbour y xHarbour
Posted: Fri May 30, 2025 05:47 PM

Leandro,

maybe you can post your PHP code that works for you. That way, we can better understand what exactly you want to do.

Best regards,

Otto

Posts: 1067
Joined: Wed Nov 09, 2005 02:17 AM
Re: Creando Servicios con Harbour y xHarbour
Posted: Mon Jun 02, 2025 06:42 PM
Dear Antonio,

When I try to compile this code, I'm having these Warnings bellow. What am I missing?
Compiling...
Harbour 3.2.0dev (r2008190002)
Copyright (c) 1999-2020, https://harbour.github.io/
Compiling 'tarif20.prg' and generating preprocessed output to 'tarif20.ppo'...
Lines 5450, Functions/Procedures 13
Generating C source output to 'tarif20.c'... Done.
Embarcadero C++ 7.30 for Win32 Copyright (c) 1993-2017 Embarcadero Technologies, Inc.
tarif20.c:
Warning W8065 tarif20.prg 398: Call to function 'InitService' with no prototype in function ServiceMain
Warning W8065 tarif20.prg 407: Call to function 'hb_dynsymFindName' with no prototype in function ServiceMain
Warning W8065 tarif20.prg 407: Call to function 'hb_dynsymSymbol' with no prototype in function ServiceMain
Warning W8065 tarif20.prg 407: Call to function 'hb_vmPushSymbol' with no prototype in function ServiceMain
Warning W8065 tarif20.prg 408: Call to function 'hb_vmPushNil' with no prototype in function ServiceMain
Warning W8065 tarif20.prg 409: Call to function 'hb_vmDo' with no prototype in function ServiceMain
Turbo Incremental Link 6.80 Copyright (c) 1997-2017 Embarcadero Technologies, Inc.
* Application successfully built *
Sds,
Vilian F. Arraes
vilian@vfatec.com.br
Belém-Pa-Brazil
Posts: 1067
Joined: Wed Nov 09, 2005 02:17 AM
Re: Creando Servicios con Harbour y xHarbour
Posted: Mon Jun 02, 2025 07:05 PM
After read your messages, I replaced my buildh.bat and now i have only one warning:
Compiling...
Harbour 3.2.0dev (r2008190002)
Copyright (c) 1999-2020, https://harbour.github.io/
Compiling 'tarif20.prg' and generating preprocessed output to 'tarif20.ppo'...
Lines 5450, Functions/Procedures 13
Generating C source output to 'tarif20.c'... Done.
Embarcadero C++ 7.30 for Win32 Copyright (c) 1993-2017 Embarcadero Technologies, Inc.
tarif20.c:
Warning W8065 tarif20.prg 398: Call to function 'InitService' with no prototype in function ServiceMain
Turbo Incremental Link 6.80 Copyright (c) 1997-2017 Embarcadero Technologies, Inc.
* Application successfully built *
Sds,
Vilian F. Arraes
vilian@vfatec.com.br
Belém-Pa-Brazil
Posts: 1067
Joined: Wed Nov 09, 2005 02:17 AM
Re: Creando Servicios con Harbour y xHarbour
Posted: Tue Jun 03, 2025 06:14 PM

??

Sds,
Vilian F. Arraes
vilian@vfatec.com.br
Belém-Pa-Brazil
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM
Re: Creando Servicios con Harbour y xHarbour
Posted: Wed Jun 04, 2025 04:23 AM

int InitService( void );

Place this declaration in your code before calling it

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 1067
Joined: Wed Nov 09, 2005 02:17 AM
Re: Creando Servicios con Harbour y xHarbour
Posted: Wed Jun 04, 2025 11:27 AM

Thank you, the warning was solved.

Sds,
Vilian F. Arraes
vilian@vfatec.com.br
Belém-Pa-Brazil
Posts: 1067
Joined: Wed Nov 09, 2005 02:17 AM
Re: Creando Servicios con Harbour y xHarbour
Posted: Wed Jun 04, 2025 11:51 AM
Dear Antonio,
I change in your code this:
        // Aquí va el bucle principal del servicio
        while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
        {
            // Realiza las tareas del servicio
            hb_vmPushSymbol( hb_dynsymSymbol( hb_dynsymFindName( "ExecutaServicos" ) ) );
            hb_vmPushNil();
            hb_vmDo( 0 );
            Sleep(1000);
        }
The code for function ExecutaServicos Is:
function ExecutaServicos()
LOCAL cDirBack,cFilBack

   FWLOG TO ("c:\vfatec\sgvfa\tarbd.log") "ENVIOU ZAP"
   
RETURN nil
I can create and start the service.
C:\vfatec>sc create vfatec binpath=c:\vfatec\sgvfa\tarif20.exe
[SC] CreateService ÊXITO

C:\vfatec>sc start vfatec

NOME_DO_SERVIÇO: vfatec
    TIPO                       : 10  WIN32_OWN_PROCESS
    ESTADO                     : 4  RUNNING
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
    CÓDIGO_DE_SAÍDA_DO_WIN32   : 0  (0x0)
    CÓDIGO_DE_SAÍDA_DO_SERVIÇO : 0  (0x0)
    PONTO_DE_VERIFICAÇÃO       : 0x0
    AGUARDAR_DICA              : 0x0
    PID                        : 10888
    SINALIZADORES              :
But nothing happened. What am i doing wrong?
Sds,
Vilian F. Arraes
vilian@vfatec.com.br
Belém-Pa-Brazil
Posts: 1067
Joined: Wed Nov 09, 2005 02:17 AM
Re: Creando Servicios con Harbour y xHarbour
Posted: Mon Jun 23, 2025 11:54 AM

??

Sds,
Vilian F. Arraes
vilian@vfatec.com.br
Belém-Pa-Brazil
Posts: 231
Joined: Fri Jul 20, 2012 01:49 AM
Re: Creando Servicios con Harbour y xHarbour
Posted: Thu Jun 26, 2025 03:14 PM
Try to replace it:
  hb_vmPushSymbol( hb_dynsymSymbol( hb_dynsymFindName( "ExecutaServicos" ) ) );
with
  hb_vmPushSymbol( hb_dynsymSymbol( hb_dynsymFindName( "EXECUTASERVICOS" ) ) );

and also add to your prg
REQUEST EXECUTASERVICOS
Then try it again.
Regards,

Lailton Fernando Mariano
Posts: 1067
Joined: Wed Nov 09, 2005 02:17 AM
Re: Creando Servicios con Harbour y xHarbour
Posted: Thu Jun 26, 2025 05:20 PM

Thank you Lailton, but it still not working.

The service is created, but it's always shown as "stopped".

Sds,
Vilian F. Arraes
vilian@vfatec.com.br
Belém-Pa-Brazil
Posts: 231
Joined: Fri Jul 20, 2012 01:49 AM
Re: Creando Servicios con Harbour y xHarbour
Posted: Thu Jun 26, 2025 07:47 PM

Run the command:

Services

and in the list check if the service is installed, then you can manualy start it.

Remember that the block code service should be inside WHILE.

Regards,

Lailton Fernando Mariano