Mercurial > foo_out_sdl
comparison foosdk/sdk/foobar2000/helpers/file_win32_wrapper.cpp @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #include "StdAfx.h" | |
| 2 | |
| 3 #ifdef _WIN32 | |
| 4 | |
| 5 #include "file_win32_wrapper.h" | |
| 6 | |
| 7 namespace file_win32_helpers { | |
| 8 t_filesize get_size(HANDLE p_handle) { | |
| 9 LARGE_INTEGER v = {}; | |
| 10 WIN32_IO_OP(GetFileSizeEx(p_handle, &v)); | |
| 11 return make_uint64(v); | |
| 12 } | |
| 13 void seek(HANDLE p_handle,t_sfilesize p_position,file::t_seek_mode p_mode) { | |
| 14 union { | |
| 15 t_int64 temp64; | |
| 16 struct { | |
| 17 DWORD temp_lo; | |
| 18 LONG temp_hi; | |
| 19 }; | |
| 20 }; | |
| 21 | |
| 22 temp64 = p_position; | |
| 23 SetLastError(ERROR_SUCCESS); | |
| 24 temp_lo = SetFilePointer(p_handle,temp_lo,&temp_hi,(DWORD)p_mode); | |
| 25 if (GetLastError() != ERROR_SUCCESS) exception_io_from_win32(GetLastError()); | |
| 26 } | |
| 27 | |
| 28 void fillOverlapped(OVERLAPPED & ol, HANDLE myEvent, t_filesize s) { | |
| 29 ol.hEvent = myEvent; | |
| 30 ol.Offset = (DWORD)( s & 0xFFFFFFFF ); | |
| 31 ol.OffsetHigh = (DWORD)(s >> 32); | |
| 32 } | |
| 33 | |
| 34 void writeOverlappedPass(HANDLE handle, HANDLE myEvent, t_filesize position, const void * in,DWORD inBytes, abort_callback & abort) { | |
| 35 abort.check(); | |
| 36 if (inBytes == 0) return; | |
| 37 OVERLAPPED ol = {}; | |
| 38 fillOverlapped(ol, myEvent, position); | |
| 39 ResetEvent(myEvent); | |
| 40 DWORD bytesWritten; | |
| 41 SetLastError(NO_ERROR); | |
| 42 if (WriteFile( handle, in, inBytes, &bytesWritten, &ol)) { | |
| 43 // succeeded already? | |
| 44 if (bytesWritten != inBytes) throw exception_io(); | |
| 45 return; | |
| 46 } | |
| 47 | |
| 48 { | |
| 49 const DWORD code = GetLastError(); | |
| 50 if (code != ERROR_IO_PENDING) exception_io_from_win32(code); | |
| 51 } | |
| 52 const HANDLE handles[] = {myEvent, abort.get_abort_event()}; | |
| 53 SetLastError(NO_ERROR); | |
| 54 DWORD state = WaitForMultipleObjects(_countof(handles), handles, FALSE, INFINITE); | |
| 55 if (state == WAIT_OBJECT_0) { | |
| 56 try { | |
| 57 WIN32_IO_OP( GetOverlappedResult(handle,&ol,&bytesWritten,TRUE) ); | |
| 58 } catch(...) { | |
| 59 CancelIo(handle); | |
| 60 throw; | |
| 61 } | |
| 62 if (bytesWritten != inBytes) throw exception_io(); | |
| 63 return; | |
| 64 } | |
| 65 CancelIo(handle); | |
| 66 throw exception_aborted(); | |
| 67 } | |
| 68 | |
| 69 void writeOverlapped(HANDLE handle, HANDLE myEvent, t_filesize & position, const void * in, size_t inBytes, abort_callback & abort) { | |
| 70 enum {writeMAX = 16*1024*1024}; | |
| 71 size_t done = 0; | |
| 72 while(done < inBytes) { | |
| 73 size_t delta = inBytes - done; | |
| 74 if (delta > writeMAX) delta = writeMAX; | |
| 75 writeOverlappedPass(handle, myEvent, position, (const BYTE*)in + done, (DWORD) delta, abort); | |
| 76 done += delta; | |
| 77 position += delta; | |
| 78 } | |
| 79 } | |
| 80 void writeStreamOverlapped(HANDLE handle, HANDLE myEvent, const void * in, size_t inBytes, abort_callback & abort) { | |
| 81 enum {writeMAX = 16*1024*1024}; | |
| 82 size_t done = 0; | |
| 83 while(done < inBytes) { | |
| 84 size_t delta = inBytes - done; | |
| 85 if (delta > writeMAX) delta = writeMAX; | |
| 86 writeOverlappedPass(handle, myEvent, 0, (const BYTE*)in + done, (DWORD) delta, abort); | |
| 87 done += delta; | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 DWORD readOverlappedPass(HANDLE handle, HANDLE myEvent, t_filesize position, void * out, DWORD outBytes, abort_callback & abort) { | |
| 92 abort.check(); | |
| 93 if (outBytes == 0) return 0; | |
| 94 OVERLAPPED ol = {}; | |
| 95 fillOverlapped(ol, myEvent, position); | |
| 96 ResetEvent(myEvent); | |
| 97 DWORD bytesDone; | |
| 98 SetLastError(NO_ERROR); | |
| 99 if (ReadFile( handle, out, outBytes, &bytesDone, &ol)) { | |
| 100 // succeeded already? | |
| 101 return bytesDone; | |
| 102 } | |
| 103 | |
| 104 { | |
| 105 const DWORD code = GetLastError(); | |
| 106 switch(code) { | |
| 107 case ERROR_HANDLE_EOF: | |
| 108 case ERROR_BROKEN_PIPE: | |
| 109 return 0; | |
| 110 case ERROR_IO_PENDING: | |
| 111 break; // continue | |
| 112 default: | |
| 113 exception_io_from_win32(code); | |
| 114 }; | |
| 115 } | |
| 116 | |
| 117 const HANDLE handles[] = {myEvent, abort.get_abort_event()}; | |
| 118 SetLastError(NO_ERROR); | |
| 119 DWORD state = WaitForMultipleObjects(_countof(handles), handles, FALSE, INFINITE); | |
| 120 if (state == WAIT_OBJECT_0) { | |
| 121 SetLastError(NO_ERROR); | |
| 122 if (!GetOverlappedResult(handle,&ol,&bytesDone,TRUE)) { | |
| 123 const DWORD code = GetLastError(); | |
| 124 if (code == ERROR_HANDLE_EOF || code == ERROR_BROKEN_PIPE) bytesDone = 0; | |
| 125 else { | |
| 126 CancelIo(handle); | |
| 127 exception_io_from_win32(code); | |
| 128 } | |
| 129 } | |
| 130 return bytesDone; | |
| 131 } | |
| 132 CancelIo(handle); | |
| 133 throw exception_aborted(); | |
| 134 } | |
| 135 size_t readOverlapped(HANDLE handle, HANDLE myEvent, t_filesize & position, void * out, size_t outBytes, abort_callback & abort) { | |
| 136 enum {readMAX = 16*1024*1024}; | |
| 137 size_t done = 0; | |
| 138 while(done < outBytes) { | |
| 139 size_t delta = outBytes - done; | |
| 140 if (delta > readMAX) delta = readMAX; | |
| 141 delta = readOverlappedPass(handle, myEvent, position, (BYTE*) out + done, (DWORD) delta, abort); | |
| 142 if (delta == 0) break; | |
| 143 done += delta; | |
| 144 position += delta; | |
| 145 } | |
| 146 return done; | |
| 147 } | |
| 148 | |
| 149 size_t readStreamOverlapped(HANDLE handle, HANDLE myEvent, void * out, size_t outBytes, abort_callback & abort) { | |
| 150 enum {readMAX = 16*1024*1024}; | |
| 151 size_t done = 0; | |
| 152 while(done < outBytes) { | |
| 153 size_t delta = outBytes - done; | |
| 154 if (delta > readMAX) delta = readMAX; | |
| 155 delta = readOverlappedPass(handle, myEvent, 0, (BYTE*) out + done, (DWORD) delta, abort); | |
| 156 if (delta == 0) break; | |
| 157 done += delta; | |
| 158 } | |
| 159 return done; | |
| 160 } | |
| 161 | |
| 162 typedef BOOL (WINAPI * pCancelSynchronousIo_t)(HANDLE hThread); | |
| 163 | |
| 164 | |
| 165 struct createFileData_t { | |
| 166 LPCTSTR lpFileName; | |
| 167 DWORD dwDesiredAccess; | |
| 168 DWORD dwShareMode; | |
| 169 LPSECURITY_ATTRIBUTES lpSecurityAttributes; | |
| 170 DWORD dwCreationDisposition; | |
| 171 DWORD dwFlagsAndAttributes; | |
| 172 HANDLE hTemplateFile; | |
| 173 HANDLE hResult; | |
| 174 DWORD dwErrorCode; | |
| 175 }; | |
| 176 | |
| 177 HANDLE createFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile, abort_callback & abort) { | |
| 178 abort.check(); | |
| 179 | |
| 180 return CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); | |
| 181 } | |
| 182 | |
| 183 size_t lowLevelIO(HANDLE hFile, const GUID & guid, size_t arg1, void * arg2, size_t arg2size, bool canWrite, abort_callback & abort) { | |
| 184 if ( guid == file_lowLevelIO::guid_flushFileBuffers ) { | |
| 185 if (!canWrite) { | |
| 186 PFC_ASSERT(!"File opened for reading, not writing"); | |
| 187 throw exception_io_denied(); | |
| 188 } | |
| 189 WIN32_IO_OP( ::FlushFileBuffers(hFile) ); | |
| 190 return 1; | |
| 191 } else if ( guid == file_lowLevelIO::guid_getFileTimes ) { | |
| 192 if ( arg2size == sizeof(file_lowLevelIO::filetimes_t) ) { | |
| 193 if (canWrite) WIN32_IO_OP(::FlushFileBuffers(hFile)); | |
| 194 auto ft = reinterpret_cast<file_lowLevelIO::filetimes_t *>(arg2); | |
| 195 static_assert(sizeof(t_filetimestamp) == sizeof(FILETIME), "struct sanity"); | |
| 196 WIN32_IO_OP( GetFileTime( hFile, (FILETIME*)&ft->creation, (FILETIME*)&ft->lastAccess, (FILETIME*)&ft->lastWrite) ); | |
| 197 return 1; | |
| 198 } | |
| 199 } else if ( guid == file_lowLevelIO::guid_setFileTimes ) { | |
| 200 if (arg2size == sizeof(file_lowLevelIO::filetimes_t)) { | |
| 201 if (!canWrite) { | |
| 202 PFC_ASSERT(!"File opened for reading, not writing"); | |
| 203 throw exception_io_denied(); | |
| 204 } | |
| 205 WIN32_IO_OP(::FlushFileBuffers(hFile)); | |
| 206 auto ft = reinterpret_cast<file_lowLevelIO::filetimes_t *>(arg2); | |
| 207 static_assert(sizeof(t_filetimestamp) == sizeof(FILETIME), "struct sanity"); | |
| 208 const FILETIME * pCreation = nullptr; | |
| 209 const FILETIME * pLastAccess = nullptr; | |
| 210 const FILETIME * pLastWrite = nullptr; | |
| 211 if ( ft->creation != filetimestamp_invalid ) pCreation = (const FILETIME*)&ft->creation; | |
| 212 if ( ft->lastAccess != filetimestamp_invalid ) pLastAccess = (const FILETIME*)&ft->lastAccess; | |
| 213 if ( ft->lastWrite != filetimestamp_invalid ) pLastWrite = (const FILETIME*)&ft->lastWrite; | |
| 214 WIN32_IO_OP( SetFileTime(hFile, pCreation, pLastAccess, pLastWrite) ); | |
| 215 return 1; | |
| 216 } | |
| 217 } | |
| 218 return 0; | |
| 219 } | |
| 220 | |
| 221 t_filestats2 stats2_from_handle(HANDLE h, const wchar_t * fallbackPath, uint32_t flags, abort_callback& a) { | |
| 222 a.check(); | |
| 223 // Sadly GetFileInformationByHandle() is UNRELIABLE with certain net shares | |
| 224 BY_HANDLE_FILE_INFORMATION info = {}; | |
| 225 if (GetFileInformationByHandle(h, &info)) { | |
| 226 return file_win32_helpers::translate_stats2(info); | |
| 227 } | |
| 228 | |
| 229 a.check(); | |
| 230 t_filestats2 ret; | |
| 231 | |
| 232 // ALWAYS get size, fail if bad handle | |
| 233 ret.m_size = get_size(h); | |
| 234 | |
| 235 if (flags & (stats2_timestamp | stats2_timestampCreate)) { | |
| 236 static_assert(sizeof(t_filetimestamp) == sizeof(FILETIME), "struct sanity"); | |
| 237 FILETIME ftCreate = {}, ftWrite = {}; | |
| 238 if (GetFileTime(h, &ftCreate, nullptr, &ftWrite)) { | |
| 239 ret.m_timestamp = make_uint64(ftWrite); ret.m_timestampCreate = make_uint64(ftCreate); | |
| 240 } | |
| 241 } | |
| 242 if (flags & stats2_flags) { | |
| 243 // No other way to get this from handle? | |
| 244 if (fallbackPath != nullptr && *fallbackPath != 0) { | |
| 245 DWORD attr = GetFileAttributes(fallbackPath); | |
| 246 if (attr != INVALID_FILE_ATTRIBUTES) { | |
| 247 attribs_from_win32(ret, attr); | |
| 248 } | |
| 249 } | |
| 250 } | |
| 251 return ret; | |
| 252 } | |
| 253 void attribs_from_win32(t_filestats2& out, DWORD in) { | |
| 254 out.set_readonly((in & FILE_ATTRIBUTE_READONLY) != 0); | |
| 255 out.set_folder((in & FILE_ATTRIBUTE_DIRECTORY) != 0); | |
| 256 out.set_hidden((in & FILE_ATTRIBUTE_HIDDEN) != 0); | |
| 257 out.set_system((in & FILE_ATTRIBUTE_SYSTEM) != 0); | |
| 258 out.set_remote(false); | |
| 259 } | |
| 260 | |
| 261 | |
| 262 // Seek penalty query, effectively: is this an SSD? | |
| 263 // Credit: | |
| 264 // https://devblogs.microsoft.com/oldnewthing/20201023-00/?p=104395 | |
| 265 static bool queryVolumeSeekPenalty(HANDLE hVolume, bool& out) { | |
| 266 STORAGE_PROPERTY_QUERY query = {}; | |
| 267 query.PropertyId = StorageDeviceSeekPenaltyProperty; | |
| 268 query.QueryType = PropertyStandardQuery; | |
| 269 DWORD count = 1; | |
| 270 DEVICE_SEEK_PENALTY_DESCRIPTOR result = {}; | |
| 271 if (!DeviceIoControl(hVolume, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &result, sizeof(result), &count, nullptr)) { | |
| 272 return false; | |
| 273 } | |
| 274 out = result.IncursSeekPenalty; | |
| 275 return true; | |
| 276 } | |
| 277 | |
| 278 static HANDLE GetVolumeHandleForFile(PCWSTR filePath) { | |
| 279 wchar_t volumePath[MAX_PATH] = {}; | |
| 280 WIN32_OP_D(GetVolumePathName(filePath, volumePath, ARRAYSIZE(volumePath))); | |
| 281 | |
| 282 wchar_t volumeName[MAX_PATH] = {}; | |
| 283 WIN32_OP_D(GetVolumeNameForVolumeMountPoint(volumePath, volumeName, ARRAYSIZE(volumeName))); | |
| 284 | |
| 285 auto length = wcslen(volumeName); | |
| 286 if ( length == 0 ) { | |
| 287 PFC_ASSERT(!"???"); | |
| 288 return NULL; | |
| 289 } | |
| 290 if (length && volumeName[length - 1] == L'\\') { | |
| 291 volumeName[length - 1] = L'\0'; | |
| 292 } | |
| 293 | |
| 294 HANDLE ret; | |
| 295 WIN32_OP_D( ret = CreateFile(volumeName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr) ); | |
| 296 return ret; | |
| 297 } | |
| 298 bool querySeekPenalty(const wchar_t* nativePath, bool& out) { | |
| 299 CHandle h; | |
| 300 h.Attach( GetVolumeHandleForFile( nativePath ) ); | |
| 301 if (!h) return false; | |
| 302 return queryVolumeSeekPenalty(h, out); | |
| 303 } | |
| 304 bool querySeekPenalty(const char* fb2k_path, bool& out) { | |
| 305 const char * path = fb2k_path; | |
| 306 if ( matchProtocol(path, "file")) path = afterProtocol(path); | |
| 307 return querySeekPenalty(pfc::wideFromUTF8(path), out); | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 #endif // _WIN32 | |
| 312 |
