Entradas populares

miércoles, 9 de febrero de 2011

Incialmente este proceso fue desarrollado por  Ron Petrusha y traducido por ElGuille.
Primero lo Primero

ADVERTENCIA: PUEDE HABER MUCHOS HORRORES POR LA TRUCCION DE LA PAGINA CON GOOGLE

¿Qué es una biblioteca de Windows Dynamic Link?.
Una librería de enlace dinámico (DLL) es una biblioteca de funciones y procedimientos que se puede llamar desde una aplicación o archivo DLL otro. Uso de una biblioteca de esta forma tiene dos funciones principales: 

    * Permite el intercambio de código. El mismo archivo DLL puede ser utilizada por muchos otros archivos DLL y las aplicaciones. La API de Win32, por ejemplo, se implementa como una serie de archivos DLL de Windows.
* Permite el desarrollo basado en componentes y modular, lo que hace que el desarrollo y el proceso de actualización sea más fácil. 
 
Normalmente, cuando una biblioteca estática se utiliza en el desarrollo de aplicaciones, los módulos de la biblioteca debe estar vinculada a la aplicación final. Con la vinculación dinámica, los módulos pueden residir en un archivo independiente “”DLL” que se carga de forma dinámica, ya sea cuando se carga la aplicación o cuando sus funciones miembro son necesarios. 


Un archivo DLL también incluye un punto de entrada opcional, que se llama cuando se carga un proceso o subproceso o descarga el archivo DLL. Windows llama a este punto de entrada cuando se carga un proceso y descarga el archivo DLL. También llama a la puerta de entrada cuando el proceso crea o termina un hilo. Eso permite que el archivo DLL para realizar cualquiera por proceso y la inicialización de cada aplicación y limpieza. La sintaxis de este punto de entrada, que debe utilizar la convención de llamada estándar (usado por defecto en Visual Basic), es:

Public Function DllMain(hinstDLL As Long, fdwReason As Long,
   lpwReserved As Long) As Boolean




 
los Parámetros son:
hInstDLL, es un long que contiene el identificador de instancia de la DLL. Este es el mismo módulo de la DLL de manejar.
fdwReason, una constante que indica por qué el punto de entrada ha sido llamado. Los valores posibles son:
DLL_PROCESS_ATTACH (1)
Un proceso se está cargando el archivo DLL. Cualquier inicialización por proceso se debe realizar.
DLL_THREAD_ATTACH (2)
El proceso está generando un nuevo hilo. Cualquier inicialización por hilo debe ser realizada.
DLL_THREAD_DETACH (3)
Un hilo es la salida. Cualquier limpieza por hilo debe ser realizada.
DLL_PROCESS_DETACH (0)
Un proceso es extraer el archivo DLL, o el proceso está saliendo. Cualquier limpieza por proceso se debe realizar.
lpvReserved
Es un long que proporciona más información acerca de DLL_PROCESS_ATTACH y DLL_PROCESS_DETACH(que proporciona más información acerca de fdwReason es DLL_THREAD_ATTACH  o DLL_THREAD_DETACH.)
Si fdwReason es DLL_PROCESS_ATTACH, esto es nada para las bibliotecas cargadas dinámicamente utilizando funciones como LoadLibrary y GetProcAddress, y no es nada para las bibliotecas de carga estática por talones de la colección de proporcionar en tiempo de compilación. Si fdwReason es DLL_PROCESS_DETACH, no es nada si la llamada es el resultado de una llamada a la función de Win32 FreeLibrary, y no es nada si el punto de entrada se llama durante la terminación del proceso.
El valor de retorno de la función sólo tiene sentido si es fdwReason DLL_PROCESS_ATTACH. Si la inicialización se realiza correctamente, la función debe devolver True, de lo contrario, debe devolver false. Tenga en cuenta que debido a que la función es un punto de entrada llamada por Windows, los valores de los argumentos pasados a la función son proporcionados por Windows. Además, el punto de entrada no se llama cuando un hilo se termina con la función de Win32 TerminateThread, ni se llama cuando un proceso se termina con la función de Win32 TerminateProcess.

El valor de retorno de la función sólo tiene sentido si es fdwReason DLL_PROCESS_ATTACH. Si la inicialización se realiza correctamente, la función debe devolver True, de lo contrario, debe devolver false. Tenga en cuenta que debido a que la función es un punto de entrada llamada por Windows, los valores de los argumentos pasados a la función son proporcionados por Windows. Además, el punto de entrada no se llama cuando un hilo se termina con la función de Win32 TerminateThread, ni se llama cuando un proceso se termina con la función de Win32 TerminateProcess.

