Cómo llamar a funciones API de Windows con requisitos especiales de Visual Basic

Seleccione idioma Seleccione idioma
Id. de artículo: 202179 - Ver los productos a los que se aplica este artículo
Este artículo se ha archivado. Se ofrece "tal cual" y no se volverá a actualizar.
Expandir todo | Contraer todo

En esta página

Resumen

Algunas funciones de API de Microsoft Windows requieren una solución adicional cuando se llaman desde Visual Basic porque Visual Basic no se puede controlar las variables de tipo de puntero. Este comportamiento es muy común cuando se llama a seguridad de Microsoft Windows NT y API relacionadas con la impresora. Este artículo describen los siguientes casos:
  • Caso 1: una función que devuelve una estructura de longitud variable.
  • Caso 2: una función que devuelve una estructura mediante un segundo nivel de direccionamiento indirecto (que requieren un puntero a un puntero como parámetro).
  • Caso 3: utilizar una estructura devuelta con un segundo nivel de direccionamiento indirecto.

Más información

Este artículo proporciona código de ejemplo que utiliza la API de seguridad de Windows NT para ilustrar los tres casos anteriores. Para probar el ejemplo se requiere un equipo con Windows NT.

Ejemplo paso a paso

  1. Inicie un nuevo proyecto EXE estándar en Visual Basic. Se creará Form1 de manera predeterminada.
  2. Agregue un control CommandButton (Command1) a Form1.
  3. En el evento Command1_Click , agregue la siguiente línea:
        MySample
    					
  4. En el menú proyecto , agregue un nuevo módulo .BAS.
  5. Pegue el código siguiente en el módulo:
    Option Explicit
    
    ' structures
    Type ACL_SIZE_INFORMATION
            AceCount As Long
            AclBytesInUse As Long
            AclBytesFree As Long
    End Type
    
    Type ACE_HEADER
            AceType As Byte
            AceFlags As Byte
            AceSize As Integer
    End Type
    
    ' constants
    Public Const ERROR_SUCCESS = 0&
    Public Const ERROR_INSUFFICIENT_BUFFER = 122  ' dderror
    Public Const HKEY_CLASSES_ROOT = &H80000000
    Public Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000
    Public Const DACL_SECURITY_INFORMATION = &H4&
    Public Const AclSizeInformation = 2  ' from the ACL_INFORMATION_CLASS enum
    
    ' API function declarations
    Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
      (lpDest As Any, lpSrc As Any, ByVal Length As Long)
    Declare Function RegGetKeySecurity Lib "advapi32.dll" _
      (ByVal hKey As Long, ByVal SecurityInformation As Long, _
      pSecurityDescriptor As Any, lpcbSecurityDescriptor As Long) As Long
    Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" _
      (ByVal dwFlags As Long, lpSource As Any, ByVal dwMessageId As Long, _
      ByVal dwLanguageId As Long, ByVal lpBuffer As String, _
      ByVal nSize As Long, Arguments As Long) As Long
    Declare Function GetSecurityDescriptorDacl Lib "advapi32.dll" _
      (pSecurityDescriptor As Any, lpbDaclPresent As Long, pDacl As Long, _
      lpbDaclDefaulted As Long) As Long
    Declare Function GetAclInformation Lib "advapi32.dll" (pDacl As Any, _
      pAclInformation As Any, ByVal nAclInformationLength As Long, _
      ByVal dwAclInformationClass As Integer) As Long
    Declare Function GetAce Lib "advapi32.dll" (pDacl As Any, _
      ByVal dwAceIndex As Long, pAce As Any) As Long
    
    
    Sub MySample()
        Dim lErrorCode As Long
        Dim lSdSize As Long
        Dim bDaclExist As Long, bDaclPresent As Long ' booleans returned in API's
        Dim pDacl As Long  ' to store the address of a DACL
        Dim pAce As Long   ' to store the address of a ACE
        Dim i As Long
        Dim SecurityDescriptor() As Byte
        Dim aclSizeInfo As ACL_SIZE_INFORMATION
        Dim AceHeader As ACE_HEADER
    
    '
    '   CASE 1
    '
    
        ' initializing the buffer with a very low size
        lSdSize = 0
        ReDim SecurityDescriptor(lSdSize)
        
        ' first call is basically only to find out the required buffer size
        lErrorCode = RegGetKeySecurity(HKEY_CLASSES_ROOT, _
          DACL_SECURITY_INFORMATION, SecurityDescriptor(0), lSdSize)
        If lErrorCode = ERROR_INSUFFICIENT_BUFFER Then
            ' redimensioning the buffer and calling the function again
            ' the lSdSize returned the required size from the previous call
            ReDim SecurityDescriptor(lSdSize)
            lErrorCode = RegGetKeySecurity(HKEY_CLASSES_ROOT, _
              DACL_SECURITY_INFORMATION, SecurityDescriptor(0), lSdSize)
        End If
        ' display message error if not successful
        If lErrorCode <> ERROR_SUCCESS Then
            DisplayError lErrorCode, "RegGetKeySecurity"
            Exit Sub
        End If
    
    '
    '  CASE 2
    '
    
        ' get a pointer (pDacl) to the discretionary access-control list (ACL)
        ' pDacl was declared as a variable of type Long and will store the 
        ' address of the DACL list
        lErrorCode = GetSecurityDescriptorDacl(SecurityDescriptor(0), _
          bDaclPresent, pDacl, bDaclExist)
        If lErrorCode = 0 Then
            lErrorCode = Err.LastDllError
            DisplayError lErrorCode, "GetSecurityDescriptorDacl"
            Exit Sub
        End If
        
        If pDacl = 0 Then
            MsgBox "Key has a NULL DACL"
            Exit Sub
        End If
        
        ' retrieving DACL's information; information is returned in the 
        ' aclSizeInfo structure
        lErrorCode = GetAclInformation(ByVal pDacl, aclSizeInfo, _
          Len(aclSizeInfo), AclSizeInformation)
        If lErrorCode = 0 Then
            lErrorCode = Err.LastDllError
            DisplayError lErrorCode, "GetAclInformation"
            Exit Sub
        End If
        
        '
        ' if Dacl is present, get ACE's information
        ' for each ACE in the DACL list we are going to display the ACE's size
        '
        If bDaclPresent Then
            MsgBox "DACL contains " & aclSizeInfo.AceCount & " ACEs"
            If aclSizeInfo.AceCount > 0 Then
                For i = 0 To aclSizeInfo.AceCount - 1
                    ' The GetAce function obtains a pointer to an ACE in an ACL
                    ' GetAce expects a reference to DACL in the first 
                    ' parameter, thus we pass it ByVal
                    ' GetAce returns the address of an ACE in the second 
                    ' parameter, thus we pass pAce ByRef
                    ' pAce was declared as a variable of type Long and will 
                    ' store the address of an ACE
                    lErrorCode = GetAce(ByVal pDacl, i, pAce)
                    If lErrorCode = 0 Then
                        lErrorCode = Err.LastDllError
                        DisplayError lErrorCode, "GetAce"
                    Else
                        ' copying the memory block pointed by pAce to the 
                        ' ACE_HEADER structure
                        ' pAce stores the address of an ACE; we want this 
                        ' address to be passed
                        ' to the CopyMemory function, thus we pass this 
                        ' parameter ByVal.
                        CopyMemory AceHeader, ByVal pAce, Len(AceHeader)
                        ' use the AceHeader variable to access structure members
                        MsgBox "Size of ACE(" & i + 1 & ") is: " _
                          & AceHeader.AceSize
                    End If
    
                Next i
            End If
        End If
    End Sub
    
    Sub DisplayError(ByVal dwError As Long, RelatedApi As String)
        Dim ErrorMsg As String, SysMsg As String
        Dim MsgSize As Long
        
        ' get the error's description
        If dwError <> 0 Then
            MsgSize = 1000
            SysMsg = Space(MsgSize)
            
            MsgSize = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, ByVal 0&, _
              dwError, 0, SysMsg, MsgSize, ByVal 0&)
            ' function returns number of characters in string; 0=function failed
            If MsgSize = 0 Then
                SysMsg = "System error code: " & Str$(dwError)
            Else
                ' resizing the string for output
                SysMsg = Left$(SysMsg, MsgSize)
            End If
        Else
            SysMsg = ""
        End If
        
        ' including additional information in the string
        ErrorMsg = "ErrorCode: " & Str$(dwError) & vbCrLf & "API: " _
          & RelatedApi & vbCrLf & "System error: " & SysMsg
        
        MsgBox ErrorMsg
    End Sub
    					
  6. Guarde el proyecto.
  7. Presione la tecla F5 para ejecutar el proyecto.
  8. Haga clic en Command1 para probar el código.

