¿Comprobar la firma de una función P/Invoke en tiempo de ejecución?

Iniciado por Eleкtro, 17 Junio 2015, 03:13 AM

0 Miembros y 1 Visitante están viendo este tema.

Eleкtro

Cómo dice el título del tema, ¿alguien tiene puñetera idea de si es posible, tal vez mediante Reflection, cómo se puede comprobar los valores de los atributos de una firma de una declaración de invocación de plataforma?.

Código (csharp,1) [Seleccionar]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int xxx();


Código (vbnet,1) [Seleccionar]
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function xxx() As Integer
End Function


Basicamente necesito comprobar el valor de SetLastError en tiempo de ejecución, para determinar si es True o es False, pero parece un misterio sin resolver.

¿Alguien sabe de alguna referencia oficial de MSDN donde expliquen este detalle tan específico?, ¿cómo obtener los metadatos del método?.

Es para mejorar la lógica de este método genérico, ya que sin poder determinar ese detalle que me falta, no sirve para nada realizar un checkeo:
Código (vbnet) [Seleccionar]
   ''' <summary>
   ''' Invokes the encapsulated function, Tthen checks the exit code returned by the function that was called using platform invoke
   ''' that has the <see cref="DllImportAttribute.SetLastError"/> flag set.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <exception cref="Win32Exception">
   ''' Function 'X' thrown an unmanaged Win32 exception with error code 'X'.
   ''' </exception>
   ''' ----------------------------------------------------------------------------------------------------
   Private Shared Sub SafePInvoke(Of T)(ByRef refResult As T, ByVal expr As expression(Of Func(Of T)))

       refResult = expr.Compile.Invoke()

       Dim exitCode As Integer = Marshal.GetLastWin32Error
       If exitCode <> 0 Then
           Throw New Win32Exception([error]:=exitCode, message:=String.Format("Function '{0}' thrown an unmanaged Win32 exception with error code '{1}'.",
                                                                              CType(expr.Body, MethodCallExpression).Method.Name, CStr(exitCode)))
       End If

   End Sub


Saludos!








Eleкtro

#1
Analizando bastante a fondo la TEDIOSA class MethodInfo descubrí cómo resolverlo:

Código (vbnet) [Seleccionar]
Dim isSetLastError As Boolean =
   CType(expr.Body, MethodCallExpression).Method.GetCustomAttributes(False).
                                                 OfType(Of DllImportAttribute)().FirstOrDefault.SetLastError





EDITO:

Os dejo el método genérico si quereis testearlo, es simplemente el inicio de una solución todo-en-uno para ahorrar toneladas de repetición de escritura de código para llevar a cabo la misma tarea, y cómo tal puede no ser perfecto, de hecho no lo es, es imposible hacer una función de este estilo que sea "compatible" con la magnitud de funciones de la WinAPI, ya que la función GetLastError depende de la función "X" que haya sido llamada, en la que algunos casos no tiene por que devolver "0" para una salida exitosa, pero si estamos seguros del valor de retorno de las funciones que usemos entonces si que es bueno usarlo.

Código (vbnet) [Seleccionar]

   ''' ----------------------------------------------------------------------------------------------------
   ''' <remarks>
   ''' Title : SafePInvoke
   ''' Author: Elektro
   ''' Date  : 17-June-2015
   ''' </remarks>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' Invokes the specified encapsulated function, trying to provide a higher safety level for error-handling.
   ''' If the function that was called using platform invoke has the <see cref="DllImportAttribute.SetLastError"/>,
   ''' then it checks the exit code returned by the function, and, if is not a success code, throws a <see cref="Win32Exception"/>.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <typeparam name="T"></typeparam>
   '''
   ''' <param name="expr">
   ''' The encapsulated function.
   ''' </param>
   '''
   ''' <param name="refResult">
   ''' The referenced result variable.
   ''' </param>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <returns>
   ''' The type of the return value depends on the function definition.
   ''' </returns>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <exception cref="Win32Exception">
   ''' Function 'X' thrown an unmanaged Win32 exception with error code 'X'.
   ''' </exception>
   ''' ----------------------------------------------------------------------------------------------------
   <DebuggerStepThrough>
    Private Shared Function SafePInvoke(Of T)(ByVal expr As Expression(Of Func(Of T)),
                                              Optional ByVal successCode As Integer = 0) As T

        Dim result As T = expr.Compile.Invoke()

        Dim method As MethodInfo =
            CType(expr.Body, MethodCallExpression).Method

        Dim isSetLastError As Boolean =
            method.GetCustomAttributes(inherit:=False).
                   OfType(Of DllImportAttribute)().FirstOrDefault.SetLastError

        If isSetLastError Then

            Dim exitCode As Integer = Marshal.GetLastWin32Error

            If exitCode <> successCode Then
                Throw New Win32Exception([error]:=exitCode,
                                         message:=String.Format("Function '{0}' thrown an unmanaged Win32 exception with error code '{1}'.",
                                                                method.Name, CStr(exitCode)))
            End If

        End If

        Return result

    End Function


Ejemplos de uso ...parciales:

Código (vbnet) [Seleccionar]
Dim length As Integer = SafePInvoke(Function() NativeMethods.GetWindowTextLength(hWnd))

Código (vbnet) [Seleccionar]
Dim sb As New StringBuilder(String.Empty, length)
SafePInvoke(Function() NativeMethods.GetWindowText(hWnd, sb, sb.Capacity + 1))


Saludos!