解释这个功能可能有什么问题(如果有的话)

时间:2015-06-17 12:49:40

标签: .net vb.net winapi pinvoke getlasterror

SCENARIO

当P / Invoking时,我认为通过设计调用函数的泛型函数来简化/减少大量代码可能是一个好主意,然后它会检查GetLastWin32Error

我正在使用此代码:

''' <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>
''' ----------------------------------------------------------------------------------------------------
''' <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))) 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 <> 0 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

我认为提高效率的事情应该是API函数应该能够设置最后一个错误,并且当函数能够执行它时,我应该将SetLastError属性设置为< strong> True ,当然如果函数自然没有设置最后一个错误,SetLastError=True将会被忽略,所以无论如何,无论我将哪种函数传递给这个泛型{{1}功能,它不会给出“误报”,或者至少我认为不会。

然后,一个用法示例应为:

  • 首先,我们寻找管理上一个错误的API函数,例如 FindWindow

  • 其次,我们在代码中添加定义,在签名中设置SafePinvoke属性。

    SetLastError
  • 最后,我们使用它。

    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function FindWindow(
                            ByVal lpClassName As String,
                            ByVal zero As IntPtr
    ) As IntPtr
    End Function
    

此时我们可以看到所有内容似乎都按预期工作,如果找到窗口它将返回非零 Intptr ,如果找不到窗口,它将返回 Intptr.Zero ,如果函数由于空字符串而失败,它将抛出一个Dim lpszParentClass As String = "Notepad" Dim parenthWnd As IntPtr = SafePInvoke(Function() FindWindow(lpszParentClass, IntPtr.Zero)) If parenthWnd = IntPtr.Zero Then MessageBox.Show(String.Format("Window found with HWND: {0}", CStr(parenthWnd))) Else MessageBox.Show("Window not found.") End If 错误代码 123 ,这意味着:

  

ERROR_INVALID_NAME

Win32Exception

一切似乎都没问题。

问题

我要说这是为了解释我的问题的原因,我不会造成任何负面影响,但发生的事情是,一些非常有经验的程序员说我的功能不安全,因为我错了许多事情GetLastWin32Error,在这个帖子中:

https://stackoverflow.com/questions/30878232/check-at-run-time-whether-a-p-invoke-function-has-the-dllimportattribute-setlast/30881540?noredirect=1#comment49821007_30881540

我的目的是从错误中吸取教训,但为此,首先我应该遇到错误的证据,而我却没有找到错误。

我想改进或者如果需要完全删除并重新考虑上面的泛型函数123 (0x7B) The filename, directory name, or volume label syntax is incorrect. 的方法,如果真的无法在“X”环境中按预期工作,只是我想通过提供可以测试以证明错误/冲突的真实代码示例来查看和了解可能是什么情况,然后,我的问题是:

有人可以通过API函数的真实代码示例说明,当通过上面的SafePinvoke函数传递时,它可能会出现“误报”错误或其他类型的冲突吗?

如果SafePinvoke函数真的安全,或者不安全,或者它是否可以改进,那么有人可以指导我并解释一下这个?提供一个可以测试的代码示例?

我将非常感谢所有可以帮助我改进这种方法的信息,或者了解这种方法在某些情况下确实不起作用,但是请给出一个演示它的代码示例。

1 个答案:

答案 0 :(得分:3)

来自GetLastError的文档:

  

设置最后错误代码的每个函数的文档的返回值部分说明了函数设置最后错误代码的条件。大多数设置线程最后错误代码的函数在失败时设置它。但是,某些函数在成功时也会设置最后一个错误代码。如果没有记录该函数来设置最后错误代码,则此函数返回的值只是已设置的最新最后错误代码;某些函数在成功时将最后一个错误代码设置为0,而其他函数则不会。

换句话说,无条件呼叫GetLastError是错误的。 GetLastError返回非零值并不表示最新的API调用失败。此外,GetLastError返回零不表示成功。