martes, 14 de julio de 2015

Consejos para programar mejor

No se si yo sea el mas indicado para crear una entrada así, aunque si escribo código, pero hay cierto tabu en la programación si no programas en Java, Python, .NET o algún lenguaje relativamente popular, me ha pasado que me han preguntado:

"¿En que lenguaje programás?"

yo les dijo:

"Yo programo en Autoit, Vb6 y Pauscal, a veces."

Y ni saben que es, aunque ese no es el problema, nunca falta el cabeza que me dice:

"Ah, entonces vos no programás."

Cuando de hecho yo se hacer mas cosas con AutoIT que el (o ellos) en C Sharp, Python y PHP combinados.

Pero bueno, la trama no es esa, el chiste aca es recibir consejos para programar mejor. Les informo que estos consejos se aplican a CUALQUIER LENGUAJE de programación existente.

Comencemos.

1 - Comentarios
Para los que no creen que los comentarios son importantes, es porque nunca se encontraron con un proyecto real.

Como sabemos, los comentarios son palabras que el compilador ignora y que nos sirven para poder entender mejor el código, nada mas util si se trabaja con código de terceros.

Es recomendable comentar lo mas que se pueda el código con la definición mas humana posible, para mejores resultados, es bueno agregar etiquetas a los comentarios.


  • FIXME: para marcar código problemático potencial que requiere una atención especial y/o revisión.
  • NOTE: peligros potenciales para documentar el funcionamiento interno del código y de indicar.
  • TODO: para indicar las mejoras planificadas.
  • XXX: para advertir a otros programadores de código problemático o equivoco.

Aunque no me las se de memoria, estas etiquetas las e visto en código de Pauscal (obviamente codeadas por otra persona) y me ha sido de utilidad.

2 - Funciones
Un error comun para muchos es llamar multiples veces una función que devuelve un mismo resultado, este error es bastante importante y es cometido mas que nada por los programadores novatos o con poca experiencia, relentiza la velocidad del programa (ya que invoca rutinas pre-programadas multiples veces) y es totalmente innecesario.

Lo mejor que puedes hacer (con funciones que sabes que devolveran el mismo valor no importa cuantas veces la llames) es almacenar el valor de la función en una variable de su tipo y utilizar esta variable, tal vez no notes cambios visibles, pero tu procesador te lo agradecera.

Un código de ejemplo que muestra el mal habito de un programador, esta función convierte una cadena binaria en su valor octal.

Function BinToOct(BinNum As String) As String
   Dim BinLen As Integer, i As Integer
   Dim OctNum As Variant
   On Error GoTo ErrorHandler
   BinLen = Len(BinNum)
   For i = BinLen To 1 Step -1
'     Check the string for invalid characters
      If Asc(Mid(BinNum, i, 1)) < 48 Or Asc(Mid(BinNum, i, 1)) > 49 Then ' < ERROR
         OctNum = ""
         Err.Raise 1002, "BinToOct", "Invalid Input"
      End If
'     Calculate Octal value of BinNum
      If Mid(BinNum, i, 1) And 1 Then
         OctNum = OctNum + 2 ^ Abs(i - BinLen)
      End If
   Next i
'  Return OctNum as String
   BinToOct = Oct(OctNum)
ErrorHandler:
End Function

Como se puede apreciar, el programador de este código (no, no fui yo) utilizo dos veces la misma sentencia "Asc(Mid(BinNum, i, 1))". ¿No conoce las variables? pff...

3 - Tabulaciones
Por fin llegamos a mi parte favorita, las tabulaciones, estas son tan importantes en mi código como el mismísimo código.

No son solo para que el código se vea mejor, nos hace diferenciar las instrucciones (por ejemplo) de dentro de un procedimiento a las de afuera de este, ocupan menos memoria ROM (¿a poco no es mejor usar tabulaciones que 6 espacios normales? por favor... son 5 bytes ahorrados...) y son mas estéticamente correctas.

En el ejemplo anterior les mostre un código de Visual Basic 6.0 sin tabular.

Function BinToOct(BinNum As String) As String
  Dim BinLen As Integer, i As Integer
  Dim OctNum As Variant
  On Error GoTo ErrorHandler
  BinLen = Len(BinNum)
  For i = BinLen To 1 Step -1
     'Check the string for invalid characters
     If Asc(Mid(BinNum, i, 1)) < 48 Or Asc(Mid(BinNum, i, 1)) > 49 Then ' < ERROR
     OctNum = ""
        Err.Raise 1002, "BinToOct", "Invalid Input"
     End If
     'Calculate Octal value of BinNum
     If Mid(BinNum, i, 1) And 1 Then
        OctNum = OctNum + 2 ^ Abs(i - BinLen)
     End If
  Next i
  'Return OctNum as String
  BinToOct = Oct(OctNum)
  ErrorHandler:
