LOCAL NomImpresora
strComputer = "."
objWMIService = GETOBJECT("winmgmts:"+ "{impersonationLevel=impersonate}!\\" + strComputer + "\root\cimv2")
colInstalledPrinters = objWMIService.ExecQuery("SELECT * FROM Win32_Printer")
*** impresora predeterminada
NomImpresora = UPPER(SET("Printer",2))
FOR EACH objPrinter IN colInstalledPrinters
IF ALLTRIM(UPPER(objPrinter.NAME)) = NomImpresora
IF (objPrinter.PrinterStatus = 3 OR objPrinter.PrinterStatus = 2) AND objPrinter.workoffline
WAIT WIND "Impresora Apagada"
RETURN
ENDIF
ENDIF
NEXT
Jose Luis Sayago Baez
30 de enero de 2012
Saber si una impresora esta en linea
Aquí les dejo un código que modifique para saber si una impresora esta en linea.
28 de enero de 2012
Controladores ODBC de 32 bits en SO de 64 bits
Como acceder a los controladores de orígenes de datos de 32 bits en los sistemas operativos de 64 bits.
Muchas veces vimos la pregunta de que en los sistemas operativos de 64 bits no se pueden acceder a los controladores de datos ODBC de 32 bits desde el "Administrador de orígenes de datos ODBC"
La solución a este problema es abrir la ventana "Ejecutar" (con la combinación de teclas "Windows" + "R"), tipear
y darle al botón "Aceptar".
Para tener simpre a mano, deberían crear un "Nuevo acceso directo" y tipear
en el cuadro de texto "Ubicación del elemento" y darle el nombre "Administrador de orígenes de datos ODBC de 32 bits".
Muchas veces vimos la pregunta de que en los sistemas operativos de 64 bits no se pueden acceder a los controladores de datos ODBC de 32 bits desde el "Administrador de orígenes de datos ODBC"
La solución a este problema es abrir la ventana "Ejecutar" (con la combinación de teclas "Windows" + "R"), tipear
"%systemdrive%\Windows\SysWoW64\odbcad32.exe"y darle al botón "Aceptar".
Para tener simpre a mano, deberían crear un "Nuevo acceso directo" y tipear
"%systemdrive%\Windows\SysWoW64\odbcad32.exe"en el cuadro de texto "Ubicación del elemento" y darle el nombre "Administrador de orígenes de datos ODBC de 32 bits".
12 de diciembre de 2011
Crear un buffer rapidamente
Primero, aclaremos conceptos. Llamo "buffer" a un objeto muy liviano cuya unica función es contener data temporal en forma de propiedades. En muchos sentidos vendría a ser como una versión light de un STRUCT, solo que sin la posibilidad de incluir métodos; solo propiedades.
Por ejemplo, si quiero pasar información sobre una persona de un form a otro, me seria muy útil pasar un solo parámetro que contuviera toda la información de esa persona. Una forma de lograr esto es crear una clase "Persona" que contenga las propiedades para almacenar la información sobre una persona, pero muchas veces queremos lograr lo mismo sin la sobrecarga de crear una clase que en si misma no hace nada.
Ese objeto "temporal" que sirve para almacenar múltiples informaciones relacionadas, es lo que llamo "buffer". La función crearBuffer hace justamente eso: crea un buffer con una lista de propiedades dada y, opcionalmente, inicializa esas propiedades con los datos suministrados; todo en una misma instrucción.
Ejemplo:
Victor Espina
Por ejemplo, si quiero pasar información sobre una persona de un form a otro, me seria muy útil pasar un solo parámetro que contuviera toda la información de esa persona. Una forma de lograr esto es crear una clase "Persona" que contenga las propiedades para almacenar la información sobre una persona, pero muchas veces queremos lograr lo mismo sin la sobrecarga de crear una clase que en si misma no hace nada.
Ese objeto "temporal" que sirve para almacenar múltiples informaciones relacionadas, es lo que llamo "buffer". La función crearBuffer hace justamente eso: crea un buffer con una lista de propiedades dada y, opcionalmente, inicializa esas propiedades con los datos suministrados; todo en una misma instrucción.
Ejemplo:
LOCAL oPersona
oPersona = crearBuffer("nombre,apellido,cedula,fechaNac,cargo","Victor","Espina","12345678",{18-11-1970})
?oPersona.Nombre --> "Victor"
?oPersona.Apellido --> "Espina"
Práctico, cierto? aqui les dejo el código fuente.*-- crearBuffer
* Funcion para crear un buffer de datos e inicializarlo. Compatible con VFP 5 o superior
*
* Autor: V Espina
*
* Ejemplo:
* oBuff = CFDBuffer("Nombre,Apellido","Victor","Espina")
* ?oBuff.Nombre -> "Victor"
* ?oBuff.Apellido -> "Espina"
*
PROCEDURE crearBuffer
LPARAMETERS pcItemList,p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19
*
LOCAL oBuff,i,cProp
oBuff=CREATEOBJECT("Custom")
LOCAL cPropName,uPropValue,nCount
LOCAL ARRAY aProps[1]
nCount = ALINES(aProps,STRT(pcItemList,",",CHR(13)+CHR(10)))
FOR i=1 TO MIN(nCount,20)
cPropName = aProps[i]
uPropValue = EVALUATE("P" + ALLTRIM(STR(i - 1)))
oBuff.AddProperty(cPropName, uPropValue)
ENDFOR
RETURN oBuff
*
ENDPROC
Victor Espina
7 de diciembre de 2011
Una manera distinta de armar un IN dinamico a partir de una lista de elementos
Esta es una situación que seguro les ha pasado muchas veces. Tienen un SP que recibe un parametro usado para filtrar sobre una tabla, digamos @status. Normalmente haríamos algo como esto:
Eso nos permitiria filtrar por un status especifico o no filtrar por status, indicando el valor NULL. Pero luego nos topamos con una situación en la que necesitamos filtrar por DOS valores de status distintos, digamos, registros ACTIVOS y ANULADOS. Obviamente nuestra primera intención es hacer:
lo cual no es una instrucción valida para SQL Server, sin importar lo logico que se vea. Normalmente la solución a esta situación pasa por la creación de una función que tome un VARCHAR con la lista de valores separados por coma y devuelva un TABLE con los valores ya separados, y luego usamos ese TABLE para hacer un FULL JOIN o un IN (SELECT).
Pero hoy encontre una solución bien sencilla, usando las desconocidas (al menos para mi) capacidades XML de SQL Server. La idea, basicamente, es usar la lista de valores para crear un XML y luego usar ese XML como una fuente datos en un IN (SELECT). Aplicando esto al ejemplo que mencionaba al principio, la cosa quedaria asi:
Simple, cierto?
Victor Espina
SELECT * FROM tabla WHERE (@status IS NULL OR status = @status)
Eso nos permitiria filtrar por un status especifico o no filtrar por status, indicando el valor NULL. Pero luego nos topamos con una situación en la que necesitamos filtrar por DOS valores de status distintos, digamos, registros ACTIVOS y ANULADOS. Obviamente nuestra primera intención es hacer:
SELECT * FROM tabla WHERE (@status IS NULL OR status IN (@status))
lo cual no es una instrucción valida para SQL Server, sin importar lo logico que se vea. Normalmente la solución a esta situación pasa por la creación de una función que tome un VARCHAR con la lista de valores separados por coma y devuelva un TABLE con los valores ya separados, y luego usamos ese TABLE para hacer un FULL JOIN o un IN (SELECT).
Pero hoy encontre una solución bien sencilla, usando las desconocidas (al menos para mi) capacidades XML de SQL Server. La idea, basicamente, es usar la lista de valores para crear un XML y luego usar ese XML como una fuente datos en un IN (SELECT). Aplicando esto al ejemplo que mencionaba al principio, la cosa quedaria asi:
DECLARE @x XML
SET @x = '' + REPLACE( @status, ',', '') + ''
SELECT *
FROM tabla
WHERE status IN (SELECT x.j.value('.', 'VARCHAR(max)') AS item FROM @x.nodes('//j') x(j))
Simple, cierto?
Victor Espina
1 de diciembre de 2011
Abrir un cursor para lectura/escritura
Como sabemos, los cursores creados por el comando SELECT INTO en VFP son solo lectura. A partir de VFP 7 se incluyo una clausula llamada READWRITE que nos permite indicar si queremos que el cursor sea de lectura-escritura, lo cual es sumamente útil en infinidad de situaciones.
Sin embargo, para los que por algún motivo aun debemos seguir trabajando con versiones anteriores a VFP 6, convertir un cursor a lectura-escritura representa todo un reto. El truco mas usado para lograr esto en VFP 6 es usar la clausula NOFILTER para asegurarnos que VFP creara un cursor fisico temporal, hacer una copia fisica del cursor temporal con otro nombre, cerrar el cursor original y luego abrir el cursor temporal con el nombre original del cursor:
Pero esta solución dista mucho de ser perfecta, especialmente si el cursor original contaba con columnas cuyo nombre superara los 10 caracteres. En estos casos, el nombre de esas columnas era truncado lo cual causaba molestos problemas con las referencias a esas columnas en el código.
Hace un tiempo ayudé a un amigo a encontrar una forma de solventar este problema, y me encontré con que, como siempre, VFP me daba todas las herramientas necesarias para lograrlo; era solo cuestión de ponerlas a trabajar juntas.
En este caso, la ayuda llegó de mano de los arrays. Yo sabía desde siempre que VFP contaba con una función que tomaba la estructura de un DBF o cursor y la almacenaba en un array. Lo que no sabía es que tanto el comando CREATE CURSOR como INSERT INTO contenían la clausula FROM ARRAY.
El resultado? una función que recibe el nombre de un cursor solo-lectura y lo convierte en lectura-escritura respetando los nombres largos de columna.
Aqui les dejo la función por si les resulta útil (NOTA Nov 19: limpié y mejorpe un poco el código, ademas de corregir un problema cuando el cursor original estaba vacío).
Victor Espina
Sin embargo, para los que por algún motivo aun debemos seguir trabajando con versiones anteriores a VFP 6, convertir un cursor a lectura-escritura representa todo un reto. El truco mas usado para lograr esto en VFP 6 es usar la clausula NOFILTER para asegurarnos que VFP creara un cursor fisico temporal, hacer una copia fisica del cursor temporal con otro nombre, cerrar el cursor original y luego abrir el cursor temporal con el nombre original del cursor:
SELECT * FROM mitabla INTO CURSOR Q1 NOFILTER
SELECT Q1
COPY FILE (DBF("Q1")) TO ("Q1.TMP")
USE IN Q1
SELECT 0
USE ("Q1.TMP") ALIAS Q1
Pero esta solución dista mucho de ser perfecta, especialmente si el cursor original contaba con columnas cuyo nombre superara los 10 caracteres. En estos casos, el nombre de esas columnas era truncado lo cual causaba molestos problemas con las referencias a esas columnas en el código.
Hace un tiempo ayudé a un amigo a encontrar una forma de solventar este problema, y me encontré con que, como siempre, VFP me daba todas las herramientas necesarias para lograrlo; era solo cuestión de ponerlas a trabajar juntas.
En este caso, la ayuda llegó de mano de los arrays. Yo sabía desde siempre que VFP contaba con una función que tomaba la estructura de un DBF o cursor y la almacenaba en un array. Lo que no sabía es que tanto el comando CREATE CURSOR como INSERT INTO contenían la clausula FROM ARRAY.
El resultado? una función que recibe el nombre de un cursor solo-lectura y lo convierte en lectura-escritura respetando los nombres largos de columna.
Aqui les dejo la función por si les resulta útil (NOTA Nov 19: limpié y mejorpe un poco el código, ademas de corregir un problema cuando el cursor original estaba vacío).
* openForUpdate() * Función que recibe un cursor de S/L y lo convierte en L/E respetando las columnas con nombres largos * * Autor: Victor Espina * PROCEDURE openForUpdate(pcCursor) * * Obtenemos el schema del cursor LOCAL ARRAY aSchema[1] AFIELDS(aSchema, pcCursor) * Obtenemos el contenido del cursor LOCAL ARRAY aRows[1] LOCAL nRowCount SELECT * FROM (pcCursor) INTO ARRAY aRows nRowCount = _TALLY * Cerrar el cursor original, lo recreamos como un cursor de L/E * y lo llenamos con los datos originales (si habia) SELECT (pcCursor) USE CREATE CURSOR (pcCursor) FROM ARRAY aSchema IF nRowCount > 0 INSERT INTO (pcCursor) FROM ARRAY aRows ENDIF GO TOP * ENDPROC
Victor Espina
Suscribirse a:
Entradas
(
Atom
)