【问题标题】:Am I using CreateFile/GetFileType/CloseHandle incorrectly?我是否错误地使用了 CreateFile/GetFileType/CloseHandle?
【发布时间】:2015-08-08 08:14:42
【问题描述】:

我有一个 VB6 应用程序,它获取文件路径并且必须检查此路径是否指向实际文件,而不是 LPT 设备、管道等。我使用 API CreateFile、GetFileType 和 CloseHandle。

虽然这段代码有效,但当我将调试器附加到程序时,调试器会因异常而中断,表示 CloseHandle 正在尝试关闭不存在的句柄(或者,有时会说“无效参数”)。即使我检查(从代码中可以看到)句柄 INVALID_HANDLE_VALUE,也会发生这种情况。

我有两个问题。

首先,我想我需要更改对 CreateFile 的调用,从

handle = CreateFile(FilePath, 0, 0, ByVal 0&, OPEN_EXISTING, 0&, 0&)

...到

handle = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE, 0&, OPEN_EXISTING, FILE_ATTRIBUTES_ENUM.FA_NORMAL, 0&)

我对此是否正确?我只使用 CreateFile 来获取传递给 GetFileType 的句柄。我实际上并没有对这个文件做任何事情。

其次,为什么会发生这种情况?我正在检查句柄 INVALID_HANDLE_VALUE,但仍然收到此错误!

转储的结果附在代码之后。

Private Const OPEN_EXISTING         As Long = 3
Private Const FILE_SHARE_READ       As Long = &H1
Private Const INVALID_HANDLE_VALUE  As Long = -1

Private Const FILE_TYPE_DISK        As Long = &H1
Private Const FILE_TYPE_CHAR        As Long = &H2
Private Const FILE_TYPE_PIPE        As Long = &H3
Private Const FILE_TYPE_REMOTE      As Long = &H8000
Private Const FILE_TYPE_UNKNOWN     As Long = &H0

Private Declare Function CloseHandle_API Lib "kernel32" Alias "CloseHandle" (ByVal hObject As Long) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function GetFileType_API Lib "kernel32" Alias "GetFileType" (ByVal hFile As Long) As Long

Private Function IsAnActualFile(FilePath As String, fileType As Long) As Boolean
    On Error GoTo errHandle

    Dim handle                  As Long
    Dim lpSecurityAttributes    As Long

    lpSecurityAttributes = 0
    handle = CreateFile(FilePath, 0, 0, ByVal 0&, OPEN_EXISTING, 0&, 0&)
    If handle <> INVALID_HANDLE_VALUE Then
        fileType = GetFileType_API(handle)
        If fileType = FILE_TYPE_DISK Then
            IsAnActualFile = True
        End If
        CloseHandle_API handle
    End If

errHandle:
    If handle <> INVALID_HANDLE_VALUE Then CloseHandle_API handle
End Function

这里是转储:

0:000> kb
ChildEBP RetAddr  Args to Child              
0018edf0 74ebc463 000038ec 000038ec 0018ee10 ntdll!NtClose+0x12
0018ee00 75141418 000038ec 00000001 0018ee68 KERNELBASE!CloseHandle+0x2d
0018ee10 1107ee38 000038ec 0018f32c 00000000 kernel32!CloseHandleImplementation+0x3f
0018ee68 1107d3ef 19cb5968 0018f32c 0018f158 MyProgram!Document::IsAnActualFile+0xb8

【问题讨论】:

    标签: winapi


    【解决方案1】:

    是的,您应该在打开文件时更改您请求的权限。您正在请求独占访问,如果有人出于任何原因打开了文件,这将失败。

    至于CloseHandle()错误,当文件成功打开时,你正在关闭文件句柄两次。始终输入您的 errHandle,但您在第一次关闭变量后没有重置它:

    Private Function IsAnActualFile(FilePath As String) As Boolean
        On Error GoTo errHandle
    
        Dim handle As Long
    
        handle = CreateFile(FilePath, GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE Or FILE_SHARE_DELETE, 0&, OPEN_EXISTING, FILE_ATTRIBUTES_ENUM.FA_NORMAL, 0&)
        If handle <> INVALID_HANDLE_VALUE Then
            fileType = GetFileType_API(handle)
            If fileType = FILE_TYPE_DISK Then
                IsAnActualFile = True
            End If
            CloseHandle_API handle
            handle = INVALID_HANDLE_VALUE ' <-- add this!
        End If
    
    errHandle:
        If handle <> INVALID_HANDLE_VALUE Then CloseHandle_API handle
    End Function
    

    或者,在第一次调用CloseHandle() 之后不要让代码进入errHandle:

    Private Function IsAnActualFile(FilePath As String, fileType As Long) As Boolean
        On Error GoTo errHandle
    
        Dim handle As Long
    
        handle = CreateFile(FilePath, GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE Or FILE_SHARE_DELETE, 0&, OPEN_EXISTING, FILE_ATTRIBUTES_ENUM.FA_NORMAL, 0&)
        If handle <> INVALID_HANDLE_VALUE Then
            fileType = GetFileType_API(handle)
            If fileType = FILE_TYPE_DISK Then
                IsAnActualFile = True
            End If
            CloseHandle_API handle
            Exit Function ' <-- add this!
        End If
    
    errHandle:
        If handle <> INVALID_HANDLE_VALUE Then CloseHandle_API handle
    End Function
    

    话虽如此,您可能还想考虑如果调用者传入目录或根驱动器的路径而不是实际文件会发生什么。 CreateFile() 仍然可以成功,并且 GetFileType() 很可能仍然返回 FILE_TYPE_DISK,因为它在硬盘上。

    更简单的解决方案是使用GetFileAttributes() 而不是CreateFile(),这样您就不必实际打开文件了。 GetFileAttributes() 只能查询硬盘上的文件和目录,否则会失败。所以也不需要GetFileType()。

    Private Const FILE_ATTRIBUTE_DIRECTORY as Long = &H10
    Private Const FILE_ATTRIBUTE_DEVICE as Long = &H40
    Private Const FILE_ATTRIBUTE_REPARSE_POINT as Long = &H400
    
    Private Const INVALID_FILE_ATTRIBUTES as Long = -1
    
    Private Declare Function GetFileAttributes_API Lib "kernel32" Alias "GetFileAttributesA" (ByVal lpFileName As String) As Long
    
    Private Function IsAnActualFile(FilePath As String) As Boolean
        Dim attrs As Long
    
        attrs = GetFileAttributes_API(FilePath)
        If attrs <> INVALID_FILE_ATTRIBUTES Then
            If (attrs And (FILE_ATTRIBUTE_DIRECTORY Or FILE_ATTRIBUTE_DEVICE Or FILE_ATTRIBUTE_REPARSE_POINT)) = 0 Then
                IsAnActualFile = True
            End If
        End If
    End Function
    

    【讨论】:

    • 哇,好尴尬。我什至没有注意到我每次都进入错误处理程序。谢谢雷米!
    猜你喜欢
    • 1970-01-01
    • 2014-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多