我是否正确使用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
哇,这是尴尬。我甚至没有注意到我每次都会进入错误处理程序。感谢雷米! – user884248