我有一个VB中的winforms项目。2017年净额。我有一个名为LogDataFiles的例程,它使用CreateFile和WriteFile API将一个小数据文件写入磁盘。当从源代码运行时,我可以通过直接通过命令按钮调用LogDataFiles例程,或者通过向消息队列发布消息(这反过来又调用LogDataFiles函数),成功地写入文件。
但是,一旦从可执行文件编译并运行,行为就会发生变化。我仍然可以通过命令按钮直接调用例程来写入文件,但如果我将消息发布到消息队列,然后调用LogDataFiles函数,CreateFile将失败,错误代码为998。函数尝试写入5个文件。第一次尝试时,将写入第一个文件,但所有其他文件在CreateFile上失败,出现错误998。通过消息队列进行的后续尝试全部失败,即使在第一个文件上也是如此。
我需要帮助来弄清楚当逻辑从源代码运行时,为什么逻辑在可执行模式下失败。
这是相关代码。首先是API声明,然后是日志记录例程,然后是队列逻辑。
Private Structure SECURITY_ATTRIBUTES
Dim nLength As Integer
Dim lpSecurityDescriptor As Integer
Dim bInheritHandle As Boolean
End Structure
Private Declare Auto Function CreateFile Lib "kernel32.dll" (ByVal lpFileName As String,
ByVal dwDesiredAccess As Int32, ByVal dwShareMode As Int32, ByRef lpSecurityAttributes As SECURITY_ATTRIBUTES,
ByVal dwCreationDisposition As Int32, ByVal dwFlagsAndAttributes As Int32, ByVal hTemplateFile As IntPtr) As Integer
Private Declare Auto Function CreateFile Lib "kernel32.dll" (ByVal lpFileName As String,
ByVal dwDesiredAccess As Int32, ByVal dwShareMode As Int32, ByRef lpSecurityAttributes As IntPtr,
ByVal dwCreationDisposition As Int32, ByVal dwFlagsAndAttributes As Int32, ByVal hTemplateFile As IntPtr) As Integer
Private Declare Auto Function SetFilePointer Lib "kernel32" (ByVal hFile As Integer, _
ByVal lDistanceToMove As Integer, ByRef lpDistanceToMoveHigh As Integer, _
ByVal dwMoveMethod As Integer) As Long
Private Declare Auto Function ReadFile Lib "Kernel32.dll" ( _
ByVal hndRef As Integer, ByVal lpBuffer As Byte(), _
ByVal numberOfBytesToRead As Integer, ByRef numberOfBytesRead As Integer, ByVal flag As Integer) As Boolean
Private Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Integer) As Boolean
Private Declare Function GetDiskFreeSpaceEx Lib "kernel32" Alias "GetDiskFreeSpaceExA" (ByVal lpRootPathName As String, ByRef lpFreeBytesAvailableToCaller As Long, ByRef lpTotalNumberOfBytes As Long, ByRef lpTotalNumberOfFreeBytes As Long) As Long
Private Declare Auto Function GetLastError Lib "kernel32" () As Long
Private Declare Function WriteFile Lib "kernel32" (
ByVal hTemplateFile As Integer, lpBuffer() As Byte,
ByVal nNumberOfBytesToWrite As Int32,
ByRef lpNumberOfBytesWritten As Int32, ByVal lpOverlapped As Int32) As Integer
Private Declare Function GetDiskFreeSpace Lib "kernel32" Alias "GetDiskFreeSpaceA" _
(ByVal lpRootPathName As String, ByRef lpSectorsPerCluster As UInt32, ByRef lpBytesPerSector As UInt32, ByRef lpNumberOfFreeClusters As UInt32, ByRef lpTtoalNumberOfClusters As UInt32) As Integer
Private Declare Function FlushFileBuffers Lib "kernel32" (ByVal hFile As Integer) As Integer
Private Declare Function GetTickCount Lib "kernel32" Alias "GetTickCount" () As Integer
Private Const GENERIC_WRITE As Long = &H40000000
Private Const GENERIC_READ As Long = &H80000000
Private Const FILE_ATTRIBUTE_NORMAL As Long = &H80
Private Const CREATE_ALWAYS As Long = 2
Private Const OPEN_EXISTING As Long = 3
Private Const OPEN_ALWAYS As Long = 4
Private Const INVALID_HANDLE_VALUE As Long = -1
' CreateFile dwShareMode
Private Const FILE_SHARE_READ As Integer = &H1
Private Const FILE_SHARE_WRITE As Integer = &H2
' Windows file cache related attributes
Private Const WRITE_THROUGH As Long = &H80000000
Private Const NO_BUFFERING As Long = &H20000000
Friend Structure STORAGE_DEVICE_NUMBER
Friend DeviceType As Integer
Friend DeviceNumber As Integer
Friend PartitionNumber As Integer
End Structure
Private Enum EFileAccess As System.Int32
''
'' The following are masks for the predefined standard access types
''
DELETE = &H10000
READ_CONTROL = &H20000
WRITE_DAC = &H40000
WRITE_OWNER = &H80000
SYNCHRONIZE = &H100000
STANDARD_RIGHTS_REQUIRED = &HF0000
STANDARD_RIGHTS_READ = READ_CONTROL
STANDARD_RIGHTS_WRITE = READ_CONTROL
STANDARD_RIGHTS_EXECUTE = READ_CONTROL
STANDARD_RIGHTS_ALL = &H1F0000
SPECIFIC_RIGHTS_ALL = &HFFFF
''
'' AccessSystemAcl access type
''
ACCESS_SYSTEM_SECURITY = &H1000000
''
'' MaximumAllowed access type
''
MAXIMUM_ALLOWED = &H2000000
''
'' These are the generic rights.
''
GENERIC_READ = &H80000000
GENERIC_WRITE = &H40000000
GENERIC_EXECUTE = &H20000000
GENERIC_ALL = &H10000000
End Enum
Private Enum EFileShare
FILE_SHARE_NONE = &H0
FILE_SHARE_READ = &H1
FILE_SHARE_WRITE = &H2
FILE_SHARE_DELETE = &H4
End Enum
Private Enum ECreationDisposition
''' <summary>
''' Creates a new file, only if it does not already exist.
''' If the specified file exists, the function fails and the last-error code is set to ERROR_FILE_EXISTS (80).
''' If the specified file does not exist and is a valid path to a writable location, a new file is created.
''' </summary>
CREATE_NEW = 1
''' <summary>
''' Creates a new file, always.
''' If the specified file exists and is writable, the function overwrites the file, the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS (183).
''' If the specified file does not exist and is a valid path, a new file is created, the function succeeds, and the last-error code is set to zero.
''' For more information, see the Remarks section of this topic.
''' </summary>
CREATE_ALWAYS = 2
''' <summary>
''' Opens a file or device, only if it exists.
''' If the specified file or device does not exist, the function fails and the last-error code is set to ERROR_FILE_NOT_FOUND (2).
''' For more information about devices, see the Remarks section.
''' </summary>
OPEN_EXISTING = 3
''' <summary>
''' Opens a file, always.
''' If the specified file exists, the function succeeds and the last-error code is set to ERROR_ALREADY_EXISTS (183).
''' If the specified file does not exist and is a valid path to a writable location, the function creates a file and the last-error code is set to zero.
''' </summary>
OPEN_ALWAYS = 4
''' <summary>
''' Opens a file and truncates it so that its size is zero bytes, only if it exists.
''' If the specified file does not exist, the function fails and the last-error code is set to ERROR_FILE_NOT_FOUND (2).
''' The calling process must open the file with the GENERIC_WRITE bit set as part of the dwDesiredAccess parameter.
''' </summary>
TRUNCATE_EXISTING = 5
End Enum
Private Enum EFileAttributes
FILE_ATTRIBUTE_READONLY = &H1
FILE_ATTRIBUTE_HIDDEN = &H2
FILE_ATTRIBUTE_SYSTEM = &H4
FILE_ATTRIBUTE_DIRECTORY = &H10
FILE_ATTRIBUTE_ARCHIVE = &H20
FILE_ATTRIBUTE_DEVICE = &H40
FILE_ATTRIBUTE_NORMAL = &H80
FILE_ATTRIBUTE_TEMPORARY = &H100
FILE_ATTRIBUTE_SPARSE_FILE = &H200
FILE_ATTRIBUTE_REPARSE_POINT = &H400
FILE_ATTRIBUTE_COMPRESSED = &H800
FILE_ATTRIBUTE_OFFLINE = &H1000
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = &H2000
FILE_ATTRIBUTE_ENCRYPTED = &H4000
FILE_ATTRIBUTE_VIRTUAL = &H10000
'This parameter can also contain combinations of flags (FILE_FLAG_*)
FILE_FLAG_BACKUP_SEMANTICS = &H2000000
FILE_FLAG_DELETE_ON_CLOSE = &H4000000
FILE_FLAG_NO_BUFFERING = &H20000000
FILE_FLAG_OPEN_NO_RECALL = &H100000
FILE_FLAG_OPEN_REPARSE_POINT = &H200000
FILE_FLAG_OVERLAPPED = &H40000000
FILE_FLAG_POSIX_SEMANTICS = &H1000000
FILE_FLAG_RANDOM_ACCESS = &H10000000
FILE_FLAG_SEQUENTIAL_SCAN = &H8000000
FILE_FLAG_WRITE_THROUGH = &H80000000
End
Enum
Sub LogDataFiles()
Dim i As Integer
For i = 0 To 5
Call WriteFileData(i)
Next
End Sub
Sub WriteFileData(ByVal indexNo As Integer)
Dim strFileName As String
Dim bf As New BinaryFormatter
Dim tmpStream As New MemoryStream
Dim bytArray() As Byte
'data inside defaults to all FF's
Dim tmpStorage As New clsDataStorage
Dim blnResult As Boolean
Dim strTemp As String
Try
strFileName = dataPath & "File_" & indexNo.ToString & ".dat"
'In real app, tmpStorage would be a more complex class so data is serialized
'to allow it to be put into byte array
'Must serialize the data first
bf.Serialize(tmpStream, tmpStorage)
bytArray = tmpStream.ToArray
tmpStream.Close()
Call testIO.Write_Serialized_Data_To_File(bytArray, strFileName)
blnResult = testIO.Check_File_Contents_By_CRC(strFileName)
If blnResult = True Then
strTemp = strFileName & vbTab & vbTab & "CRC Okay"
Else
strTemp = strFileName & vbTab & vbTab & "** CRC ERROR **"
End If
Call AddToList(strTemp)
'error handling
Catch ex As Exception
Call LogError(ex)
Finally
tmpStream = Nothing
bf = Nothing
bytArray = Nothing
tmpStorage = Nothing
End Try
End
Sub
Sub Write_Serialized_Data_To_File(ByVal bytSerializedData() As Byte, ByVal strFileName As String)
Dim lHandle As Integer
Dim i As Integer
Dim iBytesWritten As Integer
Dim iResult As Integer
Dim bytArray() As Byte
Dim bytCRC() As Byte
Dim intUBound As Integer
Dim arrLogged() As Byte
Dim bytTemp As Byte
Dim blnExistsAlready As Boolean = False
Dim intLoopCounter As Integer = 0
Dim lngError As Long
Dim lpSA As SECURITY_ATTRIBUTES
Try
intUBound = bytSerializedData.GetUpperBound(0)
ReDim bytArray(intUBound)
For i = 0 To intUBound
bytTemp = bytSerializedData(i)
bytArray(i) = bytTemp
Next
'check to see if file exists first
Dim strFileExists As String = " "
strFileExists = Dir(strFileName)
strFileExists = Trim$(strFileExists)
If Len(strFileExists) = 0 Then
'file does not exist
blnExistsAlready = False
ElseIf (Len(strFileExists) > 0) Then
blnExistsAlready = True
End If
''open the file
lpSA.nLength = Len(lpSA)
If blnExistsAlready = True Then
'open but do not create file
lHandle = CreateFile(strFileName, GENERIC_WRITE, 0,
lpSA, OPEN_EXISTING, NO_BUFFERING, IntPtr.Zero)
Debug.WriteLine("Write handle exists is = " & lHandle.ToString)
ElseIf blnExistsAlready = False Then
'create new file
lHandle = CreateFile(strFileName, GENERIC_WRITE, 0,
lpSA, CREATE_ALWAYS, NO_BUFFERING, IntPtr.Zero)
Debug.WriteLine("Write handle create is = " & lHandle.ToString)
End If
Debug.WriteLine("Write handle create is = " & lHandle.ToString)
lngError = GetLastError()
If lngError > 0 Then
Debug.WriteLine("B after write last error is " & lngError.ToString)
MsgBox("CreateFile Failure for " & strFileName & " -Error Code: " & lngError.ToString)
Else
'add CRC bytes to bytArray before logging to disk
intUBound = bytArray.GetUpperBound(0)
'get crc bytes
bytCRC = CRC_CalcCRC(bytArray)
'add crc bytes to array
ReDim Preserve bytArray(intUBound + 2) 'for 2 crc bytes
bytArray(intUBound + 1) = bytCRC(1)
bytArray(intUBound + 2) = bytCRC(0)
ReDim arrLogged(intUBound + 2 + 4)
For i = (intUBound + 2 + 4) To 4 Step -1
arrLogged(i) = bytArray(i - 4)
Next i
'add upper bound of data array including CRC bytes to front of log before passing
arrLogged = Convert_Long_To_Binary_Array_LSB_First((intUBound + 2), 0, 4, arrLogged)
iResult = CInt(WriteFile(lHandle, arrLogged, Convert.ToInt32(Math.Ceiling(arrLogged.Length / SRAM_Drive_SectorSize) * SRAM_Drive_SectorSize), iBytesWritten, 0))
'Call CloseHandle(lHandle)
End If
'error handling
Catch ex As Exception
Call LogError(ex)
Finally
If lHandle <> INVALID_HANDLE_VALUE Then
Call CloseHandle(lHandle)
'MsgBox("close handle " & lHandle.ToString)
End If
End Try
End Sub
以下是队列相关逻辑:
Private Sub RxQueue_ReceiveCompleted(sender As Object, e As ReceiveCompletedEventArgs) Handles RxQueue.ReceiveCompleted
Try
Dim qMessage As Message = RxQueue.EndReceive(e.AsyncResult)
Dim qBody As structEvent
qBody = CType(qMessage.Body, structEvent)
Call UpdateUI(qBody)
RxQueue.BeginReceive()
Return
Catch ex As Exception
Call LogError(ex)
End Try
End Sub
Public Sub Load_RxQueue()
Try
With RxQueue
.Path = nameOfQueue
.Formatter = New XmlMessageFormatter(New Type() {GetType(structEvent)})
'.EnableConnectionCache = True
'purge any existing messages currently in queue
.Purge()
'.BeginReceive()
End With
Catch ex As Exception
Call LogError(ex)
End Try
End Sub
Public Sub Start_RxQueue()
Call Load_RxQueue()
Me.RxQueue.BeginReceive()
End Sub
Delegate Sub UpdateUIHandler(ByVal objQueueDataFields As structEvent)
Sub UpdateUI(ByVal objQueueDataFields As structEvent)
Try
'check to see if thread switch is required
If Me.InvokeRequired = True Then
'switch control over to the primary UI thread
Dim handler As New UpdateUIHandler(AddressOf UpdateUI_Impl)
Dim args() As Object = {objQueueDataFields}
'call begin invoke method of form object
Me.BeginInvoke(handler, args)
Else
Call UpdateUI_Impl(objQueueDataFields)
End If
Catch ex As Exception
Call LogError(ex)
End Try
End Sub
Sub UpdateUI_Impl(ByVal qData As structEvent)
Try
Call DecodeUI(qData)
Catch ex As Exception
Call LogError(ex)
End Try
End Sub
Sub DecodeUI(ByVal qDecodeData As structEvent)
Try
Select Case qDecodeData.inputNo
Case 0
'list files to screen
Call List_Files()
Case 1
Call AddToList("confirm pressed")
Call LogDataFiles()
'list files to screen with CRC checked
'Call List_Files()
Call AddToList("Finished")
Case 2
'delete existing files
Call Delete_Files()
Case 100
'initial read of existing files
Me.lstData.Items.Clear()
Call List_Files()
End Select
Catch ex As Exception
Call LogError(ex)
End Try
End Sub