diff --git a/CMakeLists.txt b/CMakeLists.txt index ad57007cf..c5e85a357 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ pkg_check_modules(LIBMTP libmtp) if (WIN32) find_package(ZLIB REQUIRED) - find_library(MSWMDM_LIBRARIES MSWMDM) + find_library(MSWMDM_LIBRARIES mswmdm) find_library(SAC_SHIM_LIBRARIES sac_shim) endif (WIN32) diff --git a/src/core/song.cpp b/src/core/song.cpp index 1845bcf44..44e7e91ea 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -53,91 +53,6 @@ #ifdef Q_OS_WIN32 # include <mswmdm.h> # include <QUuid> - - // Copied from the WMDM SDK 11, it doesn't seem to be included in SDK 9 for - // some reason, even though the devices use it. - typedef enum tagWMDM_FORMATCODE { - WMDM_FORMATCODE_NOTUSED = 0x0000, - WMDM_FORMATCODE_ALLIMAGES = 0xFFFFFFFF, - WMDM_FORMATCODE_UNDEFINED = 0x3000, - WMDM_FORMATCODE_ASSOCIATION = 0x3001, - WMDM_FORMATCODE_SCRIPT = 0x3002, - WMDM_FORMATCODE_EXECUTABLE = 0x3003, - WMDM_FORMATCODE_TEXT = 0x3004, - WMDM_FORMATCODE_HTML = 0x3005, - WMDM_FORMATCODE_DPOF = 0x3006, - WMDM_FORMATCODE_AIFF = 0x3007, - WMDM_FORMATCODE_WAVE = 0x3008, - WMDM_FORMATCODE_MP3 = 0x3009, - WMDM_FORMATCODE_AVI = 0x300A, - WMDM_FORMATCODE_MPEG = 0x300B, - WMDM_FORMATCODE_ASF = 0x300C, - WMDM_FORMATCODE_RESERVED_FIRST = 0x300D, - WMDM_FORMATCODE_RESERVED_LAST = 0x37FF, - WMDM_FORMATCODE_IMAGE_UNDEFINED = 0x3800, - WMDM_FORMATCODE_IMAGE_EXIF = 0x3801, - WMDM_FORMATCODE_IMAGE_TIFFEP = 0x3802, - WMDM_FORMATCODE_IMAGE_FLASHPIX = 0x3803, - WMDM_FORMATCODE_IMAGE_BMP = 0x3804, - WMDM_FORMATCODE_IMAGE_CIFF = 0x3805, - WMDM_FORMATCODE_IMAGE_GIF = 0x3807, - WMDM_FORMATCODE_IMAGE_JFIF = 0x3808, - WMDM_FORMATCODE_IMAGE_PCD = 0x3809, - WMDM_FORMATCODE_IMAGE_PICT = 0x380A, - WMDM_FORMATCODE_IMAGE_PNG = 0x380B, - WMDM_FORMATCODE_IMAGE_TIFF = 0x380D, - WMDM_FORMATCODE_IMAGE_TIFFIT = 0x380E, - WMDM_FORMATCODE_IMAGE_JP2 = 0x380F, - WMDM_FORMATCODE_IMAGE_JPX = 0x3810, - WMDM_FORMATCODE_IMAGE_RESERVED_FIRST = 0x3811, - WMDM_FORMATCODE_IMAGE_RESERVED_LAST = 0x3FFF, - WMDM_FORMATCODE_UNDEFINEDFIRMWARE = 0xB802, - WMDM_FORMATCODE_WINDOWSIMAGEFORMAT = 0xB881, - WMDM_FORMATCODE_UNDEFINEDAUDIO = 0xB900, - WMDM_FORMATCODE_WMA = 0xB901, - WMDM_FORMATCODE_OGG = 0xB902, - WMDM_FORMATCODE_AAC = 0xB903, - WMDM_FORMATCODE_AUDIBLE = 0xB904, - WMDM_FORMATCODE_FLAC = 0xB906, - WMDM_FORMATCODE_UNDEFINEDVIDEO = 0xB980, - WMDM_FORMATCODE_WMV = 0xB981, - WMDM_FORMATCODE_MP4 = 0xB982, - WMDM_FORMATCODE_MP2 = 0xB983, - WMDM_FORMATCODE_UNDEFINEDCOLLECTION = 0xBA00, - WMDM_FORMATCODE_ABSTRACTMULTIMEDIAALBUM = 0xBA01, - WMDM_FORMATCODE_ABSTRACTIMAGEALBUM = 0xBA02, - WMDM_FORMATCODE_ABSTRACTAUDIOALBUM = 0xBA03, - WMDM_FORMATCODE_ABSTRACTVIDEOALBUM = 0xBA04, - WMDM_FORMATCODE_ABSTRACTAUDIOVIDEOPLAYLIST = 0xBA05, - WMDM_FORMATCODE_ABSTRACTCONTACTGROUP = 0xBA06, - WMDM_FORMATCODE_ABSTRACTMESSAGEFOLDER = 0xBA07, - WMDM_FORMATCODE_ABSTRACTCHAPTEREDPRODUCTION = 0xBA08, - WMDM_FORMATCODE_WPLPLAYLIST = 0xBA10, - WMDM_FORMATCODE_M3UPLAYLIST = 0xBA11, - WMDM_FORMATCODE_MPLPLAYLIST = 0xBA12, - WMDM_FORMATCODE_ASXPLAYLIST = 0xBA13, - WMDM_FORMATCODE_PLSPLAYLIST = 0xBA14, - WMDM_FORMATCODE_UNDEFINEDDOCUMENT = 0xBA80, - WMDM_FORMATCODE_ABSTRACTDOCUMENT = 0xBA81, - WMDM_FORMATCODE_XMLDOCUMENT = 0xBA82, - WMDM_FORMATCODE_MICROSOFTWORDDOCUMENT= 0xBA83, - WMDM_FORMATCODE_MHTCOMPILEDHTMLDOCUMENT = 0xBA84, - WMDM_FORMATCODE_MICROSOFTEXCELSPREADSHEET = 0xBA85, - WMDM_FORMATCODE_MICROSOFTPOWERPOINTDOCUMENT = 0xBA86, - WMDM_FORMATCODE_UNDEFINEDMESSAGE = 0xBB00, - WMDM_FORMATCODE_ABSTRACTMESSAGE = 0xBB01, - WMDM_FORMATCODE_UNDEFINEDCONTACT = 0xBB80, - WMDM_FORMATCODE_ABSTRACTCONTACT = 0xBB81, - WMDM_FORMATCODE_VCARD2 = 0xBB82, - WMDM_FORMATCODE_VCARD3 = 0xBB83, - WMDM_FORMATCODE_UNDEFINEDCALENDARITEM = 0xBE00, - WMDM_FORMATCODE_ABSTRACTCALENDARITEM = 0xBE01, - WMDM_FORMATCODE_VCALENDAR1 = 0xBE02, - WMDM_FORMATCODE_VCALENDAR2 = 0xBE03, - WMDM_FORMATCODE_UNDEFINEDWINDOWSEXECUTABLE = 0xBE80, - WMDM_FORMATCODE_MEDIA_CAST = 0xBE81, - WMDM_FORMATCODE_SECTION = 0xBE82 - } WMDM_FORMATCODE; #endif // Q_OS_WIN32 #include <boost/scoped_array.hpp> diff --git a/src/devices/wmdmlister.cpp b/src/devices/wmdmlister.cpp index bd5869dcf..bfd579a05 100644 --- a/src/devices/wmdmlister.cpp +++ b/src/devices/wmdmlister.cpp @@ -14,13 +14,15 @@ along with Clementine. If not, see <http://www.gnu.org/licenses/>. */ +#define _WIN32_WINNT 0x0501 + #include "wmdmlister.h" #include "wmdmthread.h" #include "core/utilities.h" -#include <icomponentauthenticate.h> #include <objbase.h> #include <mswmdm_i.c> +#include <winbase.h> #include <boost/bind.hpp> #include <boost/scoped_array.hpp> @@ -29,6 +31,9 @@ #include <QStringList> #include <QtDebug> +const QUuid WmdmLister::kDeviceProtocolMsc( + 0xa4d2c26c, 0xa881, 0x44bb, 0xbd, 0x5d, 0x1f, 0x70, 0x3c, 0x71, 0xf7, 0xa9); + QString WmdmLister::CanonicalNameToId(const QString& canonical_name) { return "wmdm/" + canonical_name; } @@ -139,14 +144,23 @@ WmdmLister::DeviceInfo WmdmLister::ReadDeviceInfo(IWMDMDevice2* device) { const int max_size = 512; wchar_t buf[max_size]; device->GetName(buf, max_size); - ret.name_ = QString::fromWCharArray(buf); + ret.name_ = QString::fromWCharArray(buf).trimmed(); device->GetManufacturer(buf, max_size); - ret.manufacturer_ = QString::fromWCharArray(buf); + ret.manufacturer_ = QString::fromWCharArray(buf).trimmed(); device->GetCanonicalName(buf, max_size); ret.canonical_name_ = QString::fromWCharArray(buf).toLower(); + // Upgrade to a device3 + IWMDMDevice3* device3 = NULL; + device->QueryInterface(IID_IWMDMDevice3, (void**)&device3); + + // Get the device protocol so we can figure out whether the device is MSC + PROPVARIANT protocol; + device3->GetProperty(g_wszWMDMDeviceProtocol, &protocol); + device3->Release(); + // Get the type and check whether it has storage DWORD type = 0; device->GetType(&type); @@ -180,34 +194,99 @@ WmdmLister::DeviceInfo WmdmLister::ReadDeviceInfo(IWMDMDevice2* device) { // There doesn't seem to be a way to get the drive letter of MSC devices, so // try parsing the device's name to extract it. - QRegExp drive_letter("\\(([A-Z]:)\\)$"); - if (drive_letter.indexIn(ret.name_) != -1) { - // Sanity check to make sure there really is a drive there - ScopedWCharArray path(drive_letter.cap(1) + "\\"); - ScopedWCharArray name(QString(MAX_PATH + 1, '\0')); - ScopedWCharArray type(QString(MAX_PATH + 1, '\0')); - DWORD serial = 0; - - if (!GetVolumeInformationW( - path, - name, MAX_PATH, - &serial, - NULL, // max component length - NULL, // flags - type, MAX_PATH // fat or ntfs - )) { - qWarning() << "Error getting volume information for" << drive_letter.cap(1); - } else { - ret.mount_point_ = drive_letter.cap(1) + "/"; - ret.fs_name_ = name.ToString(); - ret.fs_type_ = type.ToString(); - ret.fs_serial_ = serial; - } - } + if (QUuid(*protocol.puuid) == kDeviceProtocolMsc) + GuessDriveLetter(&ret); return ret; } +void WmdmLister::GuessDriveLetter(DeviceInfo* info) { + // Windows XP puts the drive letter in brackets at the end of the name + QRegExp drive_letter("\\(([A-Z]:)\\)$"); + if (drive_letter.indexIn(info->name_) != -1) { + CheckDriveLetter(info, drive_letter.cap(1)); + return; + } + + // Windows 7 sometimes has the drive letter as the whole name + drive_letter = QRegExp("^([A-Z]:)\\\\$"); + if (drive_letter.indexIn(info->name_) != -1) { + CheckDriveLetter(info, drive_letter.cap(1)); + return; + } + + // Otherwise Windows 7 uses the drive's DOS label as its whole name. + // Let's enumerate all the volumes on the system and find one with that + // label, then get its drive letter. Yay! + wchar_t volume_name[MAX_PATH + 1]; + HANDLE handle = FindFirstVolumeW(volume_name, MAX_PATH); + + forever { + // QueryDosDeviceW doesn't allow a trailing backslash, so remove it. + int length = wcslen(volume_name); + volume_name[length - 1] = L'\0'; + + wchar_t device_name[MAX_PATH + 1]; + QueryDosDeviceW(&volume_name[4], device_name, MAX_PATH); + + volume_name[length - 1] = L'\\'; + + // Don't do cd-roms or floppies + if (QString::fromWCharArray(device_name).contains("HarddiskVolume")) { + wchar_t volume_path[MAX_PATH + 1]; + DWORD volume_path_length = MAX_PATH; + GetVolumePathNamesForVolumeNameW( + volume_name, volume_path, volume_path_length, &volume_path_length); + + if (wcslen(volume_path) == 3) { + ScopedWCharArray name(QString(MAX_PATH + 1, '\0')); + ScopedWCharArray type(QString(MAX_PATH + 1, '\0')); + DWORD serial = 0; + + if (!GetVolumeInformationW(volume_path, name, MAX_PATH, + &serial, NULL, NULL, type, MAX_PATH)) { + qWarning() << "Error getting volume information for" << + QString::fromWCharArray(volume_path); + } else { + if (name.ToString() == info->name_ && name.characters() != 0) { + // We found it! + CheckDriveLetter(info, QString::fromWCharArray(volume_path)); + break; + } + } + } + } + + if (!FindNextVolumeW(handle, volume_name, MAX_PATH)) + break; + } + FindVolumeClose(handle); +} + +void WmdmLister::CheckDriveLetter(DeviceInfo* info, const QString& drive) { + // Sanity check to make sure there really is a drive there + ScopedWCharArray path(drive.endsWith('\\') ? drive : (drive + "\\")); + ScopedWCharArray name(QString(MAX_PATH + 1, '\0')); + ScopedWCharArray type(QString(MAX_PATH + 1, '\0')); + DWORD serial = 0; + + if (!GetVolumeInformationW( + path, + name, MAX_PATH, + &serial, + NULL, // max component length + NULL, // flags + type, MAX_PATH // fat or ntfs + )) { + qWarning() << "Error getting volume information for" << drive; + } else { + info->mount_point_ = drive + "/"; + info->fs_name_ = name.ToString(); + info->fs_type_ = type.ToString(); + info->fs_serial_ = serial; + } +} + QStringList WmdmLister::DeviceUniqueIDs() { QMutexLocker l(&mutex_); return devices_.keys(); diff --git a/src/devices/wmdmlister.h b/src/devices/wmdmlister.h index 7ef646bd8..f32c72428 100644 --- a/src/devices/wmdmlister.h +++ b/src/devices/wmdmlister.h @@ -22,6 +22,7 @@ #include <QMap> #include <QMutex> #include <QPixmap> +#include <QUuid> #include <boost/scoped_ptr.hpp> @@ -98,12 +99,16 @@ private: int fs_serial_; }; + static const QUuid kDeviceProtocolMsc; + DeviceInfo ReadDeviceInfo(IWMDMDevice2* device); template <typename T> T LockAndGetDeviceInfo(const QString& id, T DeviceInfo::*field); void UpdateFreeSpace(DeviceInfo* info); + void GuessDriveLetter(DeviceInfo* info); + void CheckDriveLetter(DeviceInfo* info, const QString& drive); static QString CanonicalNameToId(const QString& canonical_name); void WMDMDeviceAdded(const QString& canonical_name);