【问题标题】:Explain what problems could have this function (if any)说明此功能可能会出现哪些问题(如果有)
【发布时间】:2015-06-17 12:49:40
【问题描述】:

场景

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

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

  • 首先,我们寻找管理最后一个错误的 API func,例如 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函数是否真的安全,或者不安全,或者是否可以改进,以及,提供一个可以测试的代码示例?。

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

【问题讨论】:

  • 我对此不确定,但DllImportAttribute.SetLastError 不在那里,因此.NET 运行时可以抛出带有错误代码的Win32Exception(如果设置了错误代码)?也就是说,在自定义属性标志的帮助下,.NET 运行时不是已经精确地完成了您在 SafePInvoke 方法中所做的事情吗?
  • @stakx SetLastError=true 导致 p/invoke 框架立即调用 GetLastError 外部函数返回。记录此错误状态,然后由GetLastWin32Error 返回。
  • @DavidHeffernan:感谢您的解释。 (看来我一直把这与 COM 互操作混淆,默认情况下运行时会自动将指示错误情况的 HRESULTs 转换为 ComExceptions。我认为 CLR 会对非 COM 平台调用执行类似的操作。)
  • 首先,您的使用方法不完整——因为您的方法在exitCode &lt;&gt; 0 时明确抛出异常,客户端代码需要使用 Try/Catch。这并没有使 PInvoke 更容易,IMO。接下来,您需要绝对确定exitCode 是正确的,这样才能有用,而您不是,或者您不会发布此内容。接下来,result 有时是数据(如 hwnd 或 text len),有时是 T/F 失败代码。每个 WinAPI 函数都定义了它自己的返回值,但是这种大小适合所有方法并不能说明这一点。重新阅读 Anton Tykhyy 的 cmets 链接到,

标签: .net vb.net winapi pinvoke getlasterror


【解决方案1】:

来自GetLastError的文档:

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

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

【讨论】:

  • 现在,我明白了,只是我没有阅读的那部分文档。感谢您的解释,并对最初的讨论感到抱歉,我知道我错了。但是,无论如何,您知道设置非零错误代码的函数的名称吗?以您的经验,您可以说这些功能很少,或者通常非常需要考虑?
  • 有些函数成功并将最后一个错误设置为非零。喜欢CreateMutex。我在上一个问题中指出了一些示例,其中函数失败,但GetLastError 返回0。还有其他成功的函数,GetLastError 返回0,但我不知道有什么例子。你看,我没有无条件拨打GetLastError的习惯,所以我从来没有看到这种情况发生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-14
  • 2019-07-26
相关资源
最近更新 更多