Caso 1: una función devuelve una estructura de longitud variable

Para que una función devolver una estructura con longitud variable, tiene que pasar a la función de un puntero a un búfer de memoria suficiente para la estructura que se va a almacenar. Normalmente, las funciones que devuelven una estructura con longitud variable proporcionan una manera de averiguar el tamaño de búfer de memoria para asignar. Esto es lo que la función RegGetKeySecurity muestra en este caso. La función RegGetKeySecurity recupera una copia del descriptor de seguridad de una clave del registro, que es una estructura de tamaño variable. El tercer parámetro es un búfer de memoria que se utiliza para almacenar la propia estructura y el cuarto parámetro se utiliza como un parámetro in-and-out. Cuando se llama a la función, el cuarto parámetro contiene el tamaño del búfer asignado. Si este tamaño no es suficiente para almacenar la estructura, a continuación, se produce un error la función, devuelve el siguiente mensaje de error
ERROR_INSUFFICIENT_BUFFER
y utiliza el cuarto parámetro para devolver el tamaño necesario para la estructura. Esto le da una oportunidad para asignar un búfer mayor y llame a la función nuevo.

Un punto importante a tener en cuenta es la declaración de la función. Siempre debe comprobar la declaración de función definida en el Visor de texto de la API y cambiarlo según a la forma se llama a la función. En este caso, la declaración se cambiado a:
Declare Function RegGetKeySecurity Lib "advapi32.dll" _
  (ByVal hKey As Long, ByVal SecurityInformation As Long, _
  pSecurityDescriptor As Any, lpcbSecurityDescriptor As Long) As Long
				
