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:
- Sitúate en el directorio de Visual Basic (por ejemplo C:\Archivos de programa\Microsoft Visual Studio\VB98)
- Selecciona el fichero LINK.EXE
- Copia y pega
- 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
- Crea un nuevo proyecto en Visual Basic 6.0 de tipo EXE estándar
- Cambia el nombre del proyecto a LINK
- Elimina el formulario que se crea de forma predeterminada
- Añade un módulo BAS y cámbiale el nombre a MLINK.bas
- En el menú Proyecto, selecciona Propiedades...
- En el combo "Objeto inicial", selecciona Sub Main
- Pulsa Aceptar para aceptar el cambio
- En el menú Proyecto, selecciona Referencias...
- Marca la casilla de Microsoft Scripting Runtime
- Pulsa en Aceptar
- En el módulo pega el código mostrado en el listado 1
- Compílalo (menú Archivo>Generar Link.exe...)
- 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
- Crea un nuevo proyecto del tipo DLL ActiveX
- Añade un módulo BAS
- Escribe en ese módulo las funciones o procedimientos Sub que quieras "exportar" como parte de la DLL (deben ser Public).
- Crea un fichero con el mismo nombre del proyecto que tenga la extensión .DEF.
- La estructura (o formato) de ese fichero es el mostrado en el listado 2.
- Junto a NAME escribe el nombre del proyecto (sin extensión).
- Junto a LIBRARY escribe el nombre que quieras (debe ser un nombre válido, no uses espacios, etc.).
- En DESCRIPTION escribe la descripción de la librería (dentro de comillas dobles).
- 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.
- 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.
- Crea un nuevo proyecto de tipo EXE estándar
- Escribe los Declare para acceder a las funciones como si de una DLL de Windows se tratara
- 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
- La librería Prueba.dll debe estar en el mismo directorio que el ejecutable de este proyecto.
- Escribe un código que use esa función de la misma forma que lo harías con cualquier otra función, por ejemplo:
- Al ejecutarlo, verás que se muestra lo que la función Saludo devuelva, (ahora veremos un ejemplo real).
- 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