End Function

¿No les parece mas bonito?

4 - Parametros
Los parámetros son general mente pasados por valor (se crea una copia en la función o procedimiento con el mismo valor), esto puede ser conveniente aveces, pero si no vas a modificar el parámetro en ningún aspecto, no olvides pasarlos por referencia para evitarle al compilador tener que crear la copia a la variable parámetro.

5 - POO
La programación orientada a objetos es muy util, pero muchas personas se vuelven "adictas" a este paradigma, imaginense a alguien que haga una función unica pero dentro de una clase, ¿que chiste tiene?. Siempre que sea posible evita utilizar POO, ya que añade código innecesario y por lo tanto, reduce el rendimiento de tus aplicaciones, la programación orientada a objetos es utilizada para encerrar procedimientos en una tematica en particular, como una clase llamada "SO" que administre el sistema operativo, pero si programás en C# o Java, lamentablemente este tip no te sirve.

¡Saludos!

martes, 7 de julio de 2015

Tipo de dato Variante

En algunos lenguajes de programación el tipo de dato Variante no existe, no es como si todos supieran programar en Python ¿verdad?, pero en lenguajes como Visual Basic este tipo de dato es tan comun como la estructura "if", entonces, ¿que hay detras de este tipo de dato?.

Yo recuerdo que pensaba que este tipo de dato era una simple unión (para los que no saben que es una unión, vease: Unión) de todos los datos existentes, pero es algo un poquitito mas complicado, veamos.

En el mundo real, este tipo de dato puede ser declarado como una estructura con tres miembros de tipo entero (int - integer).

  1. Tipo - El tipo de dato que contendra la variante .
  2. Reservado - Este miembro debe ser cero.
  3. Ptr - Este miembro debe contener la dirección del dato.
El primer miembro puede contener uno de los siguientes valores.

Tipo de variableBytes de datosTipo C/C++Nombre de tipo
0Limpio1
1Nulo2
102A000A80HRESULT (long int)Error
1080020004HRESULT (long int)Missing3
172ABYTE (unsigned char)Byte
11FFFFVARIANT_BOOL (short int)Booleano
22A00short intEntero
32A000000long intLargo
400002842floatReal
50000000000004540doubleDoble
6A068060000000000CY EstructuraDecimal
700000000C0D5E140DATE (double)Dato
8xxxxxxxxBSTR (wchar_t pointer)Cadena
900000000Puntero IUnknownNada4
9xxxxxxxxPuntero IUnknownReferencia a objeto5

Muy simple, probemor escribir el código.

Estruc Variante,_
            Tipo:Entero,_
            Reservado:Entero,_
            Ptr:Entero

Supongamos lo siguiene, en Visual Basic por X motivo necesitamos utilizar un procedimiento de una DLL externa que NOSOTROS programamos, y por X motivo el primer parametro debe ser si o si variante, ¿como choclo hago?.

Pues muy facil, veamos el código de Visual Basic.

Private declare function Mensaje lib "MiDLL.dll" (Parametro1 as Variant)

Sub Form_Load()
       Mensaje "Hola Mundo!" ' Llamamos al procedimiento de nuestra DLL.
End Sub

Entonces el código de la DLL seria el siguiente.

Importar "Cadena.prp"
' API
Proc MessageBox(hWnd:Entero,Referencia Texto,Título:Cadena,Bandera:Entero):Entero,"User32" "MessageBoxA"


Estruc Variante,_
            Tipo:Entero,_
            Reservado:Entero,_
            Ptr:Entero

Proc Mensaje(Parametro1:Variante) Exportar
       Seleccionar Parametro1.Tipo ' Seleccionamos el tipo devuelto por la llamada de VB.
           Caso 8 ' Si el tipo del dato variante es una cadena (vease la tabla anterior).
               MessageBox(0,CadDePtr(Parametro1.Ptr),"Título!",48) ' Mostrar el contenido obtenido de Ptr miembro.
           Caso Otro
               Salir
       FinSeleccionar
FinProc

Saludos!

Crear Interfaces COM y usarlas

Todos sabemos lo que es COM (o también conocido como OLE/ActiveX), pero si no lo sabes puedes pasarte por: COM.

