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.

874 lines
26KB

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