The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

873 lines
26KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. // (This file gets included by juce_win32_NativeCode.cpp, rather than being
  19. // compiled on its own).
  20. #if JUCE_INCLUDED_FILE
  21. //==============================================================================
  22. #ifndef CSIDL_MYMUSIC
  23. #define CSIDL_MYMUSIC 0x000d
  24. #endif
  25. #ifndef CSIDL_MYVIDEO
  26. #define CSIDL_MYVIDEO 0x000e
  27. #endif
  28. #ifndef INVALID_FILE_ATTRIBUTES
  29. #define INVALID_FILE_ATTRIBUTES ((DWORD) -1)
  30. #endif
  31. //==============================================================================
  32. namespace WindowsFileHelpers
  33. {
  34. int64 fileTimeToTime (const FILETIME* const ft)
  35. {
  36. static_jassert (sizeof (ULARGE_INTEGER) == sizeof (FILETIME)); // tell me if this fails!
  37. return (reinterpret_cast<const ULARGE_INTEGER*> (ft)->QuadPart - literal64bit (116444736000000000)) / 10000;
  38. }
  39. void timeToFileTime (const int64 time, FILETIME* const ft)
  40. {
  41. reinterpret_cast<ULARGE_INTEGER*> (ft)->QuadPart = time * 10000 + literal64bit (116444736000000000);
  42. }
  43. const String getDriveFromPath (String path)
  44. {
  45. // (mess with the string to make sure it's not sharing its internal storage)
  46. path = (path + " ").dropLastCharacters(1);
  47. WCHAR* p = const_cast <WCHAR*> (path.toWideCharPointer());
  48. if (PathStripToRoot (p))
  49. return String ((const WCHAR*) p);
  50. return path;
  51. }
  52. int64 getDiskSpaceInfo (const String& path, const bool total)
  53. {
  54. ULARGE_INTEGER spc, tot, totFree;
  55. if (GetDiskFreeSpaceEx (getDriveFromPath (path).toWideCharPointer(), &spc, &tot, &totFree))
  56. return total ? (int64) tot.QuadPart
  57. : (int64) spc.QuadPart;
  58. return 0;
  59. }
  60. unsigned int getWindowsDriveType (const String& path)
  61. {
  62. return GetDriveType (getDriveFromPath (path).toWideCharPointer());
  63. }
  64. const File getSpecialFolderPath (int type)
  65. {
  66. WCHAR path [MAX_PATH + 256];
  67. if (SHGetSpecialFolderPath (0, path, type, FALSE))
  68. return File (String (path));
  69. return File::nonexistent;
  70. }
  71. }
  72. //==============================================================================
  73. const juce_wchar File::separator = '\\';
  74. const String File::separatorString ("\\");
  75. //==============================================================================
  76. bool File::exists() const
  77. {
  78. return fullPath.isNotEmpty()
  79. && GetFileAttributes (fullPath.toWideCharPointer()) != INVALID_FILE_ATTRIBUTES;
  80. }
  81. bool File::existsAsFile() const
  82. {
  83. return fullPath.isNotEmpty()
  84. && (GetFileAttributes (fullPath.toWideCharPointer()) & FILE_ATTRIBUTE_DIRECTORY) == 0;
  85. }
  86. bool File::isDirectory() const
  87. {
  88. const DWORD attr = GetFileAttributes (fullPath.toWideCharPointer());
  89. return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) && (attr != INVALID_FILE_ATTRIBUTES);
  90. }
  91. bool File::hasWriteAccess() const
  92. {
  93. if (exists())
  94. return (GetFileAttributes (fullPath.toWideCharPointer()) & FILE_ATTRIBUTE_READONLY) == 0;
  95. // on windows, it seems that even read-only directories can still be written into,
  96. // so checking the parent directory's permissions would return the wrong result..
  97. return true;
  98. }
  99. bool File::setFileReadOnlyInternal (const bool shouldBeReadOnly) const
  100. {
  101. DWORD attr = GetFileAttributes (fullPath.toWideCharPointer());
  102. if (attr == INVALID_FILE_ATTRIBUTES)
  103. return false;
  104. if (shouldBeReadOnly == ((attr & FILE_ATTRIBUTE_READONLY) != 0))
  105. return true;
  106. if (shouldBeReadOnly)
  107. attr |= FILE_ATTRIBUTE_READONLY;
  108. else
  109. attr &= ~FILE_ATTRIBUTE_READONLY;
  110. return SetFileAttributes (fullPath.toWideCharPointer(), attr) != FALSE;
  111. }
  112. bool File::isHidden() const
  113. {
  114. return (GetFileAttributes (getFullPathName().toWideCharPointer()) & FILE_ATTRIBUTE_HIDDEN) != 0;
  115. }
  116. //==============================================================================
  117. bool File::deleteFile() const
  118. {
  119. if (! exists())
  120. return true;
  121. return isDirectory() ? RemoveDirectory (fullPath.toWideCharPointer()) != 0
  122. : DeleteFile (fullPath.toWideCharPointer()) != 0;
  123. }
  124. bool File::moveToTrash() const
  125. {
  126. if (! exists())
  127. return true;
  128. SHFILEOPSTRUCT fos;
  129. zerostruct (fos);
  130. // The string we pass in must be double null terminated..
  131. String doubleNullTermPath (getFullPathName() + " ");
  132. WCHAR* const p = const_cast <WCHAR*> (doubleNullTermPath.toWideCharPointer());
  133. p [getFullPathName().length()] = 0;
  134. fos.wFunc = FO_DELETE;
  135. fos.pFrom = p;
  136. fos.fFlags = FOF_ALLOWUNDO | FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION
  137. | FOF_NOCONFIRMMKDIR | FOF_RENAMEONCOLLISION;
  138. return SHFileOperation (&fos) == 0;
  139. }
  140. bool File::copyInternal (const File& dest) const
  141. {
  142. return CopyFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer(), false) != 0;
  143. }
  144. bool File::moveInternal (const File& dest) const
  145. {
  146. return MoveFile (fullPath.toWideCharPointer(), dest.getFullPathName().toWideCharPointer()) != 0;
  147. }
  148. void File::createDirectoryInternal (const String& fileName) const
  149. {
  150. CreateDirectory (fileName.toWideCharPointer(), 0);
  151. }
  152. //==============================================================================
  153. int64 juce_fileSetPosition (void* handle, int64 pos)
  154. {
  155. LARGE_INTEGER li;
  156. li.QuadPart = pos;
  157. li.LowPart = SetFilePointer ((HANDLE) handle, li.LowPart, &li.HighPart, FILE_BEGIN); // (returns -1 if it fails)
  158. return li.QuadPart;
  159. }
  160. void FileInputStream::openHandle()
  161. {
  162. totalSize = file.getSize();
  163. HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
  164. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
  165. if (h != INVALID_HANDLE_VALUE)
  166. fileHandle = (void*) h;
  167. }
  168. void FileInputStream::closeHandle()
  169. {
  170. CloseHandle ((HANDLE) fileHandle);
  171. }
  172. size_t FileInputStream::readInternal (void* buffer, size_t numBytes)
  173. {
  174. if (fileHandle != 0)
  175. {
  176. DWORD actualNum = 0;
  177. ReadFile ((HANDLE) fileHandle, buffer, numBytes, &actualNum, 0);
  178. return (size_t) actualNum;
  179. }
  180. return 0;
  181. }
  182. //==============================================================================
  183. void FileOutputStream::openHandle()
  184. {
  185. HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), GENERIC_WRITE, FILE_SHARE_READ, 0,
  186. OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  187. if (h != INVALID_HANDLE_VALUE)
  188. {
  189. LARGE_INTEGER li;
  190. li.QuadPart = 0;
  191. li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_END);
  192. if (li.LowPart != INVALID_SET_FILE_POINTER)
  193. {
  194. fileHandle = (void*) h;
  195. currentPosition = li.QuadPart;
  196. }
  197. }
  198. }
  199. void FileOutputStream::closeHandle()
  200. {
  201. CloseHandle ((HANDLE) fileHandle);
  202. }
  203. int FileOutputStream::writeInternal (const void* buffer, int numBytes)
  204. {
  205. if (fileHandle != 0)
  206. {
  207. DWORD actualNum = 0;
  208. WriteFile ((HANDLE) fileHandle, buffer, numBytes, &actualNum, 0);
  209. return (int) actualNum;
  210. }
  211. return 0;
  212. }
  213. void FileOutputStream::flushInternal()
  214. {
  215. if (fileHandle != 0)
  216. FlushFileBuffers ((HANDLE) fileHandle);
  217. }
  218. //==============================================================================
  219. int64 File::getSize() const
  220. {
  221. WIN32_FILE_ATTRIBUTE_DATA attributes;
  222. if (GetFileAttributesEx (fullPath.toWideCharPointer(), GetFileExInfoStandard, &attributes))
  223. return (((int64) attributes.nFileSizeHigh) << 32) | attributes.nFileSizeLow;
  224. return 0;
  225. }
  226. void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int64& creationTime) const
  227. {
  228. using namespace WindowsFileHelpers;
  229. WIN32_FILE_ATTRIBUTE_DATA attributes;
  230. if (GetFileAttributesEx (fullPath.toWideCharPointer(), GetFileExInfoStandard, &attributes))
  231. {
  232. modificationTime = fileTimeToTime (&attributes.ftLastWriteTime);
  233. creationTime = fileTimeToTime (&attributes.ftCreationTime);
  234. accessTime = fileTimeToTime (&attributes.ftLastAccessTime);
  235. }
  236. else
  237. {
  238. creationTime = accessTime = modificationTime = 0;
  239. }
  240. }
  241. bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 creationTime) const
  242. {
  243. using namespace WindowsFileHelpers;
  244. bool ok = false;
  245. HANDLE h = CreateFile (fullPath.toWideCharPointer(), GENERIC_WRITE, FILE_SHARE_READ, 0,
  246. OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  247. if (h != INVALID_HANDLE_VALUE)
  248. {
  249. FILETIME m, a, c;
  250. timeToFileTime (modificationTime, &m);
  251. timeToFileTime (accessTime, &a);
  252. timeToFileTime (creationTime, &c);
  253. ok = SetFileTime (h,
  254. creationTime > 0 ? &c : 0,
  255. accessTime > 0 ? &a : 0,
  256. modificationTime > 0 ? &m : 0) != 0;
  257. CloseHandle (h);
  258. }
  259. return ok;
  260. }
  261. //==============================================================================
  262. void File::findFileSystemRoots (Array<File>& destArray)
  263. {
  264. TCHAR buffer [2048];
  265. buffer[0] = 0;
  266. buffer[1] = 0;
  267. GetLogicalDriveStrings (2048, buffer);
  268. const TCHAR* n = buffer;
  269. StringArray roots;
  270. while (*n != 0)
  271. {
  272. roots.add (String (n));
  273. while (*n++ != 0)
  274. {}
  275. }
  276. roots.sort (true);
  277. for (int i = 0; i < roots.size(); ++i)
  278. destArray.add (roots [i]);
  279. }
  280. //==============================================================================
  281. const String File::getVolumeLabel() const
  282. {
  283. TCHAR dest[64];
  284. if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest,
  285. numElementsInArray (dest), 0, 0, 0, 0, 0))
  286. dest[0] = 0;
  287. return dest;
  288. }
  289. int File::getVolumeSerialNumber() const
  290. {
  291. TCHAR dest[64];
  292. DWORD serialNum;
  293. if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest,
  294. numElementsInArray (dest), &serialNum, 0, 0, 0, 0))
  295. return 0;
  296. return (int) serialNum;
  297. }
  298. int64 File::getBytesFreeOnVolume() const
  299. {
  300. return WindowsFileHelpers::getDiskSpaceInfo (getFullPathName(), false);
  301. }
  302. int64 File::getVolumeTotalSize() const
  303. {
  304. return WindowsFileHelpers::getDiskSpaceInfo (getFullPathName(), true);
  305. }
  306. //==============================================================================
  307. bool File::isOnCDRomDrive() const
  308. {
  309. return WindowsFileHelpers::getWindowsDriveType (getFullPathName()) == DRIVE_CDROM;
  310. }
  311. bool File::isOnHardDisk() const
  312. {
  313. if (fullPath.isEmpty())
  314. return false;
  315. const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
  316. if (fullPath.toLowerCase()[0] <= 'b' && fullPath[1] == ':')
  317. return n != DRIVE_REMOVABLE;
  318. else
  319. return n != DRIVE_CDROM && n != DRIVE_REMOTE;
  320. }
  321. bool File::isOnRemovableDrive() const
  322. {
  323. if (fullPath.isEmpty())
  324. return false;
  325. const unsigned int n = WindowsFileHelpers::getWindowsDriveType (getFullPathName());
  326. return n == DRIVE_CDROM
  327. || n == DRIVE_REMOTE
  328. || n == DRIVE_REMOVABLE
  329. || n == DRIVE_RAMDISK;
  330. }
  331. //==============================================================================
  332. const File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type)
  333. {
  334. int csidlType = 0;
  335. switch (type)
  336. {
  337. case userHomeDirectory: csidlType = CSIDL_PROFILE; break;
  338. case userDocumentsDirectory: csidlType = CSIDL_PERSONAL; break;
  339. case userDesktopDirectory: csidlType = CSIDL_DESKTOP; break;
  340. case userApplicationDataDirectory: csidlType = CSIDL_APPDATA; break;
  341. case commonApplicationDataDirectory: csidlType = CSIDL_COMMON_APPDATA; break;
  342. case globalApplicationsDirectory: csidlType = CSIDL_PROGRAM_FILES; break;
  343. case userMusicDirectory: csidlType = CSIDL_MYMUSIC; break;
  344. case userMoviesDirectory: csidlType = CSIDL_MYVIDEO; break;
  345. case tempDirectory:
  346. {
  347. WCHAR dest [2048];
  348. dest[0] = 0;
  349. GetTempPath (numElementsInArray (dest), dest);
  350. return File (String (dest));
  351. }
  352. case invokedExecutableFile:
  353. case currentExecutableFile:
  354. case currentApplicationFile:
  355. {
  356. HINSTANCE moduleHandle = (HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle();
  357. WCHAR dest [MAX_PATH + 256];
  358. dest[0] = 0;
  359. GetModuleFileName (moduleHandle, dest, numElementsInArray (dest));
  360. return File (String (dest));
  361. }
  362. case hostApplicationPath:
  363. {
  364. WCHAR dest [MAX_PATH + 256];
  365. dest[0] = 0;
  366. GetModuleFileName (0, dest, numElementsInArray (dest));
  367. return File (String (dest));
  368. }
  369. default:
  370. jassertfalse; // unknown type?
  371. return File::nonexistent;
  372. }
  373. return WindowsFileHelpers::getSpecialFolderPath (csidlType);
  374. }
  375. //==============================================================================
  376. const File File::getCurrentWorkingDirectory()
  377. {
  378. WCHAR dest [MAX_PATH + 256];
  379. dest[0] = 0;
  380. GetCurrentDirectory (numElementsInArray (dest), dest);
  381. return File (String (dest));
  382. }
  383. bool File::setAsCurrentWorkingDirectory() const
  384. {
  385. return SetCurrentDirectory (getFullPathName().toWideCharPointer()) != FALSE;
  386. }
  387. //==============================================================================
  388. const String File::getVersion() const
  389. {
  390. String result;
  391. DWORD handle = 0;
  392. DWORD bufferSize = GetFileVersionInfoSize (getFullPathName().toWideCharPointer(), &handle);
  393. HeapBlock<char> buffer;
  394. buffer.calloc (bufferSize);
  395. if (GetFileVersionInfo (getFullPathName().toWideCharPointer(), 0, bufferSize, buffer))
  396. {
  397. VS_FIXEDFILEINFO* vffi;
  398. UINT len = 0;
  399. if (VerQueryValue (buffer, (LPTSTR) _T("\\"), (LPVOID*) &vffi, &len))
  400. {
  401. result << (int) HIWORD (vffi->dwFileVersionMS) << '.'
  402. << (int) LOWORD (vffi->dwFileVersionMS) << '.'
  403. << (int) HIWORD (vffi->dwFileVersionLS) << '.'
  404. << (int) LOWORD (vffi->dwFileVersionLS);
  405. }
  406. }
  407. return result;
  408. }
  409. //==============================================================================
  410. const File File::getLinkedTarget() const
  411. {
  412. File result (*this);
  413. String p (getFullPathName());
  414. if (! exists())
  415. p += ".lnk";
  416. else if (getFileExtension() != ".lnk")
  417. return result;
  418. ComSmartPtr <IShellLink> shellLink;
  419. if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)))
  420. {
  421. ComSmartPtr <IPersistFile> persistFile;
  422. if (SUCCEEDED (shellLink.QueryInterface (IID_IPersistFile, persistFile)))
  423. {
  424. if (SUCCEEDED (persistFile->Load (p.toWideCharPointer(), STGM_READ))
  425. && SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI)))
  426. {
  427. WIN32_FIND_DATA winFindData;
  428. WCHAR resolvedPath [MAX_PATH];
  429. if (SUCCEEDED (shellLink->GetPath (resolvedPath, MAX_PATH, &winFindData, SLGP_UNCPRIORITY)))
  430. result = File (resolvedPath);
  431. }
  432. }
  433. }
  434. return result;
  435. }
  436. //==============================================================================
  437. class DirectoryIterator::NativeIterator::Pimpl
  438. {
  439. public:
  440. Pimpl (const File& directory, const String& wildCard)
  441. : directoryWithWildCard (File::addTrailingSeparator (directory.getFullPathName()) + wildCard),
  442. handle (INVALID_HANDLE_VALUE)
  443. {
  444. }
  445. ~Pimpl()
  446. {
  447. if (handle != INVALID_HANDLE_VALUE)
  448. FindClose (handle);
  449. }
  450. bool next (String& filenameFound,
  451. bool* const isDir, bool* const isHidden, int64* const fileSize,
  452. Time* const modTime, Time* const creationTime, bool* const isReadOnly)
  453. {
  454. using namespace WindowsFileHelpers;
  455. WIN32_FIND_DATA findData;
  456. if (handle == INVALID_HANDLE_VALUE)
  457. {
  458. handle = FindFirstFile (directoryWithWildCard.toWideCharPointer(), &findData);
  459. if (handle == INVALID_HANDLE_VALUE)
  460. return false;
  461. }
  462. else
  463. {
  464. if (FindNextFile (handle, &findData) == 0)
  465. return false;
  466. }
  467. filenameFound = findData.cFileName;
  468. if (isDir != 0) *isDir = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
  469. if (isHidden != 0) *isHidden = ((findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0);
  470. if (fileSize != 0) *fileSize = findData.nFileSizeLow + (((int64) findData.nFileSizeHigh) << 32);
  471. if (modTime != 0) *modTime = Time (fileTimeToTime (&findData.ftLastWriteTime));
  472. if (creationTime != 0) *creationTime = Time (fileTimeToTime (&findData.ftCreationTime));
  473. if (isReadOnly != 0) *isReadOnly = ((findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0);
  474. return true;
  475. }
  476. private:
  477. const String directoryWithWildCard;
  478. HANDLE handle;
  479. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl);
  480. };
  481. DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard)
  482. : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard))
  483. {
  484. }
  485. DirectoryIterator::NativeIterator::~NativeIterator()
  486. {
  487. }
  488. bool DirectoryIterator::NativeIterator::next (String& filenameFound,
  489. bool* const isDir, bool* const isHidden, int64* const fileSize,
  490. Time* const modTime, Time* const creationTime, bool* const isReadOnly)
  491. {
  492. return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly);
  493. }
  494. //==============================================================================
  495. bool PlatformUtilities::openDocument (const String& fileName, const String& parameters)
  496. {
  497. HINSTANCE hInstance = 0;
  498. JUCE_TRY
  499. {
  500. hInstance = ShellExecute (0, 0, fileName.toWideCharPointer(), parameters.toWideCharPointer(), 0, SW_SHOWDEFAULT);
  501. }
  502. JUCE_CATCH_ALL
  503. return hInstance > (HINSTANCE) 32;
  504. }
  505. void File::revealToUser() const
  506. {
  507. if (isDirectory())
  508. startAsProcess();
  509. else if (getParentDirectory().exists())
  510. getParentDirectory().startAsProcess();
  511. }
  512. //==============================================================================
  513. class NamedPipeInternal
  514. {
  515. public:
  516. NamedPipeInternal (const String& file, const bool isPipe_)
  517. : pipeH (0),
  518. cancelEvent (0),
  519. connected (false),
  520. isPipe (isPipe_)
  521. {
  522. cancelEvent = CreateEvent (0, FALSE, FALSE, 0);
  523. pipeH = isPipe ? CreateNamedPipe (file.toWideCharPointer(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0,
  524. PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, 0)
  525. : CreateFile (file.toWideCharPointer(), GENERIC_READ | GENERIC_WRITE, 0, 0,
  526. OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
  527. }
  528. ~NamedPipeInternal()
  529. {
  530. disconnectPipe();
  531. if (pipeH != 0)
  532. CloseHandle (pipeH);
  533. CloseHandle (cancelEvent);
  534. }
  535. bool connect (const int timeOutMs)
  536. {
  537. if (! isPipe)
  538. return true;
  539. if (! connected)
  540. {
  541. OVERLAPPED over;
  542. zerostruct (over);
  543. over.hEvent = CreateEvent (0, TRUE, FALSE, 0);
  544. if (ConnectNamedPipe (pipeH, &over))
  545. {
  546. connected = false; // yes, you read that right. In overlapped mode it should always return 0.
  547. }
  548. else
  549. {
  550. const int err = GetLastError();
  551. if (err == ERROR_IO_PENDING || err == ERROR_PIPE_LISTENING)
  552. {
  553. HANDLE handles[] = { over.hEvent, cancelEvent };
  554. if (WaitForMultipleObjects (2, handles, FALSE,
  555. timeOutMs >= 0 ? timeOutMs : INFINITE) == WAIT_OBJECT_0)
  556. connected = true;
  557. }
  558. else if (err == ERROR_PIPE_CONNECTED)
  559. {
  560. connected = true;
  561. }
  562. }
  563. CloseHandle (over.hEvent);
  564. }
  565. return connected;
  566. }
  567. void disconnectPipe()
  568. {
  569. if (connected)
  570. {
  571. DisconnectNamedPipe (pipeH);
  572. connected = false;
  573. }
  574. }
  575. HANDLE pipeH;
  576. HANDLE cancelEvent;
  577. bool connected, isPipe;
  578. };
  579. void NamedPipe::close()
  580. {
  581. cancelPendingReads();
  582. const ScopedLock sl (lock);
  583. delete static_cast<NamedPipeInternal*> (internal);
  584. internal = 0;
  585. }
  586. bool NamedPipe::openInternal (const String& pipeName, const bool createPipe)
  587. {
  588. close();
  589. ScopedPointer<NamedPipeInternal> intern (new NamedPipeInternal ("\\\\.\\pipe\\" + pipeName, createPipe));
  590. if (intern->pipeH != INVALID_HANDLE_VALUE)
  591. {
  592. internal = intern.release();
  593. return true;
  594. }
  595. return false;
  596. }
  597. int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds)
  598. {
  599. const ScopedLock sl (lock);
  600. int bytesRead = -1;
  601. bool waitAgain = true;
  602. while (waitAgain && internal != 0)
  603. {
  604. NamedPipeInternal* const intern = static_cast<NamedPipeInternal*> (internal);
  605. waitAgain = false;
  606. if (! intern->connect (timeOutMilliseconds))
  607. break;
  608. if (maxBytesToRead <= 0)
  609. return 0;
  610. OVERLAPPED over;
  611. zerostruct (over);
  612. over.hEvent = CreateEvent (0, TRUE, FALSE, 0);
  613. unsigned long numRead;
  614. if (ReadFile (intern->pipeH, destBuffer, maxBytesToRead, &numRead, &over))
  615. {
  616. bytesRead = (int) numRead;
  617. }
  618. else if (GetLastError() == ERROR_IO_PENDING)
  619. {
  620. HANDLE handles[] = { over.hEvent, intern->cancelEvent };
  621. DWORD waitResult = WaitForMultipleObjects (2, handles, FALSE,
  622. timeOutMilliseconds >= 0 ? timeOutMilliseconds
  623. : INFINITE);
  624. if (waitResult != WAIT_OBJECT_0)
  625. {
  626. // if the operation timed out, let's cancel it...
  627. CancelIo (intern->pipeH);
  628. WaitForSingleObject (over.hEvent, INFINITE); // makes sure cancel is complete
  629. }
  630. if (GetOverlappedResult (intern->pipeH, &over, &numRead, FALSE))
  631. {
  632. bytesRead = (int) numRead;
  633. }
  634. else if (GetLastError() == ERROR_BROKEN_PIPE && intern->isPipe)
  635. {
  636. intern->disconnectPipe();
  637. waitAgain = true;
  638. }
  639. }
  640. else
  641. {
  642. waitAgain = internal != 0;
  643. Sleep (5);
  644. }
  645. CloseHandle (over.hEvent);
  646. }
  647. return bytesRead;
  648. }
  649. int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds)
  650. {
  651. int bytesWritten = -1;
  652. NamedPipeInternal* const intern = static_cast<NamedPipeInternal*> (internal);
  653. if (intern != 0 && intern->connect (timeOutMilliseconds))
  654. {
  655. if (numBytesToWrite <= 0)
  656. return 0;
  657. OVERLAPPED over;
  658. zerostruct (over);
  659. over.hEvent = CreateEvent (0, TRUE, FALSE, 0);
  660. unsigned long numWritten;
  661. if (WriteFile (intern->pipeH, sourceBuffer, numBytesToWrite, &numWritten, &over))
  662. {
  663. bytesWritten = (int) numWritten;
  664. }
  665. else if (GetLastError() == ERROR_IO_PENDING)
  666. {
  667. HANDLE handles[] = { over.hEvent, intern->cancelEvent };
  668. DWORD waitResult;
  669. waitResult = WaitForMultipleObjects (2, handles, FALSE,
  670. timeOutMilliseconds >= 0 ? timeOutMilliseconds
  671. : INFINITE);
  672. if (waitResult != WAIT_OBJECT_0)
  673. {
  674. CancelIo (intern->pipeH);
  675. WaitForSingleObject (over.hEvent, INFINITE);
  676. }
  677. if (GetOverlappedResult (intern->pipeH, &over, &numWritten, FALSE))
  678. {
  679. bytesWritten = (int) numWritten;
  680. }
  681. else if (GetLastError() == ERROR_BROKEN_PIPE && intern->isPipe)
  682. {
  683. intern->disconnectPipe();
  684. }
  685. }
  686. CloseHandle (over.hEvent);
  687. }
  688. return bytesWritten;
  689. }
  690. void NamedPipe::cancelPendingReads()
  691. {
  692. if (internal != 0)
  693. SetEvent (static_cast<NamedPipeInternal*> (internal)->cancelEvent);
  694. }
  695. #endif