Hoy les voy a enseñar a como crear Interfaces COM, pero no con simples marcos ("Interfase Nombre, Metodo1,Metodo2[,Metodo3 ...]") como es hoy en día en la mayoría de los lenguajes de programación, que en realidad ocultan el verdadero diseño de la tecnología COM de Microsoft.
Hoy les voy a crear verdaderas y nativas interfaces para utilizarlas en sus objetos COM.

Este manual esta pensado para aquellos programadores que le gustan los lenguajes basados en prototipos.
¿Clonación de objetos mediante instancias? ¿Quien necesita eso?

Bien, para este manual voy a utilizar el lenguaje de programación Pauscal debido a su simplicidad, su increíble potencia y su alta semejanza al pseudo-código, aunque no lo conozcan, seguro se les va a hacer muy simple entender lo que se trata de realizar, de todas formas voy a tratar de comentar y comentar el código lo mas posible, también les informo que yo uso exclusivamente Windows.

Antes que nada, el manual da por entendido que usted (o ustedes) tiene conocimientos sobre estructuras, tipos de datos, prototipos, clases (POO), punteros y obviamente interfaces (si... con marcos...), si no es así... te recomiendo que empieces a aprender a programar.

También les informo que si ustedes tienen bastos conocimientos sobre los temas recién mencionados, pueden saltar hacia el final del manual, donde habrá un resumen sobre lo que son interfaces COM.

También les dejo una lista de los tipos de datos del lenguaje, por si tienen dudas sobre su alcance.
NombreMemoria requeridaRangoDescripción
Booleano1 byte (8 Bits)Verdad - Falso1 - 0
Byte1 byte (8 Bits)0 - 255Byte sin signo.
ByteSig1 byte (8 Bits)(-128) - 127Byte con signo.
Word2 byte (16 Bits)0 - 65.535Word sin signo.
WordSig2 byte (16 Bits)(-32768) - 32767Word con signo.
Entero4 byte (32 Bits)0 - 4.294.967.295Entero sin signo.
EnteroSig4 byte (32 Bits)(-2.147.483.648) - 2.147.483.647Entero con signo.
Real8 byte (64 Bits)(-1,79769313486232^308) - (-4.94065645841247^-324)Número con coma flotante de doble precisión
Decimal8 byte (64 Bits)(-922.337.203.685.477,5800) - 922.337.203.685.477,5800Número con coma fija de 4 decimales.
CadenaVariable0 ~ 2.000 millones de caracteresCadena caracteres alfanumérica.

Para aprender a crear una interfase, tenemos que conocer una estructura.
Una estructura esta conformada por un identificador, y el identificador de sus miembros. Por ejemplo:

Estruc Estructura, _
            Miembro1:Entero
            Miembro2:Cadena

Estas son general mente declaradas en una variable de tipo (digo "de tipo" cuando el tipo de dato es diferente a los nativos, también puede ser llamado "tipo de dato compuesto" obviamente "compuesto" por diferentes tipos de datos ¿se entiende?). Por ejemplo:

Estruc Estructura, _
            Miembro1:Entero
            Miembro2:Cadena

Var Variable:Estructura

Y se puede acceder a sus miembros (general mente) escribiéndolos antes de un punto. Por ejemplo:

Variable.Miembro ' Hola, soy un comentario.

Para los que conozcan POO (Programación Orientada a Objetos) se les hará conocido, algo que comienza con "P" y termina con "ropiedad", a estas propiedades se les puede escribir y leer valores, lo mismo pasa con un miembro de una estructura.

¿Pero esto como nos ayuda a crear interfaces? Una interfase es en realidad una estructura. Ya me imagino lo que ustedes deben estar pensando.

"Pero yo vi que en C++ una interfase se declara como una clase con métodos virtuales."
"Vos no sabes nada, seguro sos un nerd envidioso! ¿Cuantos programas tenes?"

En C++, es posible declarar una clase con métodos virtuales que es utilizado para crear objetos COM, pero esta clase es en realidad un marco, una mascara del código real, esa clase es en realidad una estructura con prototipos (en caso de C++, puntero a prototipos) y variables (de milagro no puntero a variables...).

Bueno, ya que aclaramos que es una estructura y su semejanza a las clases, aclaremos lo que es un prototipo.
Un prototipo vendría a ser la "cara" de un procedimiento que aún no existe. Un ejemplo de prototipo puede ser:

Prototipo Ejemplo(Argumento1:Entero):Byte

