Referencia API
Referencia completa de FoxCore.Runtime 3.4.2: los seis tipos COM, sus miembros y qué devuelve cada uno cuando algo va mal.
Arrancar el motor
Lo mínimo que funciona: crear, activar la licencia, engancharse al hilo, lanzar una tarea, bombear mientras corre y parar.
LOCAL loRt, lcId
loRt = CREATEOBJECT("FoxCore.Runtime")
? loRt.Activate(lcJwt) && .T. -- tu licencia, como cadena
loRt.Attach(_VFP) && engancha el runtime A ESTE hilo
loRt.MaxWorkers = 8
lcId = loRt.RunScript("RETURN 6 * 42", "la primera") && el guion corre en OTRO proceso VFP
DO WHILE !loRt.IsCompleted(lcId)
loRt.Pump() && Pump() atiende a los workers; sin el, se quedan esperando
DOEVENTS
ENDDO
? loRt.GetState(lcId) && Completed
? loRt.GetResult(lcId) && 252
? loRt.GetError(lcId) && ""
loRt.Shutdown() && SIEMPRE: es lo que mata a los workersEl JWT se pasa por código: no hay ningún fichero que copiar. Cómo se consigue y qué pasa si falta está en Licencias.
Los seis tipos
Tres clases y sus tres interfaces. No hay un séptimo tipo, y no lo habrá: dos veces pareció hacer falta uno (el resultado de TryCall y luego GetStats), y las dos veces la respuesta fue una cadena JSON.
| Clase | Interfaz | ProgId | Qué es |
|---|---|---|---|
FoxRuntime | IFoxRuntime | FoxCore.Runtime | El objeto que crea el host. Es el motor: licencia, opciones, tareas, bombeo, eventos y handlers. |
FoxTaskHandle | IFoxTask | — | El handle que devuelve NewTask(). Azúcar: no guarda estado, cada propiedad pregunta al registro. |
WorkerBridge | IFoxWorker | — | La variable runtime DENTRO del guion. Es lo único que el worker ve del proceso principal. |
FoxCore.Runtime
Ciclo de vida
| Miembro | Devuelve | Notas |
|---|---|---|
Activate(cLicenseKey) | L | Valida el JWT. NUNCA lanza por licencia inválida: devuelve .F. y lo que se bloquea son las tareas, no el ciclo de vida. |
Attach(oHost, nVersion, cRuntimeMode) | — | Engancha el runtime AL HILO desde el que se llama. Normalmente loRt.Attach(_VFP). Los dos ultimos son opcionales. |
Shutdown() | — | Idempotente. ES LO QUE MATA A LOS WORKERS. Llamarlo siempre. |
Ping() | C | "FoxCore.Runtime 3.4.2". Funciona en cualquier estado, incluso tras Shutdown(). |
Version | C | Solo lectura. |
State | C | Created | Activated | Attached | ShuttingDown | ShutDown. CADENA, no enum: ningún valor numérico cruza COM. |
IsRunning | L | .T. entre Attach() y Shutdown(). |
IsLicensed | L | .T. si el JWT vale. Desde 3.4.0 NO hay excepción por modo: fuera del IDE también hace falta. |
Opciones
Todas de lectura y escritura. Se ponen después de crear el objeto y antes de lanzar tareas.
| Propiedad | Por defecto | Qué controla |
|---|---|---|
MaxWorkers | min(nucleos, 8) | Procesos worker simultáneos. Rango 1-64. Regla practica: núcleos físicos. |
DefaultTimeoutSeconds | 30 | Plazo de cada tarea. Rango 1-86.400. Al vencer, la tarea acaba TimedOut y el worker muere. |
WorkerProgId | "VisualFoxpro.Application.9" | Que proceso hace de worker: VFP 9, VFP Advanced o tu propio EXE registrado. Se valida el FORMATO, no la existencia: para saber si sirve, ProbeWorker(). |
KeepCompletedTasks | 200 | Cuantas tareas TERMINADAS conserva el registro antes de purgar las más viejas. Importa en lotes grandes. |
MaxTasksPerWorker | 1 | Cuantas tareas sirve un worker antes de reciclarse. 1 = un proceso por tarea, aislamiento total. 0 = sin límite. |
WorkerInitScript | "" | Guion que corre UNA VEZ POR WORKER, antes de su primera tarea. Aquí va el entorno. |
WorkerResetScript | "" | Guion que corre ENTRE TAREAS de un worker reutilizado. Aquí se limpia. |
WorkerInitFailurePolicy | "abort" | Que pasa si el guion de inicio da un error: "abort" lo tumba, "tolerate" deja que su propio ON ERROR mande y siga. |
PumpMaxItems | 8 | Cuanto atiende cada Pump(). |
PumpMaxMilliseconds | 20 | Presupuesto de tiempo de cada Pump(). |
RequestTimeoutSeconds | 15 | Cuanto espera un worker bloqueado en Call() antes de recibir Timeout. Es lo que impide que un host que no bombea deje colgado a un worker. |
SecurityMode | "Safe" | "Safe" es el unico modo real. |
AuditLevel | "basic" | off | basic | full. |
Tareas
| Miembro | Devuelve | Notas |
|---|---|---|
RunScript(cScript, cTaskName) | C | Crea, encola y devuelve el id de la tarea. LA VÍA NORMAL. |
NewTask(cScript, cTaskName) | FoxTaskHandle | Crea SIN encolar, para configurarla antes (por ejemplo, con SetTaskTimeout). |
Start(cTaskId) | L | Encola una tarea creada. .F. si no existe o ya se lanzo. |
GetState(cTaskId) | C | Created | Queued | Running | Completed | Failed | Cancelled | TimedOut, y NotFound para un id desconocido o ya purgado. |
GetResult(cTaskId) | valor | El RETURN del guion. .NULL. mientras no haya terminado bien; cadena vacía para un id desconocido. |
GetError(cTaskId) | C | Cadena vacia si no ha fallado; "Task not found." para un id desconocido. |
IsCompleted(cTaskId) | L | .T. en cualquier estado terminal Y TAMBIÉN para un id que no existe -- a propósito: si no, el bucle canonico giraria para siempre cuando la purga se llevara la tarea. |
GetProgress(cTaskId) | N | 0-100. Funciona aunque nadie bombee. |
GetProgressMessage(cTaskId) | C | El ultimo mensaje, o cadena vacía. |
PollCompleted() | C | Los ids terminados DESDE LA ULTIMA LLAMADA, separados por ";". Una llamada COM por vuelta en vez de N. LEERLO LO VACIA. |
SetTaskTimeout(cTaskId, nSegundos) | L | Plazo de UNA tarea. Solo antes de que arranque: NewTask + esto + Start. .F. si ya arrancó o no existe. |
Forget(cTaskId) | L | Olvida una tarea TERMINADA y libera su resultado. .F. si no existe o sigue viva. |
Cancelación
| Miembro | Devuelve | Notas |
|---|---|---|
Cancel(cTaskId) | L | COOPERATIVA: levanta la bandera que el guion lee con runtime.IsCancelled() y desbloquea al instante cualquier Call en vuelo. .F. si no hay nada que parar. |
CancelAll() | N | Cuantas cancelo. |
Kill(cTaskId) | L | DURA: mata el proceso por PID. Para el guion que no mira la bandera es la unica via. Acaba Cancelled con "Killed by host." |
Bombeo
| Miembro | Devuelve | Notas |
|---|---|---|
Pump(nMaxItems, nMaxMs) | N | Atiende primero las peticiones sincronas -- detrás de cada una hay un worker BLOQUEADO -- y después las notificaciones. Los dos parámetros a 0 o ausentes = usar las opciones. |
Eventos
| Miembro | Devuelve | Notas |
|---|---|---|
SetEventSink(oSink) | L | El runtime avisa DESDE DENTRO DE Pump(), en tu hilo. Devuelve .F. SIN LANZAR si al objeto le faltan los métodos obligatorios. |
ClearEventSink() | — | Soltarlo en el Destroy del formulario: mientras el runtime lo tenga, el formulario sigue vivo. |
El sink debe tener OnProgress(tcTaskId, tnPercent, tcMessage) y OnLog(tcTaskId, tcLevel, tcMessage), obligatorios, y puede tener OnTaskCompleted(tcTaskId, tcState) y OnTaskFailed(tcTaskId, tcError), que se detectan si están. El sink es adicional: GetProgress() y GetProgressMessage() funcionan igual sin él.
Handlers: lo que un worker puede pedirle al host
| Miembro | Devuelve | Notas |
|---|---|---|
RegisterHandler(cName, oTarget, cMethodName) | L | cMethodName por defecto "Invoke". Devuelve .F. SIN LANZAR si el nombre esta vacio, lleva espacios, pasa de 64 caracteres, ya esta registrado, es uno de los integrados, O SI EL OBJETO NO EXPONE ESE METODO -- esa ultima comprobacion hace que el error salga en el Init del formulario y no seis horas despues dentro de un worker. |
UnregisterHandler(cName) | L | .F. si no estaba. |
GetHandlerNames() | C | Los nombres separados por ";", alfabeticos. Es la superficie COMPLETA de lo que un worker puede pedir. Hay tres integrados siempre disponibles: echo, upper y add. |
Pizarra compartida
Resuelve «el worker necesita la configuración del host» sin viaje de ida y vuelta ni pump.
| Miembro | Devuelve | Notas |
|---|---|---|
Publish(cName, uValue) | — | Publica un valor que los workers leen con runtime.GetValue() SIN coste de cola ni de pump. |
GetValue(cName) | valor | .NULL. si no existe. |
Otros
| Miembro | Devuelve | Notas |
|---|---|---|
GetStats() | C | La foto del motor como CADENA JSON plana. Devuelve una cadena y no un objeto por dos razones: un objeto COM cobra un viaje de ida y vuelta POR PROPIEDAD, y añadir un tipo nuevo romperia el contrato de seis tipos. |
ProbeWorker(cProgId) | C | Sirve ese ProgId como worker? Se contesta EJECUTANDOLO. Sin argumento, sondea el WorkerProgId vigente. |
GetAuditPath() | C | DONDE IRIA el journal. No escribe nada. |
SetCorrelation(cId, cName, cValue) | L | Vocabulario CERRADO de siete nombres. .F. sin escribir para cualquier otro. |
GetCorrelation(cId, cName) | C | Cadena vacía si no esta puesto. |
Lo que devuelve GetStats()
Una cadena JSON plana, que se parsea con jsonfox en una sola llamada. Hay dos familias de contadores y confundirlas cuesta caro:
- createdEver, finishedEver, completedEver, failedEver, cancelledEver, timedOutEver: cuentan lo que ha PASADO desde que arrancó el runtime y no bajan NUNCA. Son los que sirven para pintar progreso.
- total, created, completed, failed, cancelled, timedOut: son la FOTO del registro ahora mismo, y sí bajan: Forget() y la purga los descuentan. Sirven para saber qué hay vivo.
{
"state": "Attached",
"workerProgId": "VisualFoxpro.Application.9",
"maxWorkers": 8,
"queued": 22,
"running": 8,
"peakRunning": 8,
"liveWorkers": 8,
"createdEver": 30,
"finishedEver": 0,
"completedEver": 0,
"failedEver": 0,
"cancelledEver": 0,
"timedOutEver": 0,
"total": 30,
"created": 0,
"completed": 0,
"failed": 0,
"cancelled": 0,
"timedOut": 0,
"purged": 0,
"keepCompletedTasks": 200,
"completedPending": 0,
"completedDropped": 0,
"pendingRequests": 0,
"expiredRequests": 0,
"pendingNotifications": 3,
"droppedTelemetry": 0,
"handlers": 2,
"requestsHandled": 47,
"handlerErrors": 0
}FoxTaskHandle
Lo devuelve NewTask(). No guarda estado: cada propiedad delega en el registro del motor. Es azúcar: todo lo que hace se puede hacer con el taskId y los métodos del runtime.
| Miembro | Tipo | Notas |
|---|---|---|
Id | C | El taskId. LA VERDAD CANONICA es esta cadena: cruza COM sin coste de vida útil y es lo que se pega en un ticket. |
Name | C | El nombre que se le dio al crearla. |
State | C | Lo mismo que GetState(Id). |
Result | valor | Lo mismo que GetResult(Id). |
Error | C | Lo mismo que GetError(Id). |
IsCompleted | L | Lo mismo que IsCompleted(Id). |
OperationId | C | Existe y SIEMPRE es cadena vacía: las operaciones no están implementadas. |
Start() | L | Encola la tarea. Lo mismo que Start(Id). |
WorkerBridge: la variable runtime dentro del guion
Es lo único que el worker ve del proceso principal, y su superficie está acotada a esa tarea. La columna «¿Bloquea?» es la que importa: sólo Call y TryCallJson esperan a que el host bombee, porque son las únicas que ejecutan código VFP en el proceso principal.
| Miembro | ¿Bloquea? | Notas |
|---|---|---|
TaskId / TaskName | no | El id y el nombre de ESTA tarea. |
ElapsedSeconds | no | Segundos desde que la tarea empezo. |
Ping() | no | "pong". |
Progress(nPercent, cMessage) | no | Fire-and-forget. No necesita pump y no falla. El porcentaje se acota a 0-100. |
Log(cLevel, cMessage) | no | "info" | "warn" | "error". Fire-and-forget. |
IsCancelled() | no | Lectura LOCAL, no toca el host. Pensada para llamarse en cada vuelta de un bucle. |
GetValue(cName) / SetValue(cName, uValue) | no | Pizarra compartida, local, sin pump. |
Call(cName, arg1..arg8) | SI | Llama a un handler del host. Síncrona: el worker espera a que el host bombee. LANZA error OLE si el handler falla. |
TryCallJson(cName, arg1..arg8) | SI | Como Call pero NO LANZA NUNCA. Devuelve JSON: {"ok":true,"value":"42","error":"","errorCode":""}. El valor viaja SIEMPRE como cadena. |
Patrones
Los dos bucles que hay que conocer:
- Lote normal: lanzar con RunScript(), guardar los ids, y girar un DO WHILE llamando a Pump() y DOEVENTS hasta que IsCompleted() diga que sí para todos.
- Lote grande (cientos o miles): preguntar tarea por tarea son N llamadas COM por vuelta. Se sube KeepCompletedTasks, se llama a PollCompleted() una vez por vuelta, se lee el resultado de los ids que entrega y se llama a Forget() en cada uno. El progreso sale de finishedEver, no de completed: con Forget() en cada vuelta, la foto se queda en cero y es correcto.
Los dos, con su código completo y las trampas de cada uno, están en Patrones.
Lo que ya no existe
Si llegas desde un buscador o desde código escrito para el motor clásico (WinFx.FoxCore), esto es lo que cambió. El motor clásico sigue existiendo y sigue soportado; no es el que se describe en esta página.
| Motor clásico | En FoxCore.Runtime 3.4.2 |
|---|---|
CREATEOBJECT("WinFx.FoxCore") / _screen.FoxCore | CREATEOBJECT("FoxCore.Runtime") -- es otro objeto COM, con otro ProgId y otro GUID. Los dos pueden estar registrados a la vez. |
Run(cProc, oParams) | RunScript(cGuion, cNombre). Ya no se pasa el NOMBRE de un procedimiento que tenga que existir en el worker: se pasa el GUION, como texto, y el worker lo ejecuta con EXECSCRIPT(). |
RunMultiple(@laTareas) | Sin equivalente, y a propósito: se llama a RunScript() en un FOR y se guardan los ids. Un método que recibe un array por referencia no cruza COM de forma estable, y lo que devolvía era un objeto contenedor: un tipo más, y el contrato son seis. |
WaitAll() / WaitAny() | Sin equivalente, y es la diferencia de fondo: el bucle lo escribe el HOST, con Pump() y DOEVENTS. Un WaitFor bloqueante dentro de VFP es exactamente la UI congelada que este producto existe para evitar. |
GetTasksSummary() / GetSystemStats() | GetStats() para los números (queued, running, liveWorkers) y PollCompleted() para saber quien acaba de terminar. Un array de objetos Task por COM cuesta un viaje por propiedad y por tarea. |
CancelTask(loTask) | Cancel(cTaskId). Se identifica por id, no por objeto. |
Activate(cClave) | Activate(cJwt), con el mismo nombre pero otra clave: desde 3.4.0 no hay fichero, la licencia es un JWT que se pasa por código. |
foxcore.lic | Sin equivalente: desde 3.4.0 no hay fichero de licencia. Ver la página de Licencias. |
PROCEDURE MiTarea(loParams, loTask) | Dentro del guion no hay parámetros: hay una variable llamada runtime (WorkerBridge). loTask.Progress = 50 pasa a runtime.Progress(50, "...") y loTask.Result = x pasa a RETURN x. |
Siguiente: Licencias ->