Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

jueves, 24 de marzo de 2011

VB6: Rutina para Eliminar FIlas Repetidas en un Hierarchical FlexGrid

Esta rutina está pensada para un Hierarchical FlexGrid cuya primera fila (la que tiene el índice 0) contiene los nombres de las columnas, por ello procesa a partir de la fila con índice 1:

Sub eliminar_Repetidos()
Dim estaFila, unaFila As String
Dim i, j, m, n As Integer

estaFila = vbNullString
unaFila = vbNullString

With HierarchicalFlexGrid
    m = 1 ' Apunta a la primera Fila
    n = .Rows - 1 ' Cantidad de filas del HierarchicalFlexGrid
   
    Do Until m > n
        estaFila = vbNullString  'cadena con la Fila Actual
       
        For i = 0 To .Cols - 1
            estaFila = estaFila + .TextMatrix(m, i) ' Relleno la cadena con los datos de toda la fila
        Next
       
        j = 1
        Do Until j > n
            unaFila = vbNullString ' Fila a comparar
           
            For i = 0 To .Cols - 1
                unaFila = unaFila + .TextMatrix(j, i) ' la relleno con los datos de toda la fila
            Next
           
            If estaFila = unaFila And j <> m Then
                .RemoveItem (j) ' Si son iguales remuevo unaFila solamente
                n = n - 1 ' tamaño actual del HierarchicalFlexgrid, al remover disminuye en uno
            End If
           
            j = j + 1
        Loop
        m = m + 1
    Loop
   
    'Chequeo si la última y la penúltima fila son iguales, porque no se eliminan si lo son
    For i = 0 To .Cols - 1
        estaFila = estaFila + .TextMatrix(.Rows - 1, i)
        unaFila = unaFila + .TextMatrix(.Rows - 2, i)
    Next
   
    If unaFila = estaFila Then .RemoveItem (.Rows - 2)
End With
End Sub


La rutina hace lo siguiente: cojo la primera fila y, en un bucle, la comparo con todas las demás y voy eliminando las que son iguales a la primera fila. Al terminar paso a la segunda fila y vuelvo a comparar y eliminar si son iguales. Esto se repite hasta llegar al final del Hierarchical FlexGrid.
Este algoritmo es más eficiente que, en dos bucles anidados, marcar las filas repetidas para eliminarlas después.

viernes, 11 de marzo de 2011

Obtener los nombres de las tablas y los nombres de sus esquemas de una BD en SQLServer desde VB6

Un día intentaba conectarme a AdventureWorks sample BD desde una app en Visual Basic 6.

Declaraba un recordset como sigue:

db.Open miConnectionString 'db es un objeto tipo Connection
Set rs = db.OpenSchema(adSchemaTables, Array(Empty, Empty, Empty, "TABLE"))
para poder listar todas sus tablas, usando un bucle.
Mi problema era que al ejecutar un query en SQL:

Dim s as String

Do Until rs.EOF
s="Select * from " & rs!Table_Name
' Hacer muchas muchas cosas con la variable "s"
rs.MoveNext
Loop

Me botaba error, ya que sólo tenía el nombre de la tabla. Para que funcionara, el query dentro de la variable "s" necessitaba también el nombre del esquema (Schema Name) y contruirlo todo de la siguiente forma:
Select * from Schema_Name.Tabla

Luego de mucho rebuscar en Google, hallé esta web
http://www.techrepublic.com/article/much-ado-about-field-properties/1045310

Donde explican lo que hace OpenSchema. Para mi caso, utilizo el SchemaID "AdSchemaTables", el cual incluye un filtro llamado "TABLE_SCHEMA".

Finalmente mi query queda:
Dim s as String

Do Until rs.EOF
s="Select * from " & rs!Table_Schema & "." & rs!Table_Name
' Hacer muchas muchas cosas con la variable "s" 
rs.MoveNext
Loop

Update!
Cómo saber si una tabla tiene un nombre de esquema asignado:
Basta añadir este código en el bucle ("miTabla" es una variable tipo String):

miTabla = vbNullString
miTabla = rs!TABLE_SCHEMA
    
If miTabla <> vbNullString Then
      miTabla = rs!TABLE_SCHEMA & "." & rs!TABLE_NAME
Else
      miTabla = rs!TABLE_NAME
End If 

jueves, 17 de febrero de 2011

C# y Ms Access: Excepciòn del tipo "Insert Into Syntaxis Error"

