File.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. #include "il2cpp-config.h"
  2. #if IL2CPP_TARGET_WINDOWS
  3. #include "WindowsHelpers.h"
  4. #undef CopyFile
  5. #undef DeleteFile
  6. #undef MoveFile
  7. #undef ReplaceFile
  8. #undef GetFileAttributes
  9. #undef SetFileAttributes
  10. #undef CreatePipe
  11. #include "os/File.h"
  12. #include "utils/Expected.h"
  13. #include "utils/Il2CppError.h"
  14. #include "utils/StringUtils.h"
  15. #include "utils/PathUtils.h"
  16. #if IL2CPP_TARGET_WINRT
  17. #include "os/BrokeredFileSystem.h"
  18. #endif
  19. #include <stdint.h>
  20. static inline int FileWin32ErrorToErrorCode(DWORD win32ErrorCode)
  21. {
  22. return win32ErrorCode;
  23. }
  24. namespace il2cpp
  25. {
  26. namespace os
  27. {
  28. #if IL2CPP_TARGET_WINDOWS_DESKTOP
  29. utils::Expected<bool> File::Isatty(FileHandle* fileHandle)
  30. {
  31. DWORD mode;
  32. return GetConsoleMode((HANDLE)fileHandle, &mode) != 0;
  33. }
  34. #elif IL2CPP_TARGET_WINDOWS_GAMES
  35. utils::Expected<bool> File::Isatty(FileHandle* fileHandle)
  36. {
  37. return utils::Il2CppError(utils::NotSupported, "Console functions are not supported on Windows Games platforms.");
  38. }
  39. #endif
  40. #if IL2CPP_TARGET_WINDOWS_DESKTOP || IL2CPP_TARGET_WINDOWS_GAMES
  41. FileHandle* File::GetStdInput()
  42. {
  43. return (FileHandle*)GetStdHandle(STD_INPUT_HANDLE);
  44. }
  45. FileHandle* File::GetStdError()
  46. {
  47. return (FileHandle*)GetStdHandle(STD_ERROR_HANDLE);
  48. }
  49. FileHandle* File::GetStdOutput()
  50. {
  51. return (FileHandle*)GetStdHandle(STD_OUTPUT_HANDLE);
  52. }
  53. #endif // IL2CPP_TARGET_WINDOWS_DESKTOP || IL2CPP_TARGET_WINDOWS_GAMES
  54. utils::Expected<bool> File::CreatePipe(FileHandle** read_handle, FileHandle** write_handle)
  55. {
  56. int error;
  57. return CreatePipe(read_handle, write_handle, &error);
  58. }
  59. utils::Expected<bool> File::CreatePipe(FileHandle** read_handle, FileHandle** write_handle, int* error)
  60. {
  61. #if IL2CPP_TARGET_WINDOWS_DESKTOP || IL2CPP_TARGET_WINDOWS_GAMES
  62. SECURITY_ATTRIBUTES attr;
  63. attr.nLength = sizeof(SECURITY_ATTRIBUTES);
  64. attr.bInheritHandle = TRUE;
  65. attr.lpSecurityDescriptor = NULL;
  66. bool ret = ::CreatePipe((PHANDLE)read_handle, (PHANDLE)write_handle, &attr, 0);
  67. if (ret == FALSE)
  68. {
  69. *error = GetLastError();
  70. /* FIXME: throw an exception? */
  71. return false;
  72. }
  73. return true;
  74. #else // IL2CPP_TARGET_WINDOWS_DESKTOP || IL2CPP_TARGET_WINDOWS_GAMES
  75. return utils::Il2CppError(utils::NotSupported, "Pipes are not supported on WinRT based platforms.");
  76. #endif // IL2CPP_TARGET_WINDOWS_DESKTOP || IL2CPP_TARGET_WINDOWS_GAMES
  77. }
  78. #if !IL2CPP_TARGET_XBOXONE && !IL2CPP_TARGET_WINDOWS_GAMES
  79. UnityPalFileAttributes File::GetFileAttributes(const std::string& path, int *error)
  80. {
  81. const UTF16String utf16Path(utils::StringUtils::Utf8ToUtf16(path.c_str()));
  82. WIN32_FILE_ATTRIBUTE_DATA fileAttributes;
  83. BOOL result = ::GetFileAttributesExW((LPCWSTR)utf16Path.c_str(), GetFileExInfoStandard, &fileAttributes);
  84. if (result == FALSE)
  85. {
  86. auto lastError = ::GetLastError();
  87. #if IL2CPP_TARGET_WINRT
  88. if (lastError == ERROR_ACCESS_DENIED)
  89. return BrokeredFileSystem::GetFileAttributesW(utf16Path, error);
  90. #endif
  91. *error = FileWin32ErrorToErrorCode(lastError);
  92. return static_cast<UnityPalFileAttributes>(INVALID_FILE_ATTRIBUTES);
  93. }
  94. *error = kErrorCodeSuccess;
  95. return static_cast<UnityPalFileAttributes>(fileAttributes.dwFileAttributes);
  96. }
  97. #endif // !IL2CPP_TARGET_XBOXONE && !IL2CPP_TARGET_WINDOWS_GAMES
  98. bool File::SetFileAttributes(const std::string& path, UnityPalFileAttributes attributes, int* error)
  99. {
  100. const UTF16String utf16Path(utils::StringUtils::Utf8ToUtf16(path.c_str()));
  101. *error = kErrorCodeSuccess;
  102. if (::SetFileAttributesW((LPCWSTR)utf16Path.c_str(), attributes))
  103. return true;
  104. auto lastError = ::GetLastError();
  105. #if IL2CPP_TARGET_WINRT
  106. if (lastError == ERROR_ACCESS_DENIED)
  107. return BrokeredFileSystem::SetFileAttributesW(utf16Path, attributes, error);
  108. #endif
  109. *error = FileWin32ErrorToErrorCode(lastError);
  110. return false;
  111. }
  112. static inline int64_t HighAndLowToInt64(uint32_t high, uint32_t low)
  113. {
  114. return ((uint64_t)high << 32) + low;
  115. }
  116. static inline int64_t FileTimeToInt64(const FILETIME& fileTime)
  117. {
  118. return HighAndLowToInt64(fileTime.dwHighDateTime, fileTime.dwLowDateTime);
  119. }
  120. bool File::GetFileStat(const std::string& path, il2cpp::os::FileStat * stat, int* error)
  121. {
  122. *error = kErrorCodeSuccess;
  123. const UTF16String utf16Path(utils::StringUtils::Utf8ToUtf16(path.c_str()));
  124. WIN32_FILE_ATTRIBUTE_DATA data;
  125. if (!::GetFileAttributesExW((LPCWSTR)utf16Path.c_str(), GetFileExInfoStandard, &data))
  126. {
  127. auto lastError = ::GetLastError();
  128. #if IL2CPP_TARGET_WINRT
  129. if (lastError == ERROR_ACCESS_DENIED)
  130. return BrokeredFileSystem::GetFileStat(path, utf16Path, stat, error);
  131. #endif
  132. *error = FileWin32ErrorToErrorCode(lastError);
  133. return false;
  134. }
  135. stat->name = il2cpp::utils::PathUtils::Basename(path);
  136. stat->attributes = data.dwFileAttributes;
  137. stat->creation_time = FileTimeToInt64(data.ftCreationTime);
  138. stat->last_access_time = FileTimeToInt64(data.ftLastAccessTime);
  139. stat->last_write_time = FileTimeToInt64(data.ftLastWriteTime);
  140. stat->length = HighAndLowToInt64(data.nFileSizeHigh, data.nFileSizeLow);
  141. return true;
  142. }
  143. FileType File::GetFileType(FileHandle* handle)
  144. {
  145. int result = ::GetFileType((HANDLE)handle);
  146. /*if (result == FILE_TYPE_UNKNOWN)
  147. {
  148. *error = GetLastError();
  149. }*/
  150. return (FileType)result;
  151. }
  152. bool File::CopyFile(const std::string& src, const std::string& dest, bool overwrite, int* error)
  153. {
  154. const UTF16String utf16Src(utils::StringUtils::Utf8ToUtf16(src.c_str()));
  155. const UTF16String utf16Dest(utils::StringUtils::Utf8ToUtf16(dest.c_str()));
  156. *error = kErrorCodeSuccess;
  157. if (::CopyFileW((LPWSTR)utf16Src.c_str(), (LPWSTR)utf16Dest.c_str(), overwrite ? FALSE : TRUE))
  158. return true;
  159. auto lastError = ::GetLastError();
  160. #if IL2CPP_TARGET_WINRT
  161. if (lastError == ERROR_ACCESS_DENIED)
  162. return BrokeredFileSystem::CopyFileW(utf16Src, utf16Dest, overwrite, error);
  163. #endif
  164. *error = FileWin32ErrorToErrorCode(lastError);
  165. return false;
  166. }
  167. bool File::MoveFile(const std::string& src, const std::string& dest, int* error)
  168. {
  169. const UTF16String utf16Src(utils::StringUtils::Utf8ToUtf16(src.c_str()));
  170. const UTF16String utf16Dest(utils::StringUtils::Utf8ToUtf16(dest.c_str()));
  171. *error = kErrorCodeSuccess;
  172. if (::MoveFileExW((LPWSTR)utf16Src.c_str(), (LPWSTR)utf16Dest.c_str(), MOVEFILE_COPY_ALLOWED))
  173. return true;
  174. auto lastError = ::GetLastError();
  175. #if IL2CPP_TARGET_WINRT
  176. if (lastError == ERROR_ACCESS_DENIED)
  177. return BrokeredFileSystem::MoveFileW(utf16Src, utf16Dest, error);
  178. #endif
  179. *error = FileWin32ErrorToErrorCode(lastError);
  180. return false;
  181. }
  182. bool File::DeleteFile(const std::string& path, int *error)
  183. {
  184. *error = kErrorCodeSuccess;
  185. const UTF16String utf16Path(utils::StringUtils::Utf8ToUtf16(path.c_str()));
  186. if (::DeleteFileW((LPWSTR)utf16Path.c_str()))
  187. return true;
  188. auto lastError = ::GetLastError();
  189. #if IL2CPP_TARGET_WINRT
  190. if (lastError == ERROR_ACCESS_DENIED)
  191. {
  192. *error = BrokeredFileSystem::DeleteFileW(utf16Path);
  193. return *error == kErrorCodeSuccess;
  194. }
  195. #endif
  196. *error = FileWin32ErrorToErrorCode(lastError);
  197. return false;
  198. }
  199. bool File::ReplaceFile(const std::string& sourceFileName, const std::string& destinationFileName, const std::string& destinationBackupFileName, bool ignoreMetadataErrors, int* error)
  200. {
  201. const UTF16String utf16Src(utils::StringUtils::Utf8ToUtf16(sourceFileName.c_str()));
  202. const UTF16String utf16Dest(utils::StringUtils::Utf8ToUtf16(destinationFileName.c_str()));
  203. const UTF16String utf16Backup(utils::StringUtils::Utf8ToUtf16(destinationBackupFileName.c_str()));
  204. *error = kErrorCodeSuccess;
  205. DWORD flags = REPLACEFILE_WRITE_THROUGH;
  206. if (ignoreMetadataErrors)
  207. flags |= REPLACEFILE_IGNORE_MERGE_ERRORS;
  208. if (::ReplaceFileW((LPWSTR)utf16Dest.c_str(), (LPWSTR)utf16Src.c_str(), utf16Backup.empty() ? NULL : (LPWSTR)utf16Backup.c_str(), flags, NULL, NULL))
  209. return true;
  210. *error = FileWin32ErrorToErrorCode(::GetLastError());
  211. return false;
  212. }
  213. static inline int MonoToWindowsOpenMode(int monoOpenMode)
  214. {
  215. switch (monoOpenMode)
  216. {
  217. case kFileModeCreateNew:
  218. return CREATE_NEW;
  219. case kFileModeCreate:
  220. return CREATE_ALWAYS;
  221. case kFileModeOpen:
  222. return OPEN_EXISTING;
  223. case kFileModeOpenOrCreate:
  224. case kFileModeAppend:
  225. return OPEN_ALWAYS;
  226. case kFileModeTruncate:
  227. return TRUNCATE_EXISTING;
  228. default:
  229. Assert(false && "Unknown mono open mode");
  230. IL2CPP_UNREACHABLE;
  231. }
  232. }
  233. static inline int MonoToWindowsAccessMode(int monoAccessMode)
  234. {
  235. switch (monoAccessMode)
  236. {
  237. case kFileAccessRead:
  238. return GENERIC_READ;
  239. case kFileAccessWrite:
  240. return GENERIC_WRITE;
  241. case kFileAccessExecute:
  242. return GENERIC_EXECUTE;
  243. case kFileAccessReadWrite:
  244. return GENERIC_READ | GENERIC_WRITE;
  245. case kFileAccessReadWriteExecute:
  246. return GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE;
  247. default:
  248. return 0;
  249. }
  250. }
  251. static inline DWORD MonoOptionsToWindowsFlagsAndAttributes(const std::string& path, int options)
  252. {
  253. DWORD flagsAndAttributes;
  254. if (options != 0)
  255. {
  256. if (options & kFileOptionsEncrypted)
  257. flagsAndAttributes = FILE_ATTRIBUTE_ENCRYPTED;
  258. else
  259. flagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
  260. if (options & kFileOptionsDeleteOnClose)
  261. flagsAndAttributes |= FILE_FLAG_DELETE_ON_CLOSE;
  262. if (options & kFileOptionsSequentialScan)
  263. flagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
  264. if (options & kFileOptionsRandomAccess)
  265. flagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
  266. if (options & kFileOptionsWriteThrough)
  267. flagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
  268. }
  269. else
  270. {
  271. flagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
  272. }
  273. int error;
  274. UnityPalFileAttributes currentAttributes = File::GetFileAttributes(path, &error);
  275. if (currentAttributes != INVALID_FILE_ATTRIBUTES && (currentAttributes & FILE_ATTRIBUTE_DIRECTORY))
  276. flagsAndAttributes |= FILE_FLAG_BACKUP_SEMANTICS; // Required to open a directory
  277. return flagsAndAttributes;
  278. }
  279. FileHandle* File::Open(const std::string& path, int openMode, int accessMode, int shareMode, int options, int *error)
  280. {
  281. const UTF16String utf16Path(utils::StringUtils::Utf8ToUtf16(path.c_str()));
  282. openMode = MonoToWindowsOpenMode(openMode);
  283. accessMode = MonoToWindowsAccessMode(accessMode);
  284. DWORD flagsAndAttributes = MonoOptionsToWindowsFlagsAndAttributes(path, options);
  285. HANDLE handle = ::CreateFileW((LPCWSTR)utf16Path.c_str(), accessMode, shareMode, NULL, openMode, flagsAndAttributes, NULL);
  286. if (INVALID_HANDLE_VALUE == handle)
  287. {
  288. auto lastError = ::GetLastError();
  289. #if IL2CPP_TARGET_WINRT
  290. if (lastError == ERROR_ACCESS_DENIED)
  291. return BrokeredFileSystem::Open(utf16Path, accessMode, shareMode, openMode, flagsAndAttributes, error);
  292. #endif
  293. *error = FileWin32ErrorToErrorCode(lastError);
  294. return (FileHandle*)INVALID_HANDLE_VALUE;
  295. }
  296. *error = kErrorCodeSuccess;
  297. return (FileHandle*)handle;
  298. }
  299. bool File::Close(FileHandle* handle, int *error)
  300. {
  301. *error = kErrorCodeSuccess;
  302. if (CloseHandle((HANDLE)handle))
  303. return true;
  304. *error = FileWin32ErrorToErrorCode(::GetLastError());
  305. return false;
  306. }
  307. bool File::SetFileTime(FileHandle* handle, int64_t creation_time, int64_t last_access_time, int64_t last_write_time, int* error)
  308. {
  309. FILE_BASIC_INFO fileInfo;
  310. fileInfo.CreationTime.QuadPart = creation_time;
  311. fileInfo.LastAccessTime.QuadPart = last_access_time;
  312. fileInfo.LastWriteTime.QuadPart = last_write_time;
  313. fileInfo.ChangeTime.QuadPart = 0; // 0 means don't change anything
  314. fileInfo.FileAttributes = 0; // 0 means don't change anything
  315. if (SetFileInformationByHandle(handle, FileBasicInfo, &fileInfo, sizeof(FILE_BASIC_INFO)) == FALSE)
  316. {
  317. *error = GetLastError();
  318. return false;
  319. }
  320. *error = kErrorCodeSuccess;
  321. return true;
  322. }
  323. int64_t File::GetLength(FileHandle* handle, int *error)
  324. {
  325. *error = kErrorCodeSuccess;
  326. LARGE_INTEGER size;
  327. if (!::GetFileSizeEx((HANDLE)handle, &size))
  328. {
  329. *error = FileWin32ErrorToErrorCode(::GetLastError());
  330. return 0;
  331. }
  332. return size.QuadPart;
  333. }
  334. #if !IL2CPP_USE_GENERIC_FILE
  335. bool File::Truncate(FileHandle* handle, int *error)
  336. {
  337. *error = kErrorCodeSuccess;
  338. if (!::SetEndOfFile((HANDLE)handle))
  339. {
  340. *error = FileWin32ErrorToErrorCode(::GetLastError());
  341. return false;
  342. }
  343. return true;
  344. }
  345. #endif // IL2CPP_USE_GENERIC_FILE
  346. bool File::SetLength(FileHandle* handle, int64_t length, int *error)
  347. {
  348. *error = kErrorCodeSuccess;
  349. LARGE_INTEGER zeroOffset = { 0 };
  350. LARGE_INTEGER requestedOffset = { 0 };
  351. requestedOffset.QuadPart = length;
  352. LARGE_INTEGER initialPosition = { 0 };
  353. // set position to 0 from current to retrieve current position
  354. if (!::SetFilePointerEx((HANDLE)handle, zeroOffset, &initialPosition, FILE_CURRENT))
  355. {
  356. *error = FileWin32ErrorToErrorCode(::GetLastError());
  357. return false;
  358. }
  359. // seek to requested length
  360. if (!::SetFilePointerEx((HANDLE)handle, requestedOffset, NULL, FILE_BEGIN))
  361. {
  362. *error = FileWin32ErrorToErrorCode(::GetLastError());
  363. return false;
  364. }
  365. // set requested length
  366. if (!::SetEndOfFile((HANDLE)handle))
  367. {
  368. *error = FileWin32ErrorToErrorCode(::GetLastError());
  369. return false;
  370. }
  371. // restore original position
  372. if (!::SetFilePointerEx((HANDLE)handle, initialPosition, NULL, FILE_BEGIN))
  373. {
  374. *error = FileWin32ErrorToErrorCode(::GetLastError());
  375. return false;
  376. }
  377. return true;
  378. }
  379. int64_t File::Seek(FileHandle* handle, int64_t offset, int origin, int *error)
  380. {
  381. *error = kErrorCodeSuccess;
  382. LARGE_INTEGER distance;
  383. distance.QuadPart = offset;
  384. LARGE_INTEGER position = { 0 };
  385. if (!::SetFilePointerEx((HANDLE)handle, distance, &position, origin))
  386. *error = FileWin32ErrorToErrorCode(::GetLastError());
  387. return position.QuadPart;
  388. }
  389. int File::Read(FileHandle* handle, char *dest, int count, int *error)
  390. {
  391. *error = kErrorCodeSuccess;
  392. DWORD bytesRead = 0;
  393. if (!::ReadFile(handle, dest, count, &bytesRead, NULL))
  394. *error = FileWin32ErrorToErrorCode(::GetLastError());
  395. return bytesRead;
  396. }
  397. int32_t File::Write(FileHandle* handle, const char* buffer, int count, int *error)
  398. {
  399. int32_t written;
  400. BOOL success = WriteFile((HANDLE)handle, buffer, count, (LPDWORD)&written, NULL);
  401. if (!success)
  402. {
  403. DWORD originalError = GetLastError();
  404. if (originalError == ERROR_INVALID_PARAMETER)
  405. {
  406. // Maybe this is an async file write, so try with those parameters.
  407. OVERLAPPED overlapped = {0};
  408. success = WriteFile((HANDLE)handle, buffer, count, NULL, &overlapped);
  409. if (success != 0 || GetLastError() == ERROR_IO_PENDING)
  410. {
  411. success = TRUE;
  412. // The async write succeeded. Now get the number of bytes written.
  413. #if IL2CPP_TARGET_WINDOWS_DESKTOP
  414. if (GetOverlappedResult((HANDLE)handle, &overlapped, (LPDWORD)&written, TRUE) == 0)
  415. #else
  416. if (GetOverlappedResultEx((HANDLE)handle, &overlapped, (LPDWORD)&written, INFINITE, FALSE) == 0)
  417. #endif
  418. {
  419. // Oops, we could not get the number of bytes writen, so return an error.
  420. *error = GetLastError();
  421. return -1;
  422. }
  423. }
  424. }
  425. if (!success)
  426. {
  427. *error = originalError;
  428. return -1;
  429. }
  430. }
  431. return written;
  432. }
  433. bool File::Flush(FileHandle* handle, int* error)
  434. {
  435. *error = kErrorCodeSuccess;
  436. if (FlushFileBuffers((HANDLE)handle))
  437. return true;
  438. *error = FileWin32ErrorToErrorCode(::GetLastError());
  439. return false;
  440. }
  441. void File::Lock(FileHandle* handle, int64_t position, int64_t length, int* error)
  442. {
  443. *error = kErrorCodeSuccess;
  444. OVERLAPPED overlapped;
  445. ZeroMemory(&overlapped, sizeof(overlapped));
  446. overlapped.Offset = position & 0xFFFFFFFF;
  447. overlapped.OffsetHigh = position >> 32;
  448. LARGE_INTEGER lengthUnion;
  449. lengthUnion.QuadPart = length;
  450. if (!::LockFileEx((HANDLE)handle, LOCKFILE_FAIL_IMMEDIATELY, 0, lengthUnion.LowPart, lengthUnion.HighPart, &overlapped))
  451. *error = FileWin32ErrorToErrorCode(::GetLastError());
  452. }
  453. void File::Unlock(FileHandle* handle, int64_t position, int64_t length, int* error)
  454. {
  455. *error = kErrorCodeSuccess;
  456. OVERLAPPED overlapped;
  457. ZeroMemory(&overlapped, sizeof(overlapped));
  458. overlapped.Offset = position & 0xFFFFFFFF;
  459. overlapped.OffsetHigh = position >> 32;
  460. LARGE_INTEGER lengthUnion;
  461. lengthUnion.QuadPart = length;
  462. if (!::UnlockFileEx((HANDLE)handle, 0, lengthUnion.LowPart, lengthUnion.HighPart, &overlapped))
  463. *error = FileWin32ErrorToErrorCode(::GetLastError());
  464. }
  465. utils::Expected<bool> File::DuplicateHandle(FileHandle* source_process_handle, FileHandle* source_handle, FileHandle* target_process_handle,
  466. FileHandle** target_handle, int access, int inherit, int options, int* error)
  467. {
  468. /* This is only used on Windows */
  469. //MONO_PREPARE_BLOCKING;
  470. BOOL ret = ::DuplicateHandle((HANDLE)source_process_handle, (HANDLE)source_handle, (HANDLE)target_process_handle, (LPHANDLE)target_handle, access, inherit, options);
  471. //MONO_FINISH_BLOCKING;
  472. if (ret == FALSE)
  473. {
  474. *error = GetLastError();
  475. /* FIXME: throw an exception? */
  476. return false;
  477. }
  478. return true;
  479. }
  480. static bool ends_with(const std::string& value, const std::string& ending)
  481. {
  482. if (value.length() >= ending.length())
  483. return value.compare(value.length() - ending.length(), ending.length(), ending) == 0;
  484. return false;
  485. }
  486. utils::Expected<bool> File::IsExecutable(const std::string& path)
  487. {
  488. return ends_with(path, "exe");
  489. }
  490. bool File::Cancel(FileHandle* handle)
  491. {
  492. return CancelIoEx((HANDLE)handle, NULL);
  493. }
  494. }
  495. }
  496. #endif