Estás trabajando sin conexión, espera a que vuelva la conexión a Internet

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

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
Este artículo se ha archivado. Se ofrece "tal cual" y no se volverá a actualizar.
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' structuresType ACL_SIZE_INFORMATION        AceCount As Long        AclBytesInUse As Long        AclBytesFree As LongEnd TypeType ACE_HEADER        AceType As Byte        AceFlags As Byte        AceSize As IntegerEnd Type' constantsPublic Const ERROR_SUCCESS = 0&Public Const ERROR_INSUFFICIENT_BUFFER = 122  ' dderrorPublic Const HKEY_CLASSES_ROOT = &H80000000Public Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000Public Const DACL_SECURITY_INFORMATION = &H4&Public Const AclSizeInformation = 2  ' from the ACL_INFORMATION_CLASS enum' API function declarationsDeclare 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 LongDeclare 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 LongDeclare Function GetSecurityDescriptorDacl Lib "advapi32.dll" _  (pSecurityDescriptor As Any, lpbDaclPresent As Long, pDacl As Long, _  lpbDaclDefaulted As Long) As LongDeclare Function GetAclInformation Lib "advapi32.dll" (pDacl As Any, _  pAclInformation As Any, ByVal nAclInformationLength As Long, _  ByVal dwAclInformationClass As Integer) As LongDeclare Function GetAce Lib "advapi32.dll" (pDacl As Any, _  ByVal dwAceIndex As Long, pAce As Any) As LongSub 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 IfEnd SubSub 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 ErrorMsgEnd 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.

Advertencia: este artículo se tradujo automáticamente

Propiedades

Id. de artículo: 202179 - Última revisión: 02/23/2014 05:27:21 - Revisión: 2.2

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

  • kbnosurvey kbarchive kbmt kbapi kbhowto KB202179 KbMtes
Comentarios