.net - 解释一下这个功能可能会出现什么问题(如果有的话)

标签 .net vb.net winapi pinvoke getlasterror

场景

当 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属性为 True ,当然,如果函数自然不设置最后一个错误,则 SetLastError=True 将被忽略,所以无论如何,无论我使用哪种函数会传递给这个通用的 SafePinvoke 函数,它不会给出“误报”,或者至少我认为不会。

那么,一个使用示例应该是这样的:

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

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

    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function FindWindow(
                            ByVal lpClassName As String,
                            ByVal zero As IntPtr
    ) As IntPtr
    End Function
    
  • 最后,我们使用它。

    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
    

此时我们可以看到一切都按预期工作,如果找到窗口它将返回一个非零Intptr,如果没有找到窗口它将返回一个 Intptr.Zero,并且,如果该函数因空字符串而失败,它将抛出 Win32Exception ,错误代码为 123,其含义为:

ERROR_INVALID_NAME

123 (0x7B)

The filename, directory name, or volume label syntax is incorrect.

一切似乎都很好。

问题

我需要这么说来论证我的问题的原因,我不会造成任何负面影响,但发生的事情是一些非常有经验的程序员说我的函数不安全,因为我对很多事情都错了GetLastWin32Error,在此线程中:

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

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

我想改进或者在需要的情况下完全删除并重新考虑上面的通用函数SafePinvoke的方法,如果它确实无法在“X”情况下按预期工作,只是我希望通过提供可以测试以演示错误/冲突的真实代码示例来了解并了解可能的情况,那么我的问题是:

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

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

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

最佳答案

来自 GetLastError 的文档:

The Return Value section of the documentation for each function that sets the last-error code notes the conditions under which the function sets the last-error code. Most functions that set the thread's last-error code set it when they fail. However, some functions also set the last-error code when they succeed. If the function is not documented to set the last-error code, the value returned by this function is simply the most recent last-error code to have been set; some functions set the last-error code to 0 on success and others do not.

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

关于.net - 解释一下这个功能可能会出现什么问题(如果有的话),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30892127/

相关文章:

vb.net - 为什么 Integer.TryParse 在失败时将结果设置为零?

C# event += delegate {} 在 VB.NET 中等效

c# - 从 MS Access OLE 对象列创建图像文件(使用 C# 或 VB.NET)

c++ - 将结构数据类型传递给 C++ 中的命名管道

c# - Windows Workflow Foundation 4 创建基本事件

c# - 如何使用 TcpClient 对上传进行速率限制?

c# - 如何在 C# 中实现一个 token 系统来限制处理器/IO 繁重的多线程任务的并发性?

c++ - 如何使用 Windows API 获取 "cryptographically strong"随机字节?

c++ - 更改窗口的消息循环线程

c# - 使用用户未安装的字体