2

Is it possible to retrieve via Reflection the real method name that was passed through a lambda expression?

I would like to platform invoke some functions with a better error-handling, then to avoid repeating tons of Marshal.GetLastWin32Error conditionals, I thinked in create a generic method to automate that, where I pass a reference object and a lambda expression:

<DebuggerStepThrough>
Private Shared Sub SafePInvoke(Of T)(ByRef resultVar As T, ByVal [function] As Func(Of T))

    resultVar = [function].Invoke

    Dim lastError As Integer = Marshal.GetLastWin32Error

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

End Sub

Then, I can do this to simplify error-handlings:

Dim length As Integer
SafePInvoke(length, Function() NativeMethods.GetWindowTextLength(hWnd))

I don't know if it can be improved more, it would be great to know it.

Well, now, and just for aesthetical things, If the function gets a win32 error I throw an exception and in the exception message I would like to show the real method name, in this case GetWindowTextLength instead of the "anonymous" lambda name.

This is possibly?.

ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • You may not like the result, you could resort to an Expression Tree. The body however would be `GetWindowTextLength(hWnd)` you probably dont want the param. – Ňɏssa Pøngjǣrdenlarp Jun 16 '15 at 23:24
  • from the expression body I think it could be possible to cast it to `MethodCallExpression` and take it's `Method` property (of type MethodInfo which have a Name property). Using an expression also mean you'll have to compile it inside your method to invoke it. – Sehnsucht Jun 16 '15 at 23:31
  • If for result we are talking about the syntax that takes the parameter, then actually I don't like the current syntax that I have to write (the "Function()" keyword of the lambda expression syntax). An expression tree will increment and/or will make complex the amount of writting in the parameter?, first time I hear about an expression tree (I'm already looking in MSDN) – ElektroStudios Jun 16 '15 at 23:38
  • its not* that* bad: `Sub SafePInvoke(Of T)(ByRef resultVar As T, expr As Expression(Of Func(Of T)))` but it is not what you asked for – Ňɏssa Pøngjǣrdenlarp Jun 16 '15 at 23:58

1 Answers1

2

I would not do this. It would be simpler to pass a string var representing the error message you want to display or a portion thereof (like the function name).

The "simplest" way would be to use an Expression Tree. For this, you would need to change the signature of SafePInvoke to accept an Expression.

With that you could compile the expression, and invoke it to execute the PInvoke. If there is an error, work the expression tree to get the name:

Imports System.Linq.Expressions
...
Private Shared Sub SafePInvoke(Of T)(ByRef result As T, expr As Expression(Of Func(Of T)))
    ' compile the function and invoke it
    Dim result = expr.Compile.Invoke()

    Dim lastError As Integer = Marshal.GetLastWin32Error

    If lastError <> 0 Then
        ' this is easy, but it is the full signature w/ params
        'Dim name = expr.Body.ToString

        ' ToDo: add try Catch
        Dim MethName = CType(expr.Body, MethodCallExpression).Method.Name

        Dim errMsg = MethName & " failed"
    End If
End Sub

Casting it to get the name was Sehnsucht's idea, I gave up for the time being.

Ňɏssa Pøngjǣrdenlarp
  • 38,411
  • 12
  • 59
  • 178
  • 2
    I second the "would not do this part" ; you want a name simply ask for one (possibly a clearer than the real method name). Also with VB14 addition you'll have the `NameOf` operator which can reduce the "magic string effect" : `SafePInvoke(length, Function() NativeMethods.GetWindowTextLength(hWnd), NameOf(NativeMethods.GetWindowTextLength))` Sure it's some "duplicate code" but at least renaming will work with that – Sehnsucht Jun 17 '15 at 00:02
  • @Plutonix thanks again, just could you explain why you will not do this? it is only because for aesthethical things you will preffer to pass a string, or is because any problem about performance when using an expression?. – ElektroStudios Jun 17 '15 at 00:03
  • 1
    Its convoluted and I dont like the idea of expr -> lambda -> Pinvoke; too indirect for me. – Ňɏssa Pøngjǣrdenlarp Jun 17 '15 at 00:04
  • @ElektroStudios, give him an upvote on [this](http://stackoverflow.com/a/30713133/1070452) get him points and a bronze badge – Ňɏssa Pøngjǣrdenlarp Jun 17 '15 at 00:17
  • (done) maybe now with the `MethodCallExpression` object I can do something with this? http://stackoverflow.com/questions/30878232/check-at-run-time-whether-a-p-invoke-function-has-the-dllimportattribute-setlast both take a look please, I'm analyzing the entire class `MethodCallExpression`, especially the attribute properties, but it has a lot of properties and members to learn its purpose. – ElektroStudios Jun 17 '15 at 00:28
  • You should be able to get that from the Type using reflection, the trick there is to get the Type of a lambda passed as an expression. It would be simpler to make the PInvokes private with a dedicated Shared wrapper to do whatever you want pass whatever you want etc. Since **many** Pinvokes return a code indicating fail/succeed that makes MUCH more sense. – Ňɏssa Pøngjǣrdenlarp Jun 17 '15 at 00:35