El Código DLL
En el intento de desarrollar una DLL de Windows, vamos a crear una biblioteca muy simple de funciones matemáticas. El siguiente es el código del archivo DLL, que vamos a almacenar en un módulo de código (un archivo bas.) llamado MathLib:


Option Explicit
 
Public Const DLL_PROCESS_DETACH = 0
Public Const DLL_PROCESS_ATTACH = 1
Public Const DLL_THREAD_ATTACH = 2
Public Const DLL_THREAD_DETACH = 3
 
Public Function DllMain(hInst As Long, fdwReason As Long, 
  lpvReserved As Long) As Boolean
   Select Case fdwReason
      Case DLL_PROCESS_DETACH
         ' No per-process cleanup needed
      Case DLL_PROCESS_ATTACH
         DllMain = True
      Case DLL_THREAD_ATTACH
         ' No per-thread initialization needed
      Case DLL_THREAD_DETACH
         ' No per-thread cleanup needed
   End Select
End Function
 
Public Function Increment(var As Integer) As Integer
   If Not IsNumeric(var) Then Err.Raise 5
   
   Increment = var + 1
End Function
 
Public Function Decrement(var As Integer) As Integer
   If Not IsNumeric(var) Then Err.Raise 5
   
   Decrement = var - 1
End Function
 
Public Function Square(var As Long) As Long
   If Not IsNumeric(var) Then Err.Raise 5
   
   Square = var ^ 2
End Function

varias características sobre el código vale la pena mencionar. La primera es que a pesar de que incluye un procedimiento DllMain, ningún proceso o por la inicialización por subproceso debe realizarse. Así DllMain simplemente devuelve True si se le llama con el argumento de fdwReason establece DLL_PROCESS_ATTACH.

En segundo lugar, el punto de proporcionar un archivo DLL de Windows es permitir a otros idiomas para llamarlo. Para garantizar la interoperabilidad, queremos limitarnos a las características del lenguaje que la API de Win32 admite, por lo que nuestra DLL se puede llamar desde los entornos de desarrollo y muchas plataformas como sea posible. Podríamos haber hecho cada uno de nuestros tres funciones matemáticas más flexible, por ejemplo, mediante la definición tanto en el argumento de entrada y el valor de retorno como variantes. Eso habría permitido a la función para determinar el tipo de datos se deben interpretar los datos de entrada como, además del tipo de datos se debe devolver. Sin embargo, la variante es un tipo de datos definidos por el COM, de componentes de Microsoft Modelo de objetos, y no es un tipo de datos de la API Win32 reconoce. Así que en lugar, el código utiliza el estándar de Win32 API de tipos de datos.

También necesitará un programa de prueba que nos diga si nuestra DLL de Windows funciona correctamente. Para ello, se puede crear un proyecto EXE estándar con una forma y un módulo de código. El módulo de código consiste simplemente en las instrucciones Declare que definen las funciones que se encuentran en el archivo DLL:

Public Declare Function Increment Lib "MathLib.dll" (var As Integer) As Integer
 
Public Declare Function Decrement Lib "MathLib.dll" (var As Integer) As Integer
 
Public Declare Function Square Lib "MathLib.dll" (var As Long) As Long

En lugar de simplemente especificando el nombre del archivo DLL en la cláusula Lib, también debe agregar la ruta completa al directorio que contiene el archivo DLL.

El código del formulario realiza las llamadas a las funciones DLL:

Option Explicit
 
Dim incr As Integer
Dim decr As Integer
Dim sqr As Long
 
Private Sub cmdDecrement_Click()
   decr = Increment(decr)
   cmdDecrement.Caption = "x = " & CStr(decr)
End Sub
 
Private Sub cmdIncrement_Click()
   incr = Increment(incr)
   cmdIncrement.Caption = "x = " & CStr(incr)
End Sub
 
Private Sub cmdSquare_Click()
   sqr = Square(srr)
   cmdSquare.Caption = "x = " & CStr(sqr)
End Sub
 
Private Sub Form_Load()
   incr = 1
   decr = 100
   sqr = 2
End Sub


El DLL de ActiveX Tipo de Proyecto

