htm=articulo/linkvb.htm ok articulo/linkvb.htm

Enlace tardío en Visual Basic.

Se analizan las posibilidades del enlace tardío y el modo de implementarlo en Visual Basic usando una DLL creada con ese propósito.


    Importante: Leer las anotaciones al final del artículo.

En el proceso de creación de un programa, los ficheros de texto -fuente- se compilan y se enlazan. El enlazado consiste en resolver las llamadas a funciones que se producen entre distintos módulos objeto del programa. Este enlazado se denomina enlazado estático, conviene tenerlo en cuenta aunque en Visual Basic este proceso no es muy evidente.

En Windows, los programas pueden constar de varios módulos, un fichero ejecutable (EXE) y un número indeterminado de librerías de enlace dinámico (DLL). Se llama enlace dinámico al proceso por el cual se resuelven las llamadas a funciones entre distintos módulos de una aplicación.

Dentro del enlace dinámico, podemos distinguir entre enlace temprano y enlace tardío. El enlace temprano se lleva a cabo durante la carga del ejecutable, el sistema operativo examina las dependencias entre el EXE y las DLLs y resuelve las llamadas a funciones. El enlace tardío lo lleva a cabo el programa (no el S.O.), despues de que el programa se está ejecutando es el propio programa el que se encarga de cargar las DLLs y obtener las direcciones de las funciones exportadas.

El enlace tardío se lleva a cabo en dos pasos: primero se carga la DLL usando la funcion del API LoadLibrary y luego se obtiene la direccion de la funcion con GetProcAddress. De este modo obtenemos un puntero a la funcion y posteriormente podemos invocar (llamar a) esa funcion usando ese puntero.

Actualmente Visual Basic puede obtener un puntero que apunta a una funcion de una DLL usando LoadLibrary y GetProcAddress y puede obtener un puntero que apunta a una funcion interna del programa usando el nuevo operador AddressOf. Lo que no puede hacer VB todavía es realizar una llamada a una funcion de la que solo tiene su direccion, es decir que no puede invocar a la funcion usando el puntero.

Para subsanar esta deficiencia de Visual Basic, he creado una DLL que se incluye junto con el código de ejemplo. Esta DLL exporta una funcion llamada CallProcIndirect, esta funcion recibe como primer parámetro la direccion de la otra funcion y a continuación un numero indeterminado de parámetros que son los parámetros de esa otra funcion.

En el ejemplo se invoca la funcion del API GetDiskFreeSpaceEx usando un puntero. Esta funcion no debe ser invocada usando enlace temprano ya que no está incluida en todas las versiones de KERNEL32.DLL. Si se usa esta funcion con una sentencia Declare normal, el programa no podrá ser cargado en la primera version de Windows 95. Usando enlace tardío, se puede determinar si la funcion está implementada e invocarla solo en en el caso de que efectivamente lo esté.

Para declarar GetDiskFreeSpaceEx partimos de la declaracion que tendría normalmente. Esta sería así (mas o menos):

Private Declare Function GetDiskFreeSpaceEx Lib "kernel32" ( _
  ByVal disco As String, _
  ByRef user As ULARGE, _
  ByRef total As ULARGE, _
  ByRef free As ULARGE) As Long

Modificamos esta declaracion, para que GetDiskFreeSpaceEx sea un alias de CallProcIndirect, además cambiamos el nombre de la DLL (CallProcIndirect está en INDIRECT.DLL") y añadimos como primer parámetro ByVal direccion As Long. La declaración quedaría definitivamente así:

Private Declare Function GetDiskFreeSpaceEx Lib "Indirect" Alias "CallProcIndirect" ( _
  ByVal direccion As Long, _
  ByVal disco As String, _
  ByRef user As ULARGE, _
  ByRef total As ULARGE, _
  ByRef free As ULARGE) As Long

Posteriormente, en cualquier momento de la ejecución del programa debemos obtener la direccion de la funcion GetDiskFreeSpaceEx, para ello usamos las APIs LoadLibrary y GetProcAddress.

  handle_kernel = LoadLibrary("Kernel32.dll")
  If handle_kernel <> 0 Then
    direccion_diskex = GetProcAddress(handle_kernel, "GetDiskFreeSpaceExA")
  End If

Solo nos queda realizar la llamada a la funcion, recordando poner como primer parámetro su direccion.

    result = GetDiskFreeSpaceEx(direccion_diskex, "C:", u, t, f)

En este punto, si el programa provoca una excepción debemos repasar la declaracion de la funcion. Es importante recordar que la funcion CallProcIndirect se traga cualquier llamada que hagamos, la comprobacion de tipos y parámetros la hace el compilador de VB basandose en la declaracion (sentencia Declare).


Anotaciones:

El ejemplo anterior (GetDiskFreeSpaceEx) no ha resultado muy afortunado. Visual Basic usa internamente enlace tardío, de modo que se puede declarar (sentancia Declare) una funcion que no exista y el programa no fallará. Al invocar la funcion por primera vez el Visaul Basic usa LoadLibrary y GetProcAddress para buscar la funcion, es en este momento cuando se producirá el error.

Así pues, el problema anterior se podía haber solventado sin usar la DLL auxiliar, declarando la funcion normalmente:

Private Declare Function GetDiskFreeSpaceEx Lib "kernel32" _
  Alias  "GetDiskFreeSpaceExA" ( _
  ByVal disco As String, _
  ByRef user As ULARGE, _
  ByRef total As ULARGE, _
  ByRef free As ULARGE) As Long

Y comprobando que la funcion existe antes de invocarla:

  handle_kernel = LoadLibrary("Kernel32.dll")
  If handle_kernel <> 0 Then
    dir_diskex = GetProcAddress(handle_kernel, "GetDiskFreeSpaceExA")
    FreeLibrary handle_kernel  'balancear llamadas a Load y a Free
  Else
    dir_diskex = 0
  End If
  ' realizar la llamada solo si existe la funcion
  If dir_diskex <> 0 Then
    result = GetDiskFreeSpaceEx("C:", u, t, f)
  End If

Sin embargo existen otros casos en los que el uso de la DLL, tal como se ha descrito, es necesario. Por ejemplo, si no se conoce el nombre de la DLL o el nombre de la funcion hasta el momento de la ejecución, o si queremos descargar la DLL (FreeLibrary) despues de usar la funcion.

Hay que tener en cuenta que Visual Basic solo usa LoadLibrary y GetProcAddress la primera vez que se invoca la funcion, y exige que el nombre de la librería y el nombre de la funcion (en la sentencia Declare) sean constantes.

    Los comentarios por e-mail son bienvenidos !


Indirect.DLL y CallProcIndirect.

La librería Indirect.DLL exporta una sola funcion, CallProcIndirect, que permite a Visual Basic invocar a una funcion cualquiera usando un puntero (en VB un Long). No existe limitacion en el número de parámetros de la funcion. Tampoco depende del lugar físico donde está la funcion, sirve tanto para funciones situadas en DLL's como funciones de VB (en un módulo .BAS).

Para usar esta funcion es necesario declarar (sentencia Declare) su prototipo como un alias de CallProcIndirect y añadiendo el puntero como primer parámetro, segun se ha descrito en el ajemplo anterior. Esto es válido tambien si se quiere usar CallProcIndirect con funciones de VB (en un módulo .BAS).


© info3@maicas.net