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