1 #Region "Microsoft.VisualBasic::d0734ca516af4aa5879f9cebd251761f, Microsoft.VisualBasic.Core\ApplicationServices\Tools\Win32\Win32File.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Module Win32File
35     
36     '         Function: CreateFileW, GetAccess, GetMode, GetShare, (+3 Overloads) Open
37     '                   OpenRead, OpenWrite, ReadAllText, SetFilePointer
38     
39     
40     ' /********************************************************************************/
41
42 #End Region
43
44 Imports System.Collections.Generic
45 Imports System.Text
46 Imports Microsoft.Win32.SafeHandles
47 Imports System.Runtime.InteropServices
48 Imports System.Runtime.InteropServices.Marshal
49 Imports System.IO
50
51 Namespace Win32
52
53     ''' <summary>
54     ''' .NET 2.0 Workaround for PathTooLongException
55     ''' </summary>
56     ''' <remarks>
57     ''' http://www.codeproject.com/Articles/22013/NET-Workaround-for-PathTooLongException
58     ''' </remarks>
59     Public Module Win32File
60
61         ''' <summary>
62         ''' Error
63         ''' </summary>
64         Const ERROR_ALREADY_EXISTS As Integer = 183
65         ' seek location
66         Const FILE_BEGIN As UInteger = &H0
67         Const FILE_CURRENT As UInteger = &H1
68         Const FILE_END As UInteger = &H2
69
70
71         ' access
72         Const GENERIC_READ As UInteger = &H80000000UI
73         Const GENERIC_WRITE As UInteger = &H40000000
74         Const GENERIC_EXECUTE As UInteger = &H20000000
75         Const GENERIC_ALL As UInteger = &H10000000
76
77         Const FILE_APPEND_DATA As UInteger = &H4
78
79         ' attribute
80         Const FILE_ATTRIBUTE_NORMAL As UInteger = &H80
81
82         ' share
83         Const FILE_SHARE_DELETE As UInteger = &H4
84         Const FILE_SHARE_READ As UInteger = &H1
85         Const FILE_SHARE_WRITE As UInteger = &H2
86
87         'mode
88         Const CREATE_NEW As UInteger = 1
89         Const CREATE_ALWAYS As UInteger = 2
90         Const OPEN_EXISTING As UInteger = 3
91         Const OPEN_ALWAYS As UInteger = 4
92         Const TRUNCATE_EXISTING As UInteger = 5
93
94
95         <DllImport("kernel32.dll"SetLastError:=True, CharSet:=CharSet.Unicode)>
96         Private Function CreateFileW(lpFileName As String, dwDesiredAccess As UInteger, dwShareMode As UInteger, lpSecurityAttributes As IntPtr, dwCreationDisposition As UInteger, dwFlagsAndAttributes As UInteger, hTemplateFile As IntPtr) As SafeFileHandle
97         End Function
98
99         <DllImport("kernel32.dll"SetLastError:=True, CharSet:=CharSet.Unicode)>
100         Private Function SetFilePointer(hFile As SafeFileHandle, lDistanceToMove As Long, lpDistanceToMoveHigh As IntPtr, dwMoveMethod As UIntegerAs UInteger
101         End Function
102
103 #Region "GetMode"
104
105         ''' <summary>
106         ''' Converts the filemode constant to win32 constant
107         ''' </summary>
108         ''' <param name="mode"></param>
109         ''' <returns></returns>
110         Private Function GetMode(mode As FileMode) As UInteger
111             Dim umode As UInteger = 0
112             Select Case mode
113                 Case FileMode.CreateNew
114                     umode = CREATE_NEW
115
116                 Case FileMode.Create
117                     umode = CREATE_ALWAYS
118
119                 Case FileMode.Append
120                     umode = OPEN_ALWAYS
121
122                 Case FileMode.Open
123                     umode = OPEN_EXISTING
124
125                 Case FileMode.OpenOrCreate
126                     umode = OPEN_ALWAYS
127
128                 Case FileMode.Truncate
129                     umode = TRUNCATE_EXISTING
130
131             End Select
132             Return umode
133         End Function
134 #End Region
135
136         Public Function ReadAllText(path As String, encoding As System.Text.Encoding) As String
137             On Error Resume Next
138
139             Dim FileStream = Win32File.OpenRead(path)
140             Dim ChunkBuffer As Byte() = New Byte(FileStream.Length - 1) {}
141             Call FileStream.Read(ChunkBuffer, 0, ChunkBuffer.Length)
142             Return encoding.GetString(ChunkBuffer)
143         End Function
144
145 #Region "GetAccess"
146
147         ''' <summary>
148         ''' Converts the FileAccess constant to win32 constant
149         ''' </summary>
150         ''' <param name="access"></param>
151         ''' <returns></returns>
152         Private Function GetAccess(access As FileAccess) As UInteger
153             Dim uaccess As UInteger = 0
154             Select Case access
155                 Case FileAccess.Read
156                     uaccess = GENERIC_READ
157
158                 Case FileAccess.ReadWrite
159                     uaccess = GENERIC_READ Or GENERIC_WRITE
160
161                 Case FileAccess.Write
162                     uaccess = GENERIC_WRITE
163
164             End Select
165             Return uaccess
166         End Function
167 #End Region
168
169 #Region "GetShare"
170
171         ''' <summary>
172         ''' Converts the FileShare constant to win32 constant
173         ''' </summary>
174         ''' <param name="share"></param>
175         ''' <returns></returns>
176         ''' <remarks></remarks>
177         Private Function GetShare(share As FileShare) As UInteger
178             Dim ushare As UInteger = 0
179             Select Case share
180                 Case FileShare.Read
181                     ushare = FILE_SHARE_READ
182
183                 Case FileShare.ReadWrite
184                     ushare = FILE_SHARE_READ Or FILE_SHARE_WRITE
185
186                 Case FileShare.Write
187                     ushare = FILE_SHARE_WRITE
188
189                 Case FileShare.Delete
190                     ushare = FILE_SHARE_DELETE
191
192                 Case FileShare.None
193                     ushare = 0
194
195             End Select
196             Return ushare
197         End Function
198 #End Region
199
200         Const ushare As UInteger = 0
201
202         Public Function Open(filepath As String, mode As FileMode) As FileStream
203             'opened in the specified mode and path, with read/write access and not shared
204             Dim fs As FileStream = Nothing
205             Dim umode As UInteger = GetMode(mode)
206             Dim uaccess As UInteger = GENERIC_READ Or GENERIC_WRITE
207
208             'not shared
209             If mode = FileMode.Append Then
210                 uaccess = FILE_APPEND_DATA
211             End If
212             If file path is disk file path then prepend it with \\?\
213             ' if file path is UNC prepend it with \\?\UNC\ and remove \\ prefix in unc path.
214             If filepath.StartsWith("\\"Then
215                 filepath = "\\?\UNC\" & filepath.Substring(2, filepath.Length - 2)
216             Else
217                 filepath = "\\?\" & filepath
218             End If
219             Dim sh As SafeFileHandle = CreateFileW(filepath, uaccess, ushare, IntPtr.Zero, umode, FILE_ATTRIBUTE_NORMAL,
220             IntPtr.Zero)
221             Dim iError As Integer = GetLastWin32Error()
222             If (iError > 0 AndAlso Not (mode = FileMode.Append AndAlso iError = ERROR_ALREADY_EXISTS)) OrElse sh.IsInvalid Then
223                 Throw New Exception("Error opening file Win32 Error:" & iError)
224             Else
225                 fs = New FileStream(sh, FileAccess.ReadWrite)
226             End If
227
228             ' if opened in append mode
229             If mode = FileMode.Append Then
230                 If Not sh.IsInvalid Then
231                     SetFilePointer(sh, 0, IntPtr.Zero, FILE_END)
232                 End If
233             End If
234
235             Return fs
236         End Function
237
238         Public Function Open(filepath As String, mode As FileMode, access As FileAccess) As FileStream
239             'opened in the specified mode and access and not shared
240             Dim fs As FileStream = Nothing
241             Dim umode As UInteger = GetMode(mode)
242             Dim uaccess As UInteger = GetAccess(access)
243
244             'not shared
245             If mode = FileMode.Append Then
246                 uaccess = FILE_APPEND_DATA
247             End If
248             If file path is disk file path then prepend it with \\?\
249             ' if file path is UNC prepend it with \\?\UNC\ and remove \\ prefix in unc path.
250             If filepath.StartsWith("\\"Then
251                 filepath = "\\?\UNC\" & filepath.Substring(2, filepath.Length - 2)
252             Else
253                 filepath = "\\?\" & filepath
254             End If
255             Dim sh As SafeFileHandle = CreateFileW(filepath, uaccess, ushare, IntPtr.Zero, umode, FILE_ATTRIBUTE_NORMAL,
256             IntPtr.Zero)
257             Dim iError As Integer = GetLastWin32Error()
258             If (iError > 0 AndAlso Not (mode = FileMode.Append AndAlso iError <> ERROR_ALREADY_EXISTS)) OrElse sh.IsInvalid Then
259                 Throw New Exception("Error opening file Win32 Error:" & iError)
260             Else
261                 fs = New FileStream(sh, access)
262             End If
263             ' if opened in append mode
264             If mode = FileMode.Append Then
265                 If Not sh.IsInvalid Then
266                     SetFilePointer(sh, 0, IntPtr.Zero, FILE_END)
267                 End If
268             End If
269             Return fs
270
271         End Function
272
273         Public Function Open(filepath As String, mode As FileMode, access As FileAccess, share As FileShare) As FileStream
274             'opened in the specified mode , access and  share
275             Dim fs As FileStream = Nothing
276             Dim umode As UInteger = GetMode(mode)
277             Dim uaccess As UInteger = GetAccess(access)
278             Dim ushare As UInteger = GetShare(share)
279             If mode = FileMode.Append Then
280                 uaccess = FILE_APPEND_DATA
281             End If
282             If file path is disk file path then prepend it with \\?\
283             ' if file path is UNC prepend it with \\?\UNC\ and remove \\ prefix in unc path.
284             If filepath.StartsWith("\\"Then
285                 filepath = "\\?\UNC\" & filepath.Substring(2, filepath.Length - 2)
286             Else
287                 filepath = "\\?\" & filepath
288             End If
289             Dim sh As SafeFileHandle = CreateFileW(filepath, uaccess, ushare, IntPtr.Zero, umode, FILE_ATTRIBUTE_NORMAL,
290             IntPtr.Zero)
291             Dim iError As Integer = GetLastWin32Error()
292             If (iError > 0 AndAlso Not (mode = FileMode.Append AndAlso iError <> ERROR_ALREADY_EXISTS)) OrElse sh.IsInvalid Then
293                 Throw New Exception("Error opening file Win32 Error:" & iError)
294             Else
295                 fs = New FileStream(sh, access)
296             End If
297             ' if opened in append mode
298             If mode = FileMode.Append Then
299                 If Not sh.IsInvalid Then
300                     SetFilePointer(sh, 0, IntPtr.Zero, FILE_END)
301                 End If
302             End If
303             Return fs
304         End Function
305
306         ''' <summary>
307         ''' Open readonly file mode open(String, FileMode.Open, FileAccess.Read, FileShare.Read)
308         ''' </summary>
309         ''' <param name="filepath"></param>
310         ''' <returns></returns>
311         ''' <remarks></remarks>
312         Public Function OpenRead(filepath As StringAs FileStream
313             Return Open(filepath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read)
314         End Function
315
316         ''' <summary>
317         ''' open writable open(String, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None).
318         ''' </summary>
319         ''' <param name="filepath"></param>
320         ''' <returns></returns>
321         ''' <remarks></remarks>
322         Public Function OpenWrite(filepath As StringAs FileStream
323             Return Open(filepath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)
324         End Function
325     End Module
326 End Namespace