PageRenderTime 30ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/ExtLibs/wxWidgets/src/msw/volume.cpp

https://bitbucket.org/lennonchan/cafu
C++ | 628 lines | 358 code | 94 blank | 176 comment | 82 complexity | faa11394405eb6658304cd2cf08a9557 MD5 | raw file
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Name: src/msw/volume.cpp
  3. // Purpose: wxFSVolume - encapsulates system volume information
  4. // Author: George Policello
  5. // Modified by:
  6. // Created: 28 Jan 02
  7. // RCS-ID: $Id$
  8. // Copyright: (c) 2002 George Policello
  9. // Licence: wxWindows licence
  10. ///////////////////////////////////////////////////////////////////////////////
  11. // ============================================================================
  12. // declarations
  13. // ============================================================================
  14. // ----------------------------------------------------------------------------
  15. // headers
  16. // ----------------------------------------------------------------------------
  17. #include "wx/wxprec.h"
  18. #ifdef __BORLANDC__
  19. #pragma hdrstop
  20. #endif
  21. #if wxUSE_FSVOLUME
  22. #include "wx/volume.h"
  23. #ifndef WX_PRECOMP
  24. #if wxUSE_GUI
  25. #include "wx/icon.h"
  26. #endif
  27. #include "wx/intl.h"
  28. #include "wx/log.h"
  29. #include "wx/hashmap.h"
  30. #include "wx/filefn.h"
  31. #endif // WX_PRECOMP
  32. #include "wx/dir.h"
  33. #include "wx/dynlib.h"
  34. #include "wx/arrimpl.cpp"
  35. // some compilers require including <windows.h> before <shellapi.h> so do it
  36. // even if this is not necessary with most of them
  37. #include "wx/msw/wrapwin.h"
  38. #include <shellapi.h>
  39. #include <shlobj.h>
  40. #include "wx/msw/missing.h"
  41. #if wxUSE_BASE
  42. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  43. // Dynamic library function defs.
  44. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  45. #if wxUSE_DYNLIB_CLASS
  46. static wxDynamicLibrary s_mprLib;
  47. #endif
  48. typedef DWORD (WINAPI* WNetOpenEnumPtr)(DWORD, DWORD, DWORD, LPNETRESOURCE, LPHANDLE);
  49. typedef DWORD (WINAPI* WNetEnumResourcePtr)(HANDLE, LPDWORD, LPVOID, LPDWORD);
  50. typedef DWORD (WINAPI* WNetCloseEnumPtr)(HANDLE);
  51. static WNetOpenEnumPtr s_pWNetOpenEnum;
  52. static WNetEnumResourcePtr s_pWNetEnumResource;
  53. static WNetCloseEnumPtr s_pWNetCloseEnum;
  54. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  55. // Globals/Statics
  56. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  57. static long s_cancelSearch = FALSE;
  58. struct FileInfo
  59. {
  60. FileInfo(unsigned flag=0, wxFSVolumeKind type=wxFS_VOL_OTHER) :
  61. m_flags(flag), m_type(type) {}
  62. FileInfo(const FileInfo& other) { *this = other; }
  63. FileInfo& operator=(const FileInfo& other)
  64. {
  65. m_flags = other.m_flags;
  66. m_type = other.m_type;
  67. return *this;
  68. }
  69. unsigned m_flags;
  70. wxFSVolumeKind m_type;
  71. };
  72. WX_DECLARE_STRING_HASH_MAP(FileInfo, FileInfoMap);
  73. // Cygwin bug (?) destructor for global s_fileInfo is called twice...
  74. static FileInfoMap& GetFileInfoMap()
  75. {
  76. static FileInfoMap s_fileInfo(25);
  77. return s_fileInfo;
  78. }
  79. #define s_fileInfo (GetFileInfoMap())
  80. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  81. // Local helper functions.
  82. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  83. //=============================================================================
  84. // Function: GetBasicFlags
  85. // Purpose: Set basic flags, primarily wxFS_VOL_REMOTE and wxFS_VOL_REMOVABLE.
  86. // Notes: - Local and mapped drives are mounted by definition. We have no
  87. // way to determine mounted status of network drives, so assume that
  88. // all drives are mounted, and let the caller decide otherwise.
  89. // - Other flags are 'best guess' from type of drive. The system will
  90. // not report the file attributes with any degree of accuracy.
  91. //=============================================================================
  92. static unsigned GetBasicFlags(const wxChar* filename)
  93. {
  94. unsigned flags = wxFS_VOL_MOUNTED;
  95. //----------------------------------
  96. // 'Best Guess' based on drive type.
  97. //----------------------------------
  98. wxFSVolumeKind type;
  99. switch(GetDriveType(filename))
  100. {
  101. case DRIVE_FIXED:
  102. type = wxFS_VOL_DISK;
  103. break;
  104. case DRIVE_REMOVABLE:
  105. flags |= wxFS_VOL_REMOVABLE;
  106. type = wxFS_VOL_FLOPPY;
  107. break;
  108. case DRIVE_CDROM:
  109. flags |= wxFS_VOL_REMOVABLE | wxFS_VOL_READONLY;
  110. type = wxFS_VOL_CDROM;
  111. break;
  112. case DRIVE_REMOTE:
  113. flags |= wxFS_VOL_REMOTE;
  114. type = wxFS_VOL_NETWORK;
  115. break;
  116. case DRIVE_NO_ROOT_DIR:
  117. flags &= ~wxFS_VOL_MOUNTED;
  118. type = wxFS_VOL_OTHER;
  119. break;
  120. default:
  121. type = wxFS_VOL_OTHER;
  122. break;
  123. }
  124. //-----------------------------------------------------------------------
  125. // The following most likely will not modify anything not set above,
  126. // and will not work at all for network shares or empty CD ROM drives.
  127. // But it is a good check if the Win API ever gets better about reporting
  128. // this information.
  129. //-----------------------------------------------------------------------
  130. SHFILEINFO fi;
  131. long rc = SHGetFileInfo(filename, 0, &fi, sizeof(fi), SHGFI_ATTRIBUTES);
  132. if (!rc)
  133. {
  134. // this error is not fatal, so don't show a message to the user about
  135. // it, otherwise it would appear every time a generic directory picker
  136. // dialog is used and there is a connected network drive
  137. wxLogLastError(wxT("SHGetFileInfo"));
  138. }
  139. else
  140. {
  141. if (fi.dwAttributes & SFGAO_READONLY)
  142. flags |= wxFS_VOL_READONLY;
  143. if (fi.dwAttributes & SFGAO_REMOVABLE)
  144. flags |= wxFS_VOL_REMOVABLE;
  145. }
  146. //------------------
  147. // Flags are cached.
  148. //------------------
  149. s_fileInfo[filename] = FileInfo(flags, type);
  150. return flags;
  151. } // GetBasicFlags
  152. //=============================================================================
  153. // Function: FilteredAdd
  154. // Purpose: Add a file to the list if it meets the filter requirement.
  155. // Notes: - See GetBasicFlags for remarks about the Mounted flag.
  156. //=============================================================================
  157. static bool FilteredAdd(wxArrayString& list, const wxChar* filename,
  158. unsigned flagsSet, unsigned flagsUnset)
  159. {
  160. bool accept = true;
  161. unsigned flags = GetBasicFlags(filename);
  162. if (flagsSet & wxFS_VOL_MOUNTED && !(flags & wxFS_VOL_MOUNTED))
  163. accept = false;
  164. else if (flagsUnset & wxFS_VOL_MOUNTED && (flags & wxFS_VOL_MOUNTED))
  165. accept = false;
  166. else if (flagsSet & wxFS_VOL_REMOVABLE && !(flags & wxFS_VOL_REMOVABLE))
  167. accept = false;
  168. else if (flagsUnset & wxFS_VOL_REMOVABLE && (flags & wxFS_VOL_REMOVABLE))
  169. accept = false;
  170. else if (flagsSet & wxFS_VOL_READONLY && !(flags & wxFS_VOL_READONLY))
  171. accept = false;
  172. else if (flagsUnset & wxFS_VOL_READONLY && (flags & wxFS_VOL_READONLY))
  173. accept = false;
  174. else if (flagsSet & wxFS_VOL_REMOTE && !(flags & wxFS_VOL_REMOTE))
  175. accept = false;
  176. else if (flagsUnset & wxFS_VOL_REMOTE && (flags & wxFS_VOL_REMOTE))
  177. accept = false;
  178. // Add to the list if passed the filter.
  179. if (accept)
  180. list.Add(filename);
  181. return accept;
  182. } // FilteredAdd
  183. //=============================================================================
  184. // Function: BuildListFromNN
  185. // Purpose: Append or remove items from the list
  186. // Notes: - There is no way to find all disconnected NN items, or even to find
  187. // all items while determining which are connected and not. So this
  188. // function will find either all items or connected items.
  189. //=============================================================================
  190. static void BuildListFromNN(wxArrayString& list, NETRESOURCE* pResSrc,
  191. unsigned flagsSet, unsigned flagsUnset)
  192. {
  193. HANDLE hEnum;
  194. int rc;
  195. //-----------------------------------------------
  196. // Scope may be all drives or all mounted drives.
  197. //-----------------------------------------------
  198. unsigned scope = RESOURCE_GLOBALNET;
  199. if (flagsSet & wxFS_VOL_MOUNTED)
  200. scope = RESOURCE_CONNECTED;
  201. //----------------------------------------------------------------------
  202. // Enumerate all items, adding only non-containers (ie. network shares).
  203. // Containers cause a recursive call to this function for their own
  204. // enumeration.
  205. //----------------------------------------------------------------------
  206. if (rc = s_pWNetOpenEnum(scope, RESOURCETYPE_DISK, 0, pResSrc, &hEnum), rc == NO_ERROR)
  207. {
  208. DWORD count = 1;
  209. DWORD size = 256;
  210. NETRESOURCE* pRes = (NETRESOURCE*)malloc(size);
  211. memset(pRes, 0, sizeof(NETRESOURCE));
  212. while (rc = s_pWNetEnumResource(hEnum, &count, pRes, &size), rc == NO_ERROR || rc == ERROR_MORE_DATA)
  213. {
  214. if (s_cancelSearch)
  215. break;
  216. if (rc == ERROR_MORE_DATA)
  217. {
  218. pRes = (NETRESOURCE*)realloc(pRes, size);
  219. count = 1;
  220. }
  221. else if (count == 1)
  222. {
  223. // Enumerate the container.
  224. if (pRes->dwUsage & RESOURCEUSAGE_CONTAINER)
  225. {
  226. BuildListFromNN(list, pRes, flagsSet, flagsUnset);
  227. }
  228. // Add the network share.
  229. else
  230. {
  231. wxString filename(pRes->lpRemoteName);
  232. // if the drive is unavailable, FilteredAdd() can hang for
  233. // a long time and, moreover, its failure appears to be not
  234. // cached so this will happen every time we use it, so try
  235. // a much quicker wxDirExists() test (which still hangs but
  236. // for much shorter time) for locally mapped drives first
  237. // to try to avoid this
  238. if ( pRes->lpLocalName &&
  239. *pRes->lpLocalName &&
  240. !wxDirExists(pRes->lpLocalName) )
  241. continue;
  242. if (!filename.empty())
  243. {
  244. if (filename.Last() != '\\')
  245. filename.Append('\\');
  246. // The filter function will not know mounted from unmounted, and neither do we unless
  247. // we are iterating using RESOURCE_CONNECTED, in which case they all are mounted.
  248. // Volumes on disconnected servers, however, will correctly show as unmounted.
  249. FilteredAdd(list, filename.wx_str(), flagsSet, flagsUnset&~wxFS_VOL_MOUNTED);
  250. if (scope == RESOURCE_GLOBALNET)
  251. s_fileInfo[filename].m_flags &= ~wxFS_VOL_MOUNTED;
  252. }
  253. }
  254. }
  255. else if (count == 0)
  256. break;
  257. }
  258. free(pRes);
  259. s_pWNetCloseEnum(hEnum);
  260. }
  261. } // BuildListFromNN
  262. //=============================================================================
  263. // Function: CompareFcn
  264. // Purpose: Used to sort the NN list alphabetically, case insensitive.
  265. //=============================================================================
  266. static int CompareFcn(const wxString& first, const wxString& second)
  267. {
  268. return wxStricmp(first.c_str(), second.c_str());
  269. } // CompareFcn
  270. //=============================================================================
  271. // Function: BuildRemoteList
  272. // Purpose: Append Network Neighborhood items to the list.
  273. // Notes: - Mounted gets transalated into Connected. FilteredAdd is told
  274. // to ignore the Mounted flag since we need to handle it in a weird
  275. // way manually.
  276. // - The resulting list is sorted alphabetically.
  277. //=============================================================================
  278. static bool BuildRemoteList(wxArrayString& list, NETRESOURCE* pResSrc,
  279. unsigned flagsSet, unsigned flagsUnset)
  280. {
  281. // NN query depends on dynamically loaded library.
  282. if (!s_pWNetOpenEnum || !s_pWNetEnumResource || !s_pWNetCloseEnum)
  283. {
  284. wxLogError(_("Failed to load mpr.dll."));
  285. return false;
  286. }
  287. // Don't waste time doing the work if the flags conflict.
  288. if (flagsSet & wxFS_VOL_MOUNTED && flagsUnset & wxFS_VOL_MOUNTED)
  289. return false;
  290. //----------------------------------------------
  291. // Generate the list according to the flags set.
  292. //----------------------------------------------
  293. BuildListFromNN(list, pResSrc, flagsSet, flagsUnset);
  294. list.Sort(CompareFcn);
  295. //-------------------------------------------------------------------------
  296. // If mounted only is requested, then we only need one simple pass.
  297. // Otherwise, we need to build a list of all NN volumes and then apply the
  298. // list of mounted drives to it.
  299. //-------------------------------------------------------------------------
  300. if (!(flagsSet & wxFS_VOL_MOUNTED))
  301. {
  302. // generate.
  303. wxArrayString mounted;
  304. BuildListFromNN(mounted, pResSrc, flagsSet | wxFS_VOL_MOUNTED, flagsUnset & ~wxFS_VOL_MOUNTED);
  305. mounted.Sort(CompareFcn);
  306. // apply list from bottom to top to preserve indexes if removing items.
  307. ssize_t iList = list.GetCount()-1;
  308. for (ssize_t iMounted = mounted.GetCount()-1; iMounted >= 0 && iList >= 0; iMounted--)
  309. {
  310. int compare;
  311. wxString all(list[iList]);
  312. wxString mount(mounted[iMounted]);
  313. while (compare =
  314. wxStricmp(list[iList].c_str(), mounted[iMounted].c_str()),
  315. compare > 0 && iList >= 0)
  316. {
  317. iList--;
  318. all = list[iList];
  319. }
  320. if (compare == 0)
  321. {
  322. // Found the element. Remove it or mark it mounted.
  323. if (flagsUnset & wxFS_VOL_MOUNTED)
  324. list.RemoveAt(iList);
  325. else
  326. s_fileInfo[list[iList]].m_flags |= wxFS_VOL_MOUNTED;
  327. }
  328. iList--;
  329. }
  330. }
  331. return true;
  332. } // BuildRemoteList
  333. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  334. // wxFSVolume
  335. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  336. //=============================================================================
  337. // Function: GetVolumes
  338. // Purpose: Generate and return a list of all volumes (drives) available.
  339. // Notes:
  340. //=============================================================================
  341. wxArrayString wxFSVolumeBase::GetVolumes(int flagsSet, int flagsUnset)
  342. {
  343. ::InterlockedExchange(&s_cancelSearch, FALSE); // reset
  344. #if wxUSE_DYNLIB_CLASS
  345. if (!s_mprLib.IsLoaded() && s_mprLib.Load(wxT("mpr.dll")))
  346. {
  347. #ifdef UNICODE
  348. s_pWNetOpenEnum = (WNetOpenEnumPtr)s_mprLib.GetSymbol(wxT("WNetOpenEnumW"));
  349. s_pWNetEnumResource = (WNetEnumResourcePtr)s_mprLib.GetSymbol(wxT("WNetEnumResourceW"));
  350. #else
  351. s_pWNetOpenEnum = (WNetOpenEnumPtr)s_mprLib.GetSymbol(wxT("WNetOpenEnumA"));
  352. s_pWNetEnumResource = (WNetEnumResourcePtr)s_mprLib.GetSymbol(wxT("WNetEnumResourceA"));
  353. #endif
  354. s_pWNetCloseEnum = (WNetCloseEnumPtr)s_mprLib.GetSymbol(wxT("WNetCloseEnum"));
  355. }
  356. #endif
  357. wxArrayString list;
  358. //-------------------------------
  359. // Local and mapped drives first.
  360. //-------------------------------
  361. // Allocate the required space for the API call.
  362. const DWORD chars = GetLogicalDriveStrings(0, NULL);
  363. TCHAR* buf = new TCHAR[chars+1];
  364. // Get the list of drives.
  365. GetLogicalDriveStrings(chars, buf);
  366. // Parse the list into an array, applying appropriate filters.
  367. TCHAR *pVol;
  368. pVol = buf;
  369. while (*pVol)
  370. {
  371. FilteredAdd(list, pVol, flagsSet, flagsUnset);
  372. pVol = pVol + wxStrlen(pVol) + 1;
  373. }
  374. // Cleanup.
  375. delete[] buf;
  376. //---------------------------
  377. // Network Neighborhood next.
  378. //---------------------------
  379. // not exclude remote and not removable
  380. if (!(flagsUnset & wxFS_VOL_REMOTE) &&
  381. !(flagsSet & wxFS_VOL_REMOVABLE)
  382. )
  383. {
  384. // The returned list will be sorted alphabetically. We don't pass
  385. // our in since we don't want to change to order of the local drives.
  386. wxArrayString nn;
  387. if (BuildRemoteList(nn, 0, flagsSet, flagsUnset))
  388. {
  389. for (size_t idx = 0; idx < nn.GetCount(); idx++)
  390. list.Add(nn[idx]);
  391. }
  392. }
  393. return list;
  394. } // GetVolumes
  395. //=============================================================================
  396. // Function: CancelSearch
  397. // Purpose: Instruct an active search to stop.
  398. // Notes: - This will only sensibly be called by a thread other than the one
  399. // performing the search. This is the only thread-safe function
  400. // provided by the class.
  401. //=============================================================================
  402. void wxFSVolumeBase::CancelSearch()
  403. {
  404. ::InterlockedExchange(&s_cancelSearch, TRUE);
  405. } // CancelSearch
  406. //=============================================================================
  407. // Function: constructor
  408. // Purpose: default constructor
  409. //=============================================================================
  410. wxFSVolumeBase::wxFSVolumeBase()
  411. {
  412. m_isOk = false;
  413. } // wxVolume
  414. //=============================================================================
  415. // Function: constructor
  416. // Purpose: constructor that calls Create
  417. //=============================================================================
  418. wxFSVolumeBase::wxFSVolumeBase(const wxString& name)
  419. {
  420. Create(name);
  421. } // wxVolume
  422. //=============================================================================
  423. // Function: Create
  424. // Purpose: Finds, logs in, etc. to the request volume.
  425. //=============================================================================
  426. bool wxFSVolumeBase::Create(const wxString& name)
  427. {
  428. // assume fail.
  429. m_isOk = false;
  430. // supplied.
  431. m_volName = name;
  432. // Display name.
  433. SHFILEINFO fi;
  434. long rc = SHGetFileInfo(m_volName.wx_str(), 0, &fi, sizeof(fi), SHGFI_DISPLAYNAME);
  435. if (!rc)
  436. {
  437. wxLogError(_("Cannot read typename from '%s'!"), m_volName.c_str());
  438. return m_isOk;
  439. }
  440. m_dispName = fi.szDisplayName;
  441. // all tests passed.
  442. return m_isOk = true;
  443. } // Create
  444. //=============================================================================
  445. // Function: IsOk
  446. // Purpose: returns true if the volume is legal.
  447. // Notes: For fixed disks, it must exist. For removable disks, it must also
  448. // be present. For Network Shares, it must also be logged in, etc.
  449. //=============================================================================
  450. bool wxFSVolumeBase::IsOk() const
  451. {
  452. return m_isOk;
  453. } // IsOk
  454. //=============================================================================
  455. // Function: GetKind
  456. // Purpose: Return the type of the volume.
  457. //=============================================================================
  458. wxFSVolumeKind wxFSVolumeBase::GetKind() const
  459. {
  460. if (!m_isOk)
  461. return wxFS_VOL_OTHER;
  462. FileInfoMap::iterator itr = s_fileInfo.find(m_volName);
  463. if (itr == s_fileInfo.end())
  464. return wxFS_VOL_OTHER;
  465. return itr->second.m_type;
  466. }
  467. //=============================================================================
  468. // Function: GetFlags
  469. // Purpose: Return the caches flags for this volume.
  470. // Notes: - Returns -1 if no flags were cached.
  471. //=============================================================================
  472. int wxFSVolumeBase::GetFlags() const
  473. {
  474. if (!m_isOk)
  475. return -1;
  476. FileInfoMap::iterator itr = s_fileInfo.find(m_volName);
  477. if (itr == s_fileInfo.end())
  478. return -1;
  479. return itr->second.m_flags;
  480. } // GetFlags
  481. #endif // wxUSE_BASE
  482. // ============================================================================
  483. // wxFSVolume
  484. // ============================================================================
  485. #if wxUSE_GUI
  486. void wxFSVolume::InitIcons()
  487. {
  488. m_icons.Alloc(wxFS_VOL_ICO_MAX);
  489. wxIcon null;
  490. for (int idx = 0; idx < wxFS_VOL_ICO_MAX; idx++)
  491. m_icons.Add(null);
  492. }
  493. //=============================================================================
  494. // Function: GetIcon
  495. // Purpose: return the requested icon.
  496. //=============================================================================
  497. wxIcon wxFSVolume::GetIcon(wxFSIconType type) const
  498. {
  499. wxCHECK_MSG( type >= 0 && (size_t)type < m_icons.GetCount(), wxNullIcon,
  500. wxT("wxFSIconType::GetIcon(): invalid icon index") );
  501. // Load on demand.
  502. if (m_icons[type].IsNull())
  503. {
  504. UINT flags = SHGFI_ICON;
  505. switch (type)
  506. {
  507. case wxFS_VOL_ICO_SMALL:
  508. flags |= SHGFI_SMALLICON;
  509. break;
  510. case wxFS_VOL_ICO_LARGE:
  511. flags |= SHGFI_SHELLICONSIZE;
  512. break;
  513. case wxFS_VOL_ICO_SEL_SMALL:
  514. flags |= SHGFI_SMALLICON | SHGFI_OPENICON;
  515. break;
  516. case wxFS_VOL_ICO_SEL_LARGE:
  517. flags |= SHGFI_SHELLICONSIZE | SHGFI_OPENICON;
  518. break;
  519. case wxFS_VOL_ICO_MAX:
  520. wxFAIL_MSG(wxT("wxFS_VOL_ICO_MAX is not valid icon type"));
  521. break;
  522. }
  523. SHFILEINFO fi;
  524. long rc = SHGetFileInfo(m_volName.wx_str(), 0, &fi, sizeof(fi), flags);
  525. m_icons[type].SetHICON((WXHICON)fi.hIcon);
  526. if (!rc || !fi.hIcon)
  527. {
  528. wxLogError(_("Cannot load icon from '%s'."), m_volName.c_str());
  529. }
  530. }
  531. return m_icons[type];
  532. } // GetIcon
  533. #endif // wxUSE_GUI
  534. #endif // wxUSE_FSVOLUME