tenga en cuenta que el tercer parámetro se declara como pSecurityDescriptor As Any en lugar de que se declara como una estructura. Esto ocurre porque debe pasar una referencia a un búfer de memoria en lugar de una referencia a la propia estructura. En este caso, el búfer de memoria es la matriz SecurityDescriptor que es una matriz de bytes. Tiene que pasar el búfer de memoria a la función pasando el primer elemento de la matriz (SecurityDescriptor(0)) por referencia.

Caso 2: una función devuelve una estructura utilizando un segundo nivel de direccionamiento indirecto (que requieren un puntero a un puntero como un parámetro)

Cuando una función requiere un puntero a un puntero como parámetro de salida, esto es igual a devolver la dirección de una variable. Igual una dirección puede almacenarse en un entero de 4 bytes (de tipo Long en Visual Basic), puede reemplazar este parámetro con un parámetro Long que se pasa por referencia.

La situación más común cuando una función devuelve una dirección de una estructura es cuando la función asigna la memoria para la estructura o cuando la función sólo recupera la posición de una estructura dada de un búfer ya asignado. En el ejemplo anterior, la función GetSecurityDescriptorDacl se utiliza para recuperar un puntero a la lista de control de acceso discrecional (DACL) del descriptor de seguridad. Recuperar un puntero significa recuperar una dirección, y para almacenar una dirección necesita una variable de tipo Long en Visual Basic. De nuevo, es muy importante utilizar la declaración de función adecuada. Tenga en cuenta que la declaración utilizada en el ejemplo anterior declara el tercer parámetro, el mismo que el puntero se recupera como un Long por referencia.
Declare Function GetSecurityDescriptorDacl Lib "advapi32.dll" _
  (pSecurityDescriptor As Any, lpbDaclPresent As Long, pDacl As Long, _
  lpbDaclDefaulted As Long) As Long
				

Caso 3: utilizar una estructura devuelta con un segundo nivel de direccionamiento indirecto