¿Les recuerda algo? Sí, se parece a un procedimiento sin terminar. Un prototipo no tiene código y solo puede ser utilizado si le asignamos una dirección en memoria que contenga código compatible.

¿Sabias que los miembros de una estructura pueden ser de tipo prototipo? Sí, significa que "en teoría" (es una realidad, pero teoría para ustedes) un miembro de una estructura podría ejecutar código funcional sin problema alguno.

Un ejemplo de estructura con miembros de prototipo es el siguiente.

Prototipo Ejemplo(Argumento1:Cadena):Entero

Estruc Estructura,_
          MetodoSimulado:Ejemplo,_
          SimpleMiembro:Entero

Var Variable:Estructura

' Variable.MetodoSimulado("Hola Mundo!") ' Produce error.

Si quisieramos utilizar el método de esta estructura, obtendríamos el típico error de Pauscal (Error al acceder en memoria) esto es debido a que nuestro miembro de prototipo apunta hacia 0, hacia nada.

Para hacer que el método funcione (un miembro de tipo prototipo se dominaría  "método" en una clase normal), debemos hacer que apunte hacia algo, en este caso, cambiare los argumentos del prototipo y haré que apunte hacia la API (IPA en español) MessageBox.

Prototipo Ejemplo(hWnd,lpText,lpCaption,uType:Entero):Entero

Estruc Estructura,_
          MetodoSimulado:Ejemplo,_
          SimpleMiembro:Entero

Var Variable:Estructura

Variable.MetodoSimulado@ = MessageBox@

Si desconocen esos arrobas, significa que estamos estableciendo la dirección de "MetodoSimulado" la dirección de "MessageBox".

Ahora si podemos utilizar el metodo y sera totalmente funcional.

Prototipo Ejemplo(hWnd,lpText,lpCaption,uType:Entero):Entero

Estruc Estructura,_
          MetodoSimulado:Ejemplo,_
          SimpleMiembro:Entero

Var Variable:Estructura
' Utilizamos "CadPtr" para obtener un puntero a la cadena.
Variable.MetodoSimulado(0,CadPtr("Hola!"),CadPtr("Soy un título!"),48)

Este es solo un ejemplo de como podemos utilizar un prototipo en una estructura y ejecutar código 100% funcional, pero cada método y propiedad de un objeto real (creado de una interfase COM) necesita acceder a sus miembros. Para ello, a cada prototipo hay que agregarle un parámetro adicional que se utiliza para obtener el puntero al objeto, este parámetro general mente es el primero.

Prototipo Ejemplo(PtrAEstructura,Parametro1,Parametro2:Entero):Entero

Estruc Estructura,_
          MetodoSimulado:Ejemplo,_
          SimpleMiembro:Entero

Var Variable:Estructura

Vamos a escribir un procedimiento que pueda obtener datos de los miembros de la estructura a través de un puntero a la estructura.

Proc ObtMiembro(PtrEstruc,Param1,Param2:Entero):Entero
   Var @RefAEstruct:Estructura ' Creamos un puntero de referencia a la estructura.
   RefAEstruct@ = PtrEstruc ' Establecemos la dirección a la estructura obtenida del primer parámetro.
   Devolver RefAEstruct.SimpleMiembro ' Devolvemos el miembro "SimpleMiembro" de la estructura.
FinProc

Ya tenemos el prototipo, entonces ¿Como lo usamos? obvio... al igual que con el ejemplo en el que aparece el MessageBox.

Proc ObtMiembro(PtrEstruc,Param1,Param2:Entero):Entero
   Var @RefAEstruct:Estructura ' Creamos un puntero de referencia a la estructura.
   RefAEstruct@ = PtrEstruc ' Establecemos la dirección a la estructura obtenida del primer parámetro.
   Devolver RefAEstruct.SimpleMiembro ' Devolvemos el miembro "SimpleMiembro".
FinProc

Prototipo Ejemplo(PtrAEstructura,Parametro1,Parametro2:Entero):Entero

Estruc Estructura,_
          MetodoSimulado:Ejemplo,_
          SimpleMiembro:Entero

Var Variable:Estructura

Variable.MetodoSimulado@ =  ObtMiembro@ ' Le establecemos al prototipo la dirección del procedimiento.

Variable.SimpleMiembro = 55
' EntCad = Entero a Cadena, Estructura@ = Puntero de la estructura.
Mensaje(EntCad(Variable.MetodoSimulado(Estructura@,0,0) )) ' Los dos parámetros finales no son utilizados.

