File indexing completed on 2024-05-12 04:01:49

0001 /*
0002     SPDX-FileCopyrightText: 2013 Patrick von Reth <vonreth@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #ifndef WINDEVICEMANAGER_H
0008 #define WINDEVICEMANAGER_H
0009 
0010 #include <solid/devices/ifaces/devicemanager.h>
0011 
0012 #include <QDebug>
0013 #include <QSet>
0014 
0015 #include <qt_windows.h>
0016 #include <winioctl.h>
0017 
0018 inline QString qGetLastError(ulong errorNummber = GetLastError())
0019 {
0020     LPVOID error = NULL;
0021     size_t len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
0022                                NULL,
0023                                errorNummber,
0024                                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
0025                                (LPWSTR)&error,
0026                                0,
0027                                NULL);
0028     QString out = QString::fromWCharArray((wchar_t *)error, (int)len).trimmed().append(" %1").arg(errorNummber);
0029     LocalFree(error);
0030     return out;
0031 }
0032 
0033 namespace Solid
0034 {
0035 namespace Backends
0036 {
0037 namespace Win
0038 {
0039 class WinDeviceManager : public Solid::Ifaces::DeviceManager
0040 {
0041     Q_OBJECT
0042 public:
0043     WinDeviceManager(QObject *parent = 0);
0044     ~WinDeviceManager();
0045 
0046     virtual QString udiPrefix() const;
0047 
0048     virtual QSet<Solid::DeviceInterface::Type> supportedInterfaces() const;
0049 
0050     virtual QStringList allDevices();
0051 
0052     virtual QStringList devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type = Solid::DeviceInterface::Unknown);
0053 
0054     virtual QObject *createDevice(const QString &udi);
0055 
0056     static const WinDeviceManager *instance();
0057 
0058     template<class INFO>
0059     static INFO getDeviceInfo(const QString &devName, int code)
0060     {
0061         return getDeviceInfo<INFO, void *>(devName, code);
0062     }
0063 
0064     template<class INFO, class QUERY>
0065     static INFO getDeviceInfo(const QString &devName, int code, QUERY *query = NULL)
0066     {
0067         INFO info;
0068         ZeroMemory(&info, sizeof(INFO));
0069         getDeviceInfoPrivate(devName, code, &info, sizeof(INFO), query);
0070         return info;
0071     }
0072 
0073     template<class BUFFER_TYPE, class QUERY>
0074     static void getDeviceInfo(const QString &devName, int code, BUFFER_TYPE *out, DWORD outSize, QUERY *query = NULL)
0075     {
0076         ZeroMemory(out, sizeof(BUFFER_TYPE) * outSize);
0077         getDeviceInfoPrivate(devName, code, out, outSize, query);
0078     }
0079 
0080     static void deviceAction(const QString &devName, int code)
0081     {
0082         getDeviceInfoPrivate<void, void *>(devName, code, NULL, 0, NULL);
0083     }
0084 
0085 Q_SIGNALS:
0086     void powerChanged();
0087 
0088 private Q_SLOTS:
0089     void updateDeviceList();
0090     void slotDeviceAdded(const QSet<QString> &udi);
0091     void slotDeviceRemoved(const QSet<QString> &udi);
0092 
0093 private:
0094     friend class SolidWinEventFilter;
0095 
0096     QSet<QString> m_devices;
0097     QStringList m_devicesList;
0098     QSet<Solid::DeviceInterface::Type> m_supportedInterfaces;
0099 
0100     template<class INFO, class QUERY>
0101     static void getDeviceInfoPrivate(const QString &devName, int code, INFO *info, DWORD size, QUERY *query = NULL)
0102     {
0103         Q_ASSERT(!devName.isNull());
0104         wchar_t deviceNameBuffer[MAX_PATH];
0105         QString dev = devName;
0106         if (!dev.startsWith("\\")) {
0107             dev = QLatin1String("\\\\?\\") + dev;
0108         }
0109         deviceNameBuffer[dev.toWCharArray(deviceNameBuffer)] = 0;
0110         DWORD bytesReturned = 0;
0111 
0112         ulong err = NO_ERROR;
0113         HANDLE handle = ::CreateFileW(deviceNameBuffer, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
0114         if (handle == INVALID_HANDLE_VALUE) {
0115             err = GetLastError();
0116             if (err == ERROR_ACCESS_DENIED) {
0117                 // we would need admin rights for GENERIC_READ on systenm drives and volumes
0118                 handle = ::CreateFileW(deviceNameBuffer, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
0119                 err = GetLastError();
0120             }
0121             if (handle == INVALID_HANDLE_VALUE) {
0122                 qWarning() << "Invalid Handle" << dev << "reason:" << qGetLastError(err) << "this should not happen.";
0123                 return;
0124             }
0125         }
0126         if (::DeviceIoControl(handle, code, query, sizeof(QUERY), info, size, &bytesReturned, NULL)) {
0127             ::CloseHandle(handle);
0128             return;
0129         }
0130 
0131         if (handle == INVALID_HANDLE_VALUE) {
0132             qWarning() << "Invalid Handle" << devName << "reason:" << qGetLastError() << "is probaply a subst path or more seriously there is bug!";
0133             return;
0134         }
0135 
0136         err = GetLastError();
0137         switch (err) {
0138         case ERROR_NOT_READY:
0139             // the drive is a cd drive with no disk
0140             break;
0141         case ERROR_INVALID_FUNCTION:
0142             // in most cases this means that the device doesn't support this method, like temperature for some batteries
0143             break;
0144         default:
0145             qWarning() << "Failed to query" << dev << "reason:" << qGetLastError(err);
0146             // DebugBreak();
0147         }
0148         ::CloseHandle(handle);
0149     }
0150 };
0151 
0152 }
0153 }
0154 }
0155 #endif // WINDEVICEMANAGER_H