Una vez que recuperar el puntero de una estructura de una función, como en Case 2 pueden enfrentar dos tipos de problemas. El primer problema es cómo pasar esta estructura a otra función que espera recibir la estructura por referencia. El segundo problema es la necesidad de elementos de acceso de esta estructura directamente en Visual Basic. Ambos problemas pueden resolverse mediante la función GetAce . La función GetAce se utiliza para recuperar el puntero de una ACE de una lista DACL. El primer parámetro espera una referencia a una DACL (en otras palabras, la dirección de una DACL) y el segundo parámetro devuelve la dirección de una ACE. La dirección de la DACL se ha recuperado por la función GetSecurityDescriptorDacl y se almacena en la variable pDacl . Ya que la función GetAce espera una referencia a una DACL, sólo se puede pasar el parámetro pDacl por valor. Tenga en cuenta la diferencia aquí; cuando recuperar pDacl en el GetSecurityDescriptorDacl , pasa pDacl por referencia y ahora que está pasando por valor. Se solucionó el problema primero, que consiste en pasar una estructura recuperada por un segundo nivel de direccionamiento indirecto a otra función.

Eche un vistazo el segundo parámetro de GetAce , que devuelve la dirección de una estructura ACE. Como en Case 2, se utiliza una variable de tipo Long (ritmo) para recuperar la dirección de la estructura. Sin embargo, no hace nada con esta variable en Visual Basic Si necesita tener acceso a los miembros de esta estructura. A diferencia de C, Visual Basic no proporcionan las características para manipular punteros. La solución aquí es asignar otra variable de tipo UDT (tipo definido por el usuario) con el tipo de estructura que necesita y copie en él el contenido de la estructura que está intentando obtener acceso. En el ejemplo anterior, el tipo se ha declarado como ACE_HEADER y la variable se definió como AceHeader . Puede copiar el contenido de la estructura que apunta el ritmo de la AceHeader mediante la función plantean CopyMemory . Ahora puede obtener acceso a los miembros de la estructura utilizando la variable AceHeader . Observe que ritmo se pasa ByVal plantean CopyMemory .

Comentarios generales

  1. La función de RtlMoveMemory (normalmente se declara como plantean CopyMemory ) es una API de Win32. No se documenta en el Visor de texto de la API, pero está documentado en MSDN.
  2. Siempre compruebe detenidamente las declaraciones de estos tipos de funciones. Una declaración incorrecta puede generar una excepción.
  3. Compruebe siempre la declaración de estructuras utilizadas en estos tipos de funciones con cuidado.

Propiedades

Id. de artículo: 202179 - Última revisión: domingo, 23 de febrero de 2014 - Versión: 2.2
La información de este artículo se refiere a:
  • Microsoft Visual Basic 5.0 Learning Edition
  • Microsoft Visual Basic 6.0 Edición de aprendizaje
  • Microsoft Visual Basic 5.0 Professional Edition
  • Microsoft Visual Basic 6.0 Professional Edition
  • Microsoft Visual Basic 5.0 Enterprise Edition
  • Microsoft Visual Basic Enterprise Edition for Windows 6.0
Palabras clave: 
kbnosurvey kbarchive kbmt kbapi kbhowto KB202179 KbMtes
Traducción automática
IMPORTANTE: Este artículo ha sido traducido por un software de traducción automática de Microsoft (http://support.microsoft.com/gp/mtdetails) en lugar de un traductor humano. Microsoft le ofrece artículos traducidos por un traductor humano y artículos traducidos automáticamente para que tenga acceso en su propio idioma a todos los artículos de nuestra base de conocimientos (Knowledge Base). Sin embargo, los artículos traducidos automáticamente pueden contener errores en el vocabulario, la sintaxis o la gramática, como los que un extranjero podría cometer al hablar el idioma. Microsoft no se hace responsable de cualquier imprecisión, error o daño ocasionado por una mala traducción del contenido o como consecuencia de su utilización por nuestros clientes. Microsoft suele actualizar el software de traducción frecuentemente.
Haga clic aquí para ver el artículo original (en inglés): 202179

Enviar comentarios

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com