En el ejemplo, el método "MetodoSimulado" devuelve 55 debido a que obtiene el valor de el miembro "SimpleMiembro" de la estructura establecida en el primer parámetro.

Este es el cuerpo de una interfase, es una estructura con prototipos que aceptan principal mente como primer parámetro un puntero a su mismo objeto.

Pero aún sigue sin ser una interfase funcional, mas adelante veremos porque.

GUID
Un GUID (Identificador único global) es un termino utilizado por Microsoft para un número que su programación para crear una identidad única para una entidad; como un documento de Word. GUID son ampliamente utilizados en productos de Microsoft para identificar interfaces, conjuntos de replicas, registros y otros objetos. Diferentes tipos de objetos tienen diferentes tipos de GUID.

Aparte de ser un termino, un GUID es una estructura que es rellenada con valores aleatorios y que identifican a un objeto.

Estruc GUID,_
Data1:Entero,_
Data2,_
Data3:Word,_
Data4[8]:Byte

Para los que no conozcan los diferentes terminos de C++ sobre GUID, le mostrare una pequeña lista.

  • REFIID - Referencia a estructura GUID.
  • IID - Estructura GUID.
  • CLSID - Estructura GUID.
Referencia: What is GUID

IUnknown
IUnknown es la interface basica de todo objeto, cada objeto creado debe tener como mínimo esta interfase (sus métodos).
Esta interfase consiste en tres métodos.
  • QueryInterface
  • AddRef
  • Release
Al igual que cualquier otra interface, se crea a partir de una estructura con prototipos y cada prototipo 
requiere como primer parámetro el puntero a su objeto.

Para mas información: IUnknown.


CoCreateInstance
Para crear un objeto funcional, debemos obtener la dirección en memoria de tal objeto, y para esto utilizamos la API CoCreateInstance.

CoCreateInstance tiene la siguiente sintaxis (en  C++).
HRESULT CoCreateInstance(
  _In_  REFCLSID  rclsid,
  _In_  LPUNKNOWN pUnkOuter,
  _In_  DWORD     dwClsContext,
  _In_  REFIID    riid,
  _Out_ LPVOID    *ppv
);
Aca podemos ver las innecesaria re-definiciones de los tipos de datos elementales del lenguaje.
En Pauscal, esto es:

Proc CoCreateInstance(Referencia rclsid:GUID,_
                                     pUnkOuter:Entero,_
                                     dwClsContext:Entero,_
                                     Referencia riid:GUID,_
                                     ppv:Entero):Entero, "Ole32.dll"

Como se puede apreciar, REFCLSID y REFIID son referencias a la misma estructura (GUID), pero con diferente nombre (aunque el nombre es irrelevante, lo que cuenta son sus miembros).

A continuación, una descripción de los parámetros:
  1. rclsid - El CLSID (GUID) asociado con los datos y el código que se utilizarán para crear el objeto.
  2. pUnkOuter - Indica que el objeto esta siendo creado como parte de un agregado.
  3. dwClsConext - Contexto en el que el código se gestiona
  4. riid - Una referencia al identificador de la interfaz que se utiliza para comunicarse con el objeto.
  5. ppv - Dirección de la variable que recibe el puntero de la interfase solicitada en riid.
El parametro 1 y 5 son los que realmente nos importa.

rclsid es el identificador de el objeto, no voy a entrar en como obtenerlo ya que este manual no enseña a utilizar llanamente la tecnología COM, solo voy a enseñar a crear Interfaces COM.

El parámetro 5 es un puntero hacia una variable que contendrá el puntero. El puntero obtenido es un puntero hacia un puntero VTable (no un puntero hacia un puntero nulo como se dice en C++, eso no existe).

¿Recuerdan cuando les dije que ustedes no habían aprendido a crear una interfase funcional?
Me refería a esto.

Ustedes aprendieron a crear VTables (Estructuras miembros de tipo prototipo). Una ejemplo-referencia de interfase es la siguiente.

Prototipo SoyUnPrototipo(PtrEstructura:Entero,Opcional Param1,Param2:Entero):Entero

Estruc Estructura,_
           Metodo1:SoyUnPrototipo

Estruc Interface,@VTable:Estructura

Una interfase real es una estructura que tiene como miembro un puntero a una VTable. Ahora les voy a mostrar un ejemplo real de interfase 100% real.

