Herramientas de actualización remotas a través de NT con VFP
|
|||||
|
|
Por José
Joaquín de Haro
http://www.fpress.com/revista/Num9912/d9912.zip |
||||
|
|
Descripción
La idea consiste en actualizar el
software de los usuarios de una LAN con uno o varios servidores NT desde un
puesto de administrador. El
fichero de comandos en el NT
Se incluye en la login script de
todos los usuarios de los servidores NT un fichero de comandos (inicio.bat en
mi ejemplo) situado en c:\winnt\system32\Repl\Import\Scripts\ que realiza los siguientes pasos : 1.-
Chequea si existe un fichero(vacío) que se utiliza como semáforo para
habilitar/inhabilitar la actualización en ese servidor NT (ej.- para realizar
labores de single user en los datos inhabilitamos la actualización), si no
existe sale del fichero de comandos . 2.-
Chequea si existe el fichero C:\WINDOWS\SYSTEM\VFP6RUN.EXE (componente del
runtime de VFP) y si no es así, copia el runtime de VFP completo (VFPRUN.EXE,
VFP6R.DLL y VFPRESN.DLL) en la carpeta C:\WINDOWS\SYSTEM 3.-
Ejecuta el programa VFP ejecutable (en el ejemplo update.exe) pasándole un
fichero config.fpw que contiene SCREEN=OFF para que no se visualice la
pantalla principal de VFP.
[inicio.bat] @ECHO
OFF IF
NOT EXIST \\<SERVERNT>\<PATHSEMAFORO>\SERVERNT.SI GOTO FINAL IF
EXIST C:\WINDOWS\SYSTEM\VFP6RUN.EXE GOTO EJECUTA @ECHO
ON ECHO ACTUALIZANDO SU SISTEMA... COPY
\\<SERVERNT>\<PATHRUNTIMEVFP>\*.* C:\WINDOWS\SYSTEM @ECHO
OFF :EJECUTA \\<SERVERNT>\<PATHEJECUTABLE>\UPDATE.EXE ‑C \\PALACIO\DATOS\UPDATE\CONFIG.FPW :FINAL El programa que se ejecuta en el
cliente El programa de actualización debe
obtener el usuario con el que se ha iniciado la sesión en el dominio NT : FUNCTION
get_user PUBLIC lpUserIDBuffer, ; nBufferSize, ; RetVal RetVal = 0 lpUserIDBuffer
= SPACE(25) **
Return buffer for user ID string nBufferSize = 25 **
Size of user ID return buffer DECLARE
INTEGER GetUserName IN Win32API: AS GetName ; STRING @lpUserIDBuffer,
; INTEGER @nBufferSize RetVal=GetName(@lpUserIDBuffer,; @nBufferSize) RETURN
LEFT(lpUserIDBuffer,nBufferSize‑1) ENDFUNC Con el usuario obtenido, consultamos
en una tabla previamente cargada con otro programa que comentaré más tarde
(el que usará el administrador para preparar las actualizaciones), y si
existe alguna actualización pendiente para ese usuario, ponemos visible la aplicación _SCREEN.visible=.T. y sacamos un
formulario por pantalla que pregunta si queremos actualizar nuestro software
y si es así actuamos en consecuencia ejecutando la aplicación indicada. SET EXCLUSIVE OFF SET DEFAULT TO
"\\<SERVERNT>\<PATHPROGRAMA>" SET SYSMENU TO SET DELETED ON LOCAL lcuser, lcprograma, lnresult,
llsalir lcuser='' lcprograma='' lnresult=0 llsalir=.F. lcuser=get_user() && Obtengo
el login del usuario en el dominio USE programas IN 0 SHARED SELECT programas ** Consulto si queda alguna
actualización ** pendiente SCAN FOR
UPPER(ALLTRIM(lcuser))==UPPER(ALLTRIM(; programas.cuser))
AND ; programas.lactualiza
AND !llsalir lcprograma=ALLTRIM(programas.cprograma) ** Si queremos que pregunte al usuario IF
programas.lpreguntar WITH
_SCREEN .PICTURE='dipu.bmp' .CAPTION='Actualización
de su; software' .CLOSABLE=.F. .VISIBLE=.T. .WINDOWSTATE=2 ENDWITH DO
FORM update1 TO lnresult REPLACE programas.df_act WITH DATETIME() IF
lnresult = 3 REPLACE
programas.lactualiza; WITH .F. REPLACE
programas.lnoquiere; WITH .T. ENDIF IF
lnresult = 1 REPLACE
programas.lactualiza; WITH .F. IF
programas.lreiniciar RUN
/N &lcprograma && Ejecuto asíncronamente el programa llsalir=.T. ELSE RUN
&lcprograma ENDIF ENDIF ELSE REPLACE
programas.df_act WITH DATETIME() REPLACE
programas.lactualiza WITH .F. IF
programas.lreiniciar RUN
/N &lcprograma llsalir=.T. ELSE RUN
&lcprograma ENDIF ENDIF ENDSCAN USE IN programas
cuser : login del usuario a actualizar. Cprograma: camino completo del programa a ejecutar. lactualiza : Indica si la actualización está pendiente o
no. cdesc : Instrucciones para el usuario. lreiniciar : Indica si la actualización se va a realizar
en modo asíncrono respecto a la login script. lnoquiere : Indica si el usuario respondió que no quería
actualizarse. lpreguntar : Indica si visualizar o no el formulario que
pregunta al usuario si desea o no actualizarse df_act :Fecha y hora en la que el usuario a sido actualizado df_cre : Fecha y hora en la que se creó la
actualización por parte del administrador El
programa de administración
El
programa de administración es un formulario que usa el control ADSI v2.5 para
acceder a los servicios de directorio de NT y así obtener los usuarios del
mismo; para los que indicaré que programa quiero que ejecuten al iniciar una
sesión en el servidor, así como si deseo que se les pregunte si quieren
actualizarse o no y si la ejecución del programa será síncrona o asíncrona
con el login script. Incluimos los usuarios en el
listbox de la derecha y rellenamos el campo del programa con el camino completo del programa a
actualizar, y el campo descripción con una descripción de lo que vamos a
hacer; pulsamos procesar y automáticamente se darán de alta los registros
correspondientes en la tabla programas.dbf. Para obtener los usuarios del NT
con el control ADSI(v2.5) y meterlos en un control Treeview hacemos : [treeview1.enumobjects]
Extrae los objetos (usuarios, grupos, servicios,...) y los coloca en el
control treeview a partir del nodo que recibe como parámetro, el código está
extraído de la Knowledge Base de Microsoft y adaptado al ejemplo . LPARAMETERS NODE *‑‑ Enable the Stop button ThisForm.cmdStop.Enabled = .T. liCounter = 0 && local child
object counter for DOEVENTS *‑‑ Initialize objects oADSobj = NULL oCurrentADSobj = NULL oADSobj = GETOBJECT(ALLTRIM(THISFORM.; Text1.VALUE)) oCurrentADSobj = GETOBJECT(gsPath) IF THISFORM.Treeview1.Nodes.COUNT =
0 ** Treeview is not initialized oContainer
= oADSobj *‑‑
Top‑level object NODE
= THISFORM.Treeview1.Nodes.ADD(,,; oADSobj.ADSPath,
oADSobj.NAME,2 ) NODE.TAG
= oADSobj.ADSPath+;
"/"+oADSobj.NAME NODE.SORTED
= .T. Node.Expanded
= .T. FOR
EACH CHILD IN oContainer *‑‑
This could take a while, so give the user a chance to abort liCounter
= liCounter + 1 IF
liCounter = 50 && Check for every 50 objects liCounter
= 0 DOEVENTS IF
glStop ThisForm.cmdStop.Enabled
= .F. glStop
= .F. RETURN ENDIF ENDIF IF llAll OR (lsUsers $ CHILD.CLASS ; OR
lsComputers $ CHILD.CLASS ; OR
lsPrinters $ CHILD.CLASS ; OR
lsGroups $ CHILD.CLASS ; OR
lsServices $ CHILD.CLASS) NodeChild =
THISFORM.Treeview1.Nodes.ADD(NODE, 4,; , CHILD.CLASS + ":
" + CHILD.NAME,1 ) *‑‑ Store the
paths and object name so that we can GETOBJECT() *-- based on *-- information *‑‑ in the node. NodeChild.TAG = CHILD.ADSPath NodeChild.SORTED = .T. *‑‑ Set up an empty node in case
the object is a container *‑‑ Check to see if object is a
container *‑‑ If it is a container, add a
placeholder node IF
Child.Class <> "Schema" && Can't get the schema for a
schema oSchema
= GETOBJECT(CHILD.Schema) ELSE oSchema
= .F. ENDIF IF VARTYPE(oSchema) = "O" IF oSchema.CONTAINER ChildNode =
THISFORM.Treeview1.Nodes.ADD(NodeChild, 4, ,; "Empty",2) ENDIF ENDIF && VARTYPE(oSchema) =
"O" ENDIF
&& NOT llAll ENDFOR && EACH Child IN oContainer THISFORM.Treeview1.Nodes(1).Expanded
= .T. ELSE NODE.SORTED
= .T. *‑‑
Check to make sure we even have an object. *‑‑
If the network path is not found, oCurrentADSobj will be null. IF
VARTYPE(oCurrentADSobj) <> "O" RETURN ENDIF *‑‑
Check to see if object is a container IF
oCurrentADSobj.Class <> "Schema" && Can't get the
schema for a schema oSchema
= GETOBJECT(oCurrentADSobj.Schema) ELSE oSchema
= .F. ENDIF IF
VARTYPE(oSchema) = "O" IF
oSchema.CONTAINER FOR
EACH CHILD IN oCurrentADSobj *‑‑
This could take a while, so give the user a chance to abort liCounter
= liCounter + 1 IF
liCounter = 50 ** Check for every 50
objects liCounter
= 0 DOEVENTS IF
glStop ThisForm.cmdStop.Enabled
= .F. glStop
= .F. RETURN ENDIF ENDIF
NewNode = THISFORM.Treeview1.; Nodes.ADD(NODE, 4, , CHILD.; CLASS + ": " +
CHILD.NAME,1) NewNode.TAG = CHILD.ADSPath NewNode.SORTED = .T. *‑‑ Set up an empty node
in case the object *-- is a container *‑‑ Check to see if object
is a container *‑‑ If it is a container,
add a placeholder *-- node IF Child.Class <>
"Schema" && Can't get the schema for a schema oChildSchema
= GETOBJECT(CHILD.Schema) ELSE oChildSchema
= .F. ENDIF IF VARTYPE(oChildSchema) =
"O" IF
oChildSchema.CONTAINER NewChildNode = THISFORM.Treeview1.; Nodes.ADD(NewNode, 4, , "Empty",1) ENDIF ENDIF
&& VARTYPE(oChildSchema) = "O" ENDFOR && EACH Child in oCurrentADSobj ENDIF
&& oSchema.Container ENDIF
&& VARTYPE(oSchema) = "O" ENDIF
&& ThisForm.Treeview1.Nodes.; Count = 0 ThisForm.cmdStop.Enabled = .F. Esta herramienta esta siendo usada
en Diputación de Albacete(España) con 5 servidores NT y más de 100 usuarios y
funciona perfectamente, y es altamente flexible ya que el programa a ejecutar
puede ser cualquiera, incluso otro ejecutable hecho con VFP, VB, etc. José Joaquín de Haro Navarro Sección Sistemas Servicio de Informática Diputación de Albacete
|
||||
|
|
|
||||