]> git.saurik.com Git - wxWidgets.git/blame - src/msw/volume.cpp
Fixed bug with disappearing menus. They were
[wxWidgets.git] / src / msw / volume.cpp
CommitLineData
7183fd72
VZ
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
47static wxDynamicLibrary s_mprLib;
48
49typedef DWORD (WINAPI* WNetOpenEnumPtr)(DWORD, DWORD, DWORD, LPNETRESOURCE, LPHANDLE);
50typedef DWORD (WINAPI* WNetEnumResourcePtr)(HANDLE, LPDWORD, LPVOID, LPDWORD);
51typedef DWORD (WINAPI* WNetCloseEnumPtr)(HANDLE);
52
53static WNetOpenEnumPtr s_pWNetOpenEnum;
54static WNetEnumResourcePtr s_pWNetEnumResource;
55static WNetCloseEnumPtr s_pWNetCloseEnum;
56
57//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
58// Globals/Statics
59//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
60static long s_cancelSearch = FALSE;
61
62struct 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};
69WX_DECLARE_STRING_HASH_MAP(FileInfo, FileInfoMap);
70static FileInfoMap s_fileInfo(25);
71
72//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
73// Other initialization.
74//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
75WX_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//=============================================================================
90unsigned 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//=============================================================================
163bool 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//=============================================================================
199void 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//=============================================================================
271static 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//=============================================================================
284bool 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//=============================================================================
357wxArrayString 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//=============================================================================
427void wxFSVolume::CancelSearch()
428{
429 InterlockedExchange(&s_cancelSearch, TRUE);
430} // CancelSearch
431
432//=============================================================================
433// Function: constructor
434// Purpose: default constructor
435//=============================================================================
436wxFSVolume::wxFSVolume()
437{
438 m_isOk = FALSE;
439} // wxVolume
440
441//=============================================================================
442// Function: constructor
443// Purpose: constructor that calls Create
444//=============================================================================
445wxFSVolume::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//=============================================================================
454bool 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//=============================================================================
492bool wxFSVolume::IsOk() const
493{
494 return m_isOk;
495} // IsOk
496
497//=============================================================================
498// Function: GetKind
499// Purpose: Return the type of the volume.
500//=============================================================================
501wxFSVolumeKind 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//=============================================================================
518int 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//=============================================================================
536wxIcon 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