2015-08-08 153 views
0

我有一個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 

回答

2

是的,你應該改變在打開文件時您所請求的權限。您正在請求獨佔訪問權限,如果有人因任何原因打開文件,則該權限將失敗。

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