我是否正确使用CreateFile/GetFileType/CloseHandle?

问题描述:

我有一个VB6应用程序获取文件路径,并且必须检查此路径是否指向实际文件,而不是LPT设备,管道等。我正在使用API​​ CreateFile,GetFileType,然后CloseHandle。我是否正确使用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 

是的,你应该改变在打开文件时您所请求的权限。您正在请求独占访问权限,如果有人因任何原因打开文件,则该权限将失败。

至于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 

另外,不要让代码进入errHandle调用CloseHandle()后的第一次:

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 
+0

哇,这是尴尬。我甚至没有注意到我每次都会进入错误处理程序。感谢雷米! – user884248