Prototipo pQueryInterface(PtrEstruc:Entero,riid:GUID,ptrReturn:Entero):Entero
Prototipo pAddRef(PtrEstruc:Entero):Entero
Prototipo pRelease(PtrEstruc:Entero):Entero
Prototipo pHrInit(PtrEstruc:Entero):Entero
Prototipo pAddTab(PtrEstruc,hWnd:Entero):Entero
Prototipo pDeleteTab(PtrEstruc,hWnd:Entero):Entero
Prototipo pActivateTab(PtrEstruc,hWnd:Entero):Entero
Prototipo pSetActivateAlt(PtrEstruc,hWnd:Entero):Entero

Estruc pITaskbarList,_
           QueryInterface:pQueryInterface,_
           AddRef:pAddRef,_
           Release:pRelease,_
           HrInit:pHrInit,_
           AddTab:pAddTab,_
           DeleteTab:pDeleteTab,_
           ActivateTab:pActivateTab,_
           SetActivateAlt:pSetActivateAlt

Estruc ITaskbarList,@Ptr:pITaskbarList

Como interfase, debe contener como mínimo la interface IUnknown incorporada en sus primeros tres miembros, de otra forma, no seria valida y no la podriamos usar.

La CLSID de ITaskbarList es "{56FDF344-FD6D-11D0-958A-006097C9A090}", pueden convertir esta cadena a su correspondiente GUID estructura con la IPA CLSIDFromString.

Ahora vamos a utilizar esta Interface (POR FIN!). Intenten seguir el siguiente código, despues se los explicare con detalle, les explico las funciones que voy a importar de la libreria "COM.prp".

  • CadGUID - Convierte una cadena GUID valida en un GUID estructura.

Importar "COM.prp"

' Constantes
Const CLSCTX_INPROC_SERVER = &1
Const CLSCTX_INPROC_HANDLER = &2
Const CLSCTX_LOCAL_SERVER = &4
Const CLSCTX_REMOTE_SERVER = &10
Const CLSCTX_ALL = CLSCTX_INPROC_SERVER Or CLSCTX_INPROC_HANDLER Or CLSCTX_LOCAL_SERVER Or CLSCTX_REMOTE_SERVER

Var PtrObtener:Entero,@TaskbarList:ITaskbarList
CoInitialize ' Inicializamos COM.
CoCreateInstance(_
CadGUID("{56FDF344-FD6D-11D0-958A-006097C9A090}"),_ ' GUID del objeto a obtener.
0,_ ' Este parametro es innecesario, se establece en nulo.
CLSCTX_ALL,_ ' Solamente pongan CLSCTX_ALL...
CadGUID("{00000000-0000-0000-C000-000000000046}"),_ ' Este parametro es innecesario, pueden poner el IID de la interface IUnknown, pero generalmente cada objeto tiene su IID.
PtrObtener@) ' Puntero a la variable de tipo entero.

Si PtrObtener <> 0 Entonces TaskbarList@ = PtrObtener
CoUninitialize ' Finalizamos COM.

Primero, declaramos las constantes necesarias, creamos 2 variables. una de tipo entero (PtrObtener) que obtendra el puntero del objeto, y un puntero a la interface creada que podremos utilizar una vez le asignemos la dirección devuelta a PtrObtener por CoCreateInstance.

Llamamos a la IPA, su primer parametro es una estructura GUID, utilizamos CadGUID para convertir la cadena a una estructura GUID, el segundo parametro es innecesario, lo establecemos nulo, el tercer parametro indica el contexto del objeto, es preferible establecer CLSCTX_ALL, el cuarto parametro es el IID del objeto, podemos establecer el IID de la interfase IUnknown, el quinto parametro es el puntero a la variable que obtendra la dirección del objeto (este objeto contiene aparte un puntero a la VTable), establecimos la dirección de PtrObtener.

Una vez llamada la función (y suponiendo que no haya ocurrido ningún error) la IPA retornara 0, PtrObtener contendra el puntero al objeto y se lo asignamos a TaskbarList.

Una vez hecho esto, el objeto esta creado y puede ser utilizado, pero recuerden establecer la dirección del objeto en el primer parametro de los metodos.

Aunque ya lo explique muchas veces, seguro no recuerdan que en cada metodo hay que establecer el puntero al objeto; asique les voy a mostrar como usar un objeto.

Taskbarlist.Ptr.HrInit(TaskbarList@)
Taskbarlist.Ptr.DeleteTab(TaskbarList@,Formulario.hWnd) ' Borra el icono de un formulario en la barra de tareas.

No fue tan dificil ¿Verdad?