VCS_File.bas 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. Attribute VB_Name = "VCS_File"
  2. Option Compare Database
  3. Option Private Module
  4. Option Explicit
  5. #If VBA7 Then
  6. Private Declare PtrSafe _
  7. Function getTempPath Lib "kernel32" _
  8. Alias "GetTempPathA" (ByVal nBufferLength As Long, _
  9. ByVal lpBuffer As String) As Long
  10. Private Declare PtrSafe _
  11. Function getTempFileName Lib "kernel32" _
  12. Alias "GetTempFileNameA" (ByVal lpszPath As String, _
  13. ByVal lpPrefixString As String, _
  14. ByVal wUnique As Long, _
  15. ByVal lpTempFileName As String) As Long
  16. #Else
  17. Private Declare _
  18. Function getTempPath Lib "kernel32" _
  19. Alias "GetTempPathA" (ByVal nBufferLength As Long, _
  20. ByVal lpBuffer As String) As Long
  21. Private Declare _
  22. Function getTempFileName Lib "kernel32" _
  23. Alias "GetTempFileNameA" (ByVal lpszPath As String, _
  24. ByVal lpPrefixString As String, _
  25. ByVal wUnique As Long, _
  26. ByVal lpTempFileName As String) As Long
  27. #End If
  28. ' --------------------------------
  29. ' Structures
  30. ' --------------------------------
  31. ' Structure to track buffered reading or writing of binary files
  32. Private Type BinFile
  33. file_num As Integer
  34. file_len As Long
  35. file_pos As Long
  36. buffer As String
  37. buffer_len As Integer
  38. buffer_pos As Integer
  39. at_eof As Boolean
  40. mode As String
  41. End Type
  42. ' --------------------------------
  43. ' Basic functions missing from VB 6: buffered file read/write, string builder, encoding check & conversion
  44. ' --------------------------------
  45. ' Open a binary file for reading (mode = 'r') or writing (mode = 'w').
  46. Private Function BinOpen(ByVal file_path As String, ByVal mode As String) As BinFile
  47. Dim f As BinFile
  48. f.file_num = FreeFile
  49. f.mode = LCase$(mode)
  50. If f.mode = "r" Then
  51. Open file_path For Binary Access Read As f.file_num
  52. f.file_len = LOF(f.file_num)
  53. f.file_pos = 0
  54. If f.file_len > &H4000 Then
  55. f.buffer = String$(&H4000, " ")
  56. f.buffer_len = &H4000
  57. Else
  58. f.buffer = String$(f.file_len, " ")
  59. f.buffer_len = f.file_len
  60. End If
  61. f.buffer_pos = 0
  62. Get f.file_num, f.file_pos + 1, f.buffer
  63. Else
  64. DelIfExist file_path
  65. Open file_path For Binary Access Write As f.file_num
  66. f.file_len = 0
  67. f.file_pos = 0
  68. f.buffer = String$(&H4000, " ")
  69. f.buffer_len = 0
  70. f.buffer_pos = 0
  71. End If
  72. BinOpen = f
  73. End Function
  74. ' Buffered read one byte at a time from a binary file.
  75. Private Function BinRead(ByRef f As BinFile) As Integer
  76. If f.at_eof = True Then
  77. BinRead = 0
  78. Exit Function
  79. End If
  80. BinRead = Asc(Mid$(f.buffer, f.buffer_pos + 1, 1))
  81. f.buffer_pos = f.buffer_pos + 1
  82. If f.buffer_pos >= f.buffer_len Then
  83. f.file_pos = f.file_pos + &H4000
  84. If f.file_pos >= f.file_len Then
  85. f.at_eof = True
  86. Exit Function
  87. End If
  88. If f.file_len - f.file_pos > &H4000 Then
  89. f.buffer_len = &H4000
  90. Else
  91. f.buffer_len = f.file_len - f.file_pos
  92. f.buffer = String$(f.buffer_len, " ")
  93. End If
  94. f.buffer_pos = 0
  95. Get f.file_num, f.file_pos + 1, f.buffer
  96. End If
  97. End Function
  98. ' Buffered write one byte at a time from a binary file.
  99. Private Sub BinWrite(ByRef f As BinFile, b As Integer)
  100. Mid(f.buffer, f.buffer_pos + 1, 1) = Chr$(b)
  101. f.buffer_pos = f.buffer_pos + 1
  102. If f.buffer_pos >= &H4000 Then
  103. Put f.file_num, , f.buffer
  104. f.buffer_pos = 0
  105. End If
  106. End Sub
  107. ' Close binary file.
  108. Private Sub BinClose(ByRef f As BinFile)
  109. If f.mode = "w" And f.buffer_pos > 0 Then
  110. f.buffer = Left$(f.buffer, f.buffer_pos)
  111. Put f.file_num, , f.buffer
  112. End If
  113. Close f.file_num
  114. End Sub
  115. ' Binary convert a UCS2-little-endian encoded file to UTF-8.
  116. Public Sub ConvertUcs2Utf8(ByVal Source As String, ByVal dest As String)
  117. Dim f_in As BinFile
  118. Dim f_out As BinFile
  119. Dim in_low As Integer
  120. Dim in_high As Integer
  121. f_in = BinOpen(Source, "r")
  122. f_out = BinOpen(dest, "w")
  123. Do While Not f_in.at_eof
  124. in_low = BinRead(f_in)
  125. in_high = BinRead(f_in)
  126. If in_high = 0 And in_low < &H80 Then
  127. ' U+0000 - U+007F 0LLLLLLL
  128. BinWrite f_out, in_low
  129. ElseIf in_high < &H8 Then
  130. ' U+0080 - U+07FF 110HHHLL 10LLLLLL
  131. BinWrite f_out, &HC0 + ((in_high And &H7) * &H4) + ((in_low And &HC0) / &H40)
  132. BinWrite f_out, &H80 + (in_low And &H3F)
  133. Else
  134. ' U+0800 - U+FFFF 1110HHHH 10HHHHLL 10LLLLLL
  135. BinWrite f_out, &HE0 + ((in_high And &HF0) / &H10)
  136. BinWrite f_out, &H80 + ((in_high And &HF) * &H4) + ((in_low And &HC0) / &H40)
  137. BinWrite f_out, &H80 + (in_low And &H3F)
  138. End If
  139. Loop
  140. BinClose f_in
  141. BinClose f_out
  142. End Sub
  143. ' Binary convert a UTF-8 encoded file to UCS2-little-endian.
  144. Public Sub ConvertUtf8Ucs2(ByVal Source As String, ByVal dest As String)
  145. Dim f_in As BinFile
  146. Dim f_out As BinFile
  147. Dim in_1 As Integer
  148. Dim in_2 As Integer
  149. Dim in_3 As Integer
  150. f_in = BinOpen(Source, "r")
  151. f_out = BinOpen(dest, "w")
  152. Do While Not f_in.at_eof
  153. in_1 = BinRead(f_in)
  154. If (in_1 And &H80) = 0 Then
  155. ' U+0000 - U+007F 0LLLLLLL
  156. BinWrite f_out, in_1
  157. BinWrite f_out, 0
  158. ElseIf (in_1 And &HE0) = &HC0 Then
  159. ' U+0080 - U+07FF 110HHHLL 10LLLLLL
  160. in_2 = BinRead(f_in)
  161. BinWrite f_out, ((in_1 And &H3) * &H40) + (in_2 And &H3F)
  162. BinWrite f_out, (in_1 And &H1C) / &H4
  163. Else
  164. ' U+0800 - U+FFFF 1110HHHH 10HHHHLL 10LLLLLL
  165. in_2 = BinRead(f_in)
  166. in_3 = BinRead(f_in)
  167. BinWrite f_out, ((in_2 And &H3) * &H40) + (in_3 And &H3F)
  168. BinWrite f_out, ((in_1 And &HF) * &H10) + ((in_2 And &H3C) / &H4)
  169. End If
  170. Loop
  171. BinClose f_in
  172. BinClose f_out
  173. End Sub
  174. ' Determine if this database imports/exports code as UCS-2-LE. (Older file
  175. ' formats cause exported objects to use a Windows 8-bit character set.)
  176. Public Function UsingUcs2() As Boolean
  177. Dim obj_name As String
  178. Dim obj_type As Variant
  179. Dim fn As Integer
  180. Dim bytes As String
  181. Dim obj_type_split() As String
  182. Dim obj_type_name As String
  183. Dim obj_type_num As Integer
  184. If CurrentDb.QueryDefs.Count > 0 Then
  185. obj_type_num = acQuery
  186. obj_name = CurrentDb.QueryDefs(0).name
  187. Else
  188. For Each obj_type In Split( _
  189. "Forms|" & acForm & "," & _
  190. "Reports|" & acReport & "," & _
  191. "Scripts|" & acMacro & "," & _
  192. "Modules|" & acModule _
  193. )
  194. DoEvents
  195. obj_type_split = Split(obj_type, "|")
  196. obj_type_name = obj_type_split(0)
  197. obj_type_num = Val(obj_type_split(1))
  198. If CurrentDb.Containers(obj_type_name).Documents.Count > 0 Then
  199. obj_name = CurrentDb.Containers(obj_type_name).Documents(0).name
  200. Exit For
  201. End If
  202. Next
  203. End If
  204. If obj_name = vbNullString Then
  205. ' No objects found that can be used to test UCS2 versus UTF-8
  206. UsingUcs2 = True
  207. Exit Function
  208. End If
  209. Dim tempFileName As String
  210. tempFileName = VCS_File.TempFile()
  211. Application.SaveAsText obj_type_num, obj_name, tempFileName
  212. fn = FreeFile
  213. Open tempFileName For Binary Access Read As fn
  214. bytes = " "
  215. Get fn, 1, bytes
  216. If Asc(Mid$(bytes, 1, 1)) = &HFF And Asc(Mid$(bytes, 2, 1)) = &HFE Then
  217. UsingUcs2 = True
  218. Else
  219. UsingUcs2 = False
  220. End If
  221. Close fn
  222. Dim FSO As Object
  223. Set FSO = CreateObject("Scripting.FileSystemObject")
  224. FSO.DeleteFile (tempFileName)
  225. End Function
  226. ' Generate Random / Unique tempprary file name.
  227. Public Function TempFile(Optional ByVal sPrefix As String = "VBA") As String
  228. Dim sTmpPath As String * 512
  229. Dim sTmpName As String * 576
  230. Dim nRet As Long
  231. Dim sFileName As String
  232. nRet = getTempPath(512, sTmpPath)
  233. nRet = getTempFileName(sTmpPath, sPrefix, 0, sTmpName)
  234. If nRet <> 0 Then sFileName = Left$(sTmpName, InStr(sTmpName, vbNullChar) - 1)
  235. TempFile = sFileName
  236. End Function