Comencemos por crear un proyecto DLL ActiveX y ver lo que ocurre si tratamos de llamar como si fuera una norma DLL de Windows. Al crear un proyecto DLL ActiveX, Visual Basic agrega automáticamente un módulo de clase (un archivo. cls) a la misma. Puede cambiar el nombre si desea, pero no incluye ningún código. En su lugar, agregue un módulo de código (un archivo. BAS) para el proyecto, agregar el código del archivo DLL y, a continuación, compile la DLL. Al ejecutar la aplicación de prueba DLL, el diálogo de mensaje de error que se muestra en la Figura 1 aparece. El mensaje de error indica que, aunque se encontró el archivo DLL, la función específica llamada (incremento) no lo era.

Ahora Si, una vez leido algo de la teoria vamos a lo nuestro.


Paso 1. Crear copia de LINK.exe con el nombre LinkLnk.exe

Lo primero que tenemos que hacer antes de crear nuestra propia versión del enlazador (linker), es hacer una copia del programa LINK.exe, para cambiarle el nombre a esa copia, ya que ese nuevo fichero será el que usemos desde nuestra versión de LINK.EXE, esta copia es independiente de la que te comenté antes. Para hacerlo, sigue estos pasos:
  1. Sitúate en el directorio de Visual Basic (por ejemplo C:\Archivos de programa\Microsoft Visual Studio\VB98)
  2. Selecciona el fichero LINK.EXE
  3. Copia y pega
  4. A la copia, cámbiale el nombre a LinkLnk.EXE
Una vez hecho esto, el contenido de la carpeta de Visual Basic 6.0 será como el de esta figura:




Paso 2. Crear nuestra propia versión de LINK.exe

Ahora debemos crear nuestra propia versión de LINK.exe
  1. Crea un nuevo proyecto en Visual Basic 6.0 de tipo EXE estándar
  2. Cambia el nombre del proyecto a LINK
  3. Elimina el formulario que se crea de forma predeterminada
  4. Añade un módulo BAS y cámbiale el nombre a MLINK.bas
  5. En el menú Proyecto, selecciona Propiedades...
  6. En el combo "Objeto inicial", selecciona Sub Main
  7. Pulsa Aceptar para aceptar el cambio
  8. En el menú Proyecto, selecciona Referencias...
  9. Marca la casilla de Microsoft Scripting Runtime
  10. Pulsa en Aceptar
  11. En el módulo pega el código mostrado en el listado 1
  12. Compílalo (menú Archivo>Generar Link.exe...)
  13. Copia el LINK.EXE creado por este proyecto y pégalo en la carpeta de Visual Basic 6.0, te pedirá confirmación de si quieres sobrescribirlo, dile que sí


'------------------------------------------------------------------------------
' Link.exe                                                          (27/Dic/05)
' Wrapper para el Link de VB6
' Basado en el código original de Ron Petrusha
' http://www.windowsdevcenter.com/pub/a/windows/2005/04/26/create_dll.html
'
' Versión reducida (sin escribir en un .LOG)                        (25/Ene/06)
' para publicar en mi sitio
'
' ©Guillermo 'guille' Som, 2005-2006
'------------------------------------------------------------------------------

Option Explicit