Sucedió un día que tenía que insertar unos datitos a una base de datos en Access 2003 usando C#. La base de datos tiene una sola tabla (la llamaré "miTabla") con tres campos: "No", "un_ID" y "Tittle" de tipos numérico, texto y texto, respectivamente.

Mi código era:

private void AddDato(string No, string ID, string Tittle, ref OleDbConnection cnn)
        {
            OleDbCommand cmd = new OleDbCommand(
"INSERT INTO miTabla (No, un_ID, Tittle) VALUES(@Num, @uID, @nTittle)", cnn);
            cmd.Parameters.AddWithValue("@Num", Convert.ToInt32(No));
            cmd.Parameters.AddWithValue("@uID", ID);
            cmd.Parameters.AddWithValue("@nTittle", Tittle);          

                cnn.Open();
                cmd.ExecuteNonQuery();

                cmd.Dispose();
                cnn.Close();
            }
        }


Y a pesar de estar todo bien, siempre me tiraba una excepción que decía "Syntaxis Error en Insert Into", o cualquier cosa parecida, en la línea cmd.ExecuteNonQuery().

Después de horas depurando y leyendo foros, encontré por ahí un tip clave:
A las bases de datos en Access no les gustan los campos con nombres como "Date", "login" o "Password".

Entonces el error no estaba en mi Insert Into, si no, posiblemente, en los nombres de mis campos.
Hice una pequeña modificación al código (no a la base de datos) y el problema se resolvió maravillosamente:

private void AddDato(string No, string ID, string Tittle, ref OleDbConnection cnn)
        {
            OleDbCommand cmd = new OleDbCommand(
"INSERT INTO miTabla ([No], [un_ID], [Tittle]) VALUES(@Num, @uID, @nTittle)", cnn);
            cmd.Parameters.AddWithValue("@Num", Convert.ToInt32(No));
            cmd.Parameters.AddWithValue("@uID", ID);
            cmd.Parameters.AddWithValue("@nTittle", Tittle);

                cnn.Open();
                cmd.ExecuteNonQuery();


                cmd.Dispose();
                cnn.Close();
            }
        }
 

Dejo como tarea hallar el cambio que hice :)

martes, 18 de enero de 2011

C# usando LastIndexOf y Substring para separar el nombre de un archivo de la ruta donde está almacenado

Ya antes hice lo mismo en VB6. Ahora toca hacerlo desde C#.

Imaginemos que tengo este string que contiene la ruta a un archivo:

unaRuta = "C:\unaCarpeta\otraCarpeta\unArchivo.txt"

Y quisiera separalo en dos strings, con la ruta del archivo y el nombre del archivo por separado:

miRuta =  "C:\unaCarpeta\otraCarpeta\"
miArchivo = "unArchivo.txt" 

El código es el siguiente:


int n = unaRuta.LastIndexOf(@"\");  // la @ es para que considere el "\" como caracter
// se extrae la subcadena desde la primera letra hasta n+1
string miRuta = unaRuta.Substring(0, n + 1);
// se extrae la subcadena desde la letra n+1 hasta el final
string miArchivo = unaRuta.Substring(n + 1);

martes, 28 de diciembre de 2010

Código fuente en VB6 para llenar un TreeView con las tablas, y otro con las vistas, de una Base de Datos en SQL Server 2005

El código fuente es muy parecido para hacer lo mismo en Oracle 10g 
De todos modos voy a poner el código fuente completo con la explicación:

En TreeView1 irán las tablas y en TreeView2 irán las vistas. "db" es un objeto tipo Connection, "nodx" es un objeto tipo Node, "rs" y "rs1" son Recordset, "count" es una variable tipo Integer. El estilo (propiedad Style) de los TreeViews es el número 7.


db.Open miConnectionString 'cadena de conexión a la BD en SQL Server

TreeView1.Nodes.Clear
Set rs = db.OpenSchema(adSchemaTables, Array(Empty, Empty, Empty, "TABLE"))
count = 0
Set nodx = TreeView1.Nodes.Add(, , "Root", "El título que le quiero poner")

Do Until rs.EOF
    count = count + 1
    Set nodx = TreeView1.Nodes.Add(1, tvwChild, "a" & str(count), rs!TABLE_NAME)
    rs.MoveNext
Loop
rs.Close

TreeView2.Nodes.Clear
rs1 = db.OpenSchema(adSchemaTables, Array(Empty, Empty, Empty, "VIEW"))  ' <<== Acá
count = 0
Set nodx = TreeView2.Nodes.Add(, , "Root", "El título que le quiero poner")

Do Until rs1.EOF
    count = count + 1
    Set nodx = TreeView2.Nodes.Add(1, tvwChild, "a" & str(count), rs1!TABLE_NAME)
    rs1.MoveNext
Loop
rs1.Close

Si se intenta abrir el recordset rs1 con db.OpenSchema(adSchemaViews) nos dará error, y si se le coloca un On Error Resume Next, el bucle que recorre rs1 será infinito, pues como rs1 nunca se abrió, su propiedad EOF nunca será verdadera.

lunes, 6 de diciembre de 2010

VB6: usando InStrRev para separar el nombre de un archivo de la ruta donde está almacenado

Imaginemos que tengo este string que contiene la ruta a una rchivo:

unaRuta = "C:\unaCarpeta\otraCarpeta\unArchivo.txt"

Y quisiera separalo en dos strings, con la ruta del archivo y el nombre del archivo por separado:

miRuta =  "C:\unaCarpeta\otraCarpeta\"
miArchivo = "unArchivo.txt"

Para hacerlo uso la función de VB6 InStrRev, que busca una cadena dentro de otra pero enpezando desde el final.
El código completo es:

Dim miRuta as String
Dim miArchivo as String
Dim unaRuta as String
Dim unaLen as Long

unaRuta = "C:\unaCarpeta\otraCarpeta\unArchivo.txt"

miRuta = Left$(unaRuta, InStrRev(unaRuta, "\"))

unaLen = Len(unaRuta) - InStrRev(unaRuta, "\")
miArchivo = Right$(unaRuta, unaLen)

Con este código chiquito ya tenemos separados el nombre de un archivo de la ruta donde está almacenado :D

sábado, 23 de octubre de 2010

Código fuente en VB6 para llenar un TreeView con las tablas, y otro con las vistas, de una Base de Datos en Oracle

En TreeView1 irán las tablas y en TreeView2 irán las vistas. "db" es un objeto tipo Connection, "nodx" es un objeto tipo Node, "rs" y "rs1" son Recordset, "count" es una variable tipo Integer. El estilo (propiedad Style) de los TreeViews es el número 7.

db.Open miConnectionString 'cadena de conexión a la BD en Oracle

TreeView1.Nodes.Clear
Set rs = db.OpenSchema(adSchemaTables, Array(Empty, Empty, Empty, "TABLE"))
count = 0
Set nodx = TreeView1.Nodes.Add(, , "Root", "El título que le quiero poner")

Do Until rs.EOF
    count = count + 1
    Set nodx = TreeView1.Nodes.Add(1, tvwChild, "a" & str(count), rs!TABLE_NAME)
    rs.MoveNext
Loop
rs.Close

TreeView2.Nodes.Clear
Set rs1 = db.OpenSchema(adSchemaViews)
count = 0
Set nodx = TreeView2.Nodes.Add(, , "Root", "El título que le quiero poner")

Do Until rs1.EOF
    count = count + 1
    Set nodx = TreeView2.Nodes.Add(1, tvwChild, "a" & str(count), rs1!TABLE_NAME)
    rs1.MoveNext
Loop
rs1.Close

martes, 14 de septiembre de 2010

Cómo pasar parámetros desde una App en VB6 a una App escrita en C#

En general, para ejecutar otra aplicación desde VB6 y pasarle un parámetro se usa el siguiente código:

Dim LInstr As Long
Dim RutaExe As String
Dim sExeName As String

' para este caso rutaExe = vbNullString

sExeName = RutaExe & "notepad.exe" & _
        Chr$(32) & Chr$(34) & "D:\archivo_1.txt" & Chr$(32) & Chr$(34)
       
' Se ejecuta
LInstr = Shell(sExeName, vbNormalFocus) 

Esto abrirá el archivo "archivo_1.txt" (en caso de existir) en el block de notas.

El número 32 equivale al código Ascii del espacio en blanco, 34 es el código Ascii de las comillas.

Lo bueno del C# es que todas las aplicaciones escritas en este lenguaje arrancan desde un método Main con la siguiente forma:
static void Main(string[] args)

El array "args" del tipo string recibe todos los parámetros que le pasamos al ejecutable. Si es una aplicación de consola llamada "miExe" se escribiría:


Donde "1", "2" y "3" son strings y corresponden a las variables args[0], args[1] y args[2] en la aplicación en C# respectivamente. Si se desea operar con ellas, se deberá convertir de string al tipo numérico deseado, pues "Main" siempre recibe los parámetros como string. Los elementos del array args se emplean como cualquier variable de .Net tipo string.

Si queremos llamar a miExe y pasarle parámetros desde una aplicación en VB6, el código fuente será:

Dim LInstr as Long
Dim RutaExe as String
Dim sExeName as String
Dim arg1 as String, arg2 as String, arg3 as String

... coding...  coding... coding.....
' rutaExe contiene la ruta a la carpeta donde está miExe.exe

sExeName = RutaExe & "\miExe.exe" & _
        Chr$(32) & Chr$(34) & arg0 & Chr$(32) & Chr$(34) & _
        Chr$(32) & Chr$(34) & arg1 & Chr$(32) & Chr$(34) & _
        Chr$(32) & Chr$(34) & arg2 & Chr$(34) 

' Se ejecuta miExe
LInstr = Shell(sExeName, vbMinimizedNoFocus ' el segundo parámetro es opcional

La aplicación en C# separará los argumentos de la forma correcta, y los asignará a los elementos del array args, sólo si se los pasamos con espacios y comillas en el orden correcto. La aplicación en C# usa los espacios y las comillas para saber qué partes del string sExeName son el nombre del ejecutable y cada uno de los argumentos que está recibiendo.

lunes, 23 de agosto de 2010

Cómo averiguar los tipos de datos almacenados en cada campo de una base de datos

El código está en VB6:

' rs es el recorset ya enlazado a una base de datos

Select Case rs.Fields("un_campo").Type

Case adBigInt, adBinary, adDecimal, adDouble, adNumeric, adInteger, adLongVarBinary, adSingle, _
adSmallInt, adTinyInt, adUnsignedBigInt, adUnsignedInt, adUnsignedSmallInt, adUnsignedTinyInt, _
adVarNumeric, adVarBinary, adBoolean
        ' Hacer cosas con datos de tipo numérico
   
Case adBSTR, adChar, adLongVarChar, adLongVarWChar, adVarChar, adVarWChar, adWChar, _
adVariant
        ' Hacer cosas con datos de tipo texto o caracteres
   
Case adDate, adDBDate, adDBTime, adDBTimeStamp
        If IsDate(lo que hay en el campo) Then
               ' Esto funciona sólo para Access y SQL Server
        End If
        ' Para Oracle se debe usar la función TO_DATE(lo que hay en el campo, formato de fecha)
        ' el formato de fecha es DD/MM/YY por defecto.
Case Else
       ' El formato de imágenes para estas tres bases de datos es en VB6 el tipo AdLongVarBinary, 
       ' el cual tiene el valor de 205.
End Select

viernes, 2 de julio de 2010

Reutilización de componentes en desarrollo de software

Una respuesta que di cuando alguien me preguntó sobre la reutilización de componentes en desarrollo de software:

Las librerías de la plataforma .Net agrupan clases que sirven para hacer todo: mandar y recibir datos de la consola, controlar el puerto serial, los trabajos de impresión, contienen controles tales como formularios para crear ventanas, botones, cajas de texto, etc.

.Net se basa en la reutilización de pedazos de software (las librerías) y la POO. En .Net todo son clases (las librerías, también llamadas Namespaces agrupan a las clases) y objetos que referencian a estas clases y pueden "heredar" o hacer uso de sus métodos, dentro de éstos están los algoritmos.

En el generador de laberintos se usan mucho las clases y objetos que proporciona el namespace System.Drawings. Yo no he creado ningún componente, todo el laberinto se crea usando este namespace. El objeto de tipo Graphics hereda los métodos necesarios para dibujar el laberinto.

Una explicación más detallada de cómo lo programé está en mi web:
http://yelinna.comoj.com/eam/maze_generator.html
http://yelinna.comoj.com/eam/maze_generator_hex.html

El equivalente de .Net para Java es la JVM o la máquina virtual. Para el Visual C++ existe además el MFC (Microsoft Foundation Clases) que hacen un poco más fácil trabajar directamente con las funciones mismas del sistema operativo (las Apis), que son funciones que se pueden usar para hacer las mismas cosas que con el .Net o la JVM, pero dependen mucho del tipo y versión del sistema operativo (los virus para Windows son básicamente muchas llamadas a las Apis, con ellas se puede ver la libreta de direcciones de Outlook, controlar el envío por red, copair en carpetas específicas del disco duro, ver el usuario actual del sistema, etc).
Como verás, las apis son otro componente reutilizable.