Public Sub Main()

    Dim SpecialLink As Boolean, fCPL As Boolean, fResource As Boolean
    Dim intPos As Integer
    Dim strCmd As String
    Dim strPath As String
    Dim strFileContents As String
    Dim strDefFile As String, strResFile As String
    
    Dim oFS As New Scripting.FileSystemObject
    Dim fld As Folder
    Dim fil As File
    Dim tsDef As TextStream
    
    strCmd = Command$
    
    ' Determine if .DEF file exists
    '
    ' Extract path from first .obj argument
    intPos = InStr(1, strCmd, ".OBJ", vbTextCompare)
    strPath = Mid$(strCmd, 2, intPos + 2)
    ' Esto solo vale para VB6
    intPos = InStrRev(strPath, "\")
    strPath = Left$(strPath, intPos - 1)
    ' Open folder
    Set fld = oFS.GetFolder(strPath)
    
    ' Get files in folder
    For Each fil In fld.Files
        If UCase$(oFS.GetExtensionName(fil)) = "DEF" Then
            strDefFile = fil
            SpecialLink = True
        End If
        If UCase$(oFS.GetExtensionName(fil)) = "RES" Then
            strResFile = fil
            fResource = True
        End If
        If SpecialLink And fResource Then Exit For
    Next
       
    ' Change command line arguments if flag set
    If SpecialLink Then
        
        ' Determine contents of .DEF file
        Set tsDef = oFS.OpenTextFile(strDefFile)
        strFileContents = tsDef.ReadAll
        If InStr(1, strFileContents, "CplApplet", vbTextCompare) > 0 Then
            fCPL = True
        End If
        
        ' Add module definition before /DLL switch
        intPos = InStr(1, strCmd, "/DLL", vbTextCompare)
        If intPos > 0 Then
            strCmd = Left$(strCmd, intPos - 1) & _
                  " /DEF:" & Chr$(34) & strDefFile & Chr$(34) & " " & _
                  Mid$(strCmd, intPos)
        End If
        
        ' Include .RES file if one exists
        If fResource Then
            intPos = InStr(1, strCmd, "/ENTRY", vbTextCompare)
            strCmd = Left$(strCmd, intPos - 1) & Chr$(34) & strResFile & _
                     Chr$(34) & " " & Mid$(strCmd, intPos)
        End If
        
        ' If Control Panel applet, change "DLL" extension to "CPL"
        If fCPL Then
            strCmd = Replace(strCmd, ".dll", ".cpl", 1, , vbTextCompare)
        End If
        
        strCmd = strCmd & " /LINK50COMPAT"
        
    End If
    
    Shell "linklnk.exe " & strCmd
    
    If Err.Number <> 0 Then
       ' Error al llamar al LINKer
       Err.Clear
    End If
    
End Sub
 
 
Nota: Si quieres usar un icono para el ejecutable diferente al creado por el VB6, 
 puedes hacer lo siguiente:

 -Añade un formulario al proyecto.

 -Cambia el icono del formulario por el que quieras usar.

 -En el cuado de propiedades de las propiedades del proyecto (menú 
 Proyecto>Propiedades de Link...), selecciona la ficha Generar, y selecciona 
 ese formulario del combo que hay junto a Icono.

 -El Objeto inicial (de la ficha General) debe seguir siendo Sub Main.

 -Compilar el proyecto y el ejecutable tendrá el icono indicado. 
 
 
 
Ahora cuando quieras crear una DLL normal (al estilo de las del API de Windows), debes hacer lo siguiente:

Paso 3. Crear una DLL normal con Visual Basic 6.0

  1. Crea un nuevo proyecto del tipo DLL ActiveX
  2. Añade un módulo BAS
  3. Escribe en ese módulo las funciones o procedimientos Sub que quieras "exportar" como parte de la DLL (deben ser Public).
  4. Crea un fichero con el mismo nombre del proyecto que tenga la extensión .DEF.
  5. La estructura (o formato) de ese fichero es el mostrado en el listado 2.
  6. Junto a NAME escribe el nombre del proyecto (sin extensión).
  7. Junto a LIBRARY escribe el nombre que quieras (debe ser un nombre válido, no uses espacios, etc.).
  8. En DESCRIPTION escribe la descripción de la librería (dentro de comillas dobles).
  9. En EXPORTS debes indicar los nombres de las funciones o Sub que has creado, cada una de esas funciones o Sub deben llevar seguida @ y un número correlativo a partir del número 1.
  10. Guárdalo y ya puedes compilar el proyecto creado como DLL.


NAME el_nombre_del_proyecto LIBRARY el_nombre_que_quieras DESCRIPTION "La descripción que quieras usar" EXPORTS Nombre_Función_o_Sub @1 Otro_nombre_Función_o_Sub @2




Paso 4. Usar la DLL recién creada desde otro proyecto de VB6

Ahora vamos a usar desde el propio VB6 la DLL que acabamos de crear.
  1. Crea un nuevo proyecto de tipo EXE estándar
  2. Escribe los Declare para acceder a las funciones como si de una DLL de Windows se tratara
  3. Por ejemplo, si tenemos una DLL llamada Prueba.dll en la que hemos definido una función llamada Saludo que devuelve un String, la debes definir de esta forma:
    • Private Declare Function Saludo Lib "Prueba.dll" () As String
  4. La librería Prueba.dll debe estar en el mismo directorio que el ejecutable de este proyecto.
  5. Escribe un código que use esa función de la misma forma que lo harías con cualquier otra función, por ejemplo:
    • MsgBox Saludo
  6. Al ejecutarlo, verás que se muestra lo que la función Saludo devuelva, (ahora veremos un ejemplo real).
  7. En realidad las cadenas debemos "tratarlas" un poco antes de usarlas.


Código de ejemplo de una DLL en VB6

Para crear la DLL seguiremos los pasos indicados en Paso 4, que son:
-Crear un nuevo proyecto del tipo DLL ActiveX, (cambia el nombre a Prueba).
-Añade un módulo BAS, (cambia el nombre a MPrueba).
-En ese módulo pega el código del listado 4.
-Con un editor de textos crea un fichero llamado Prueba.DEF y añade el texto mostrado en el listado 5.
-El fichero .DEF debe estar en el mismo directorio del proyecto Prueba.vbp.
-Compila el proyecto, si todo ha ido bien, tendrás un fichero llamado Prueba.dll
-Esta DLL la usaremos en nuestro proyecto de prueba desde VB6 y es la misma DLL usada en el código de ejemplo de C/C++ del listado 3.
'------------------------------------------------------------------------------
' DLL de prueba para crearla desde VB6                              (25/Ene/06)
' Esta DLL se usará como una "normal" no de ActiveX DLL
'
' ©Guillermo 'guille' Som, 2006
'------------------------------------------------------------------------------
Option Explicit

Public Function Saludo() As String
    Saludo = "Hola desde la DLL Prueba"
End Function

Public Function Suma(ByVal n1 As Double, ByVal n2 As Double) As Double
    Suma = n1 + n2
End Function
Listado 4. El código de la DLL a crear con VB6
NAME Prueba
LIBRARY elGuille
DESCRIPTION "Prueba de DLL creada con VB6"
EXPORTS Saludo @1
Suma @2





Y ya tenemos nuestra DLL creada, ahora vamos a escribir un proyecto en VB6 para usar esa DLL, lo haremos de la misma forma que usaríamos cualquier DLL "normal" de Windows.

Código del proyecto de prueba hecho con VB6

  • Crea un nuevo proyecto de tipo EXE estándar.
  • En el formulario añade 2 botones, tres etiquetas y tres cajas de textos para que tenga el aspecto de la figura 2.
  • El botón de Saludar se llama cmdSaludo
  • El botón Sumar se llama cmdSumar
  • Las cajas de texto tendrán los nombres: txtNum1, txtNum2 y txtRes (que es la caja de abajo)
  • En el formulario añade el código del listado 6
  • Pulsa F5 y verás que todo funciona bien, siempre que la DLL esté en el mismo directorio que este proyecto o bien, la DLL puede estar en cualquier directorio incluido en el PATH del sistema, por ejemplo en el directorio de Windows.




'------------------------------------------------------------------------------
' Proyecto de prueba de la librería Prueba.dll creada con VB6       (25/Ene/06)
'
' ©Guillermo 'guille' Som, 2006
'------------------------------------------------------------------------------

Option Explicit

' Las declaraciones de las funciones de la DLL
Private Declare Function Saludo Lib "Prueba.dll" () As String
Private Declare Function Suma Lib "Prueba.dll" (ByVal n1 As Double, ByVal n2 As Double) As Double

Private Sub cmdSaludo_Click()
    Dim s As String
    
    s = Saludo
    
    ' Las cadenas de VB6 son Unicode y al usarla desde una DLL
    ' se hace un follón... así que debemos quitarles los Chr$(0)
    ' que tenga en medio
    s = MTrim(s)
    
    MsgBox s
End Sub

Private Sub cmdSumar_Click()
    Dim n1 As Double
    Dim n2 As Double
    
    n1 = Val(txtNum1.Text)
    n2 = Val(txtNum2.Text)
    
    txtRes.Text = Suma(n1, n2)
End Sub

Private Function MTrim(ByVal s As String) As String
    Dim i As Long
    Dim res As String
    
    For i = 1 To Len(s)
        If Mid$(s, i, 1) <> Chr$(0) Then
            res = res & Mid$(s, i, 1)
        End If
    Next
    
    MTrim = res
End Function


Aqui esta el link y el codigo completo de prueba con la Dll

Descargar Todo: Codigo fuente- Link - dll - ejemplo de Prueba

Link  DLL_con_VB6.rar.html

 


2 comentarios:

  1. segui todos los pasos indicados en este tutorial, pero me aparece siguiente error:
    "Error '453' en tiempo de ejecucion "
    "No se pude encontrar el punto de entrada de la DLL Suma en Prueba2.dll"

    verifico los nombres de las funciones en el dll y el programa, si todos son iguales, registro el dll en el sistema, lo agrego a las referencias y lo declaro como es debido. :'(

    ResponderEliminar
  2. No tengo idea de cual sea el problema, pero te dejo el link de los archivos
    https://docs.google.com/file/d/0BxWr_e1z6MQfZ1o3RDZUZ2V4b0k/edit?usp=sharing

    ResponderEliminar