| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/storage_monitor/storage_monitor_win.h" |
| |
| #include <windows.h> |
| #include <dbt.h> |
| #include <fileapi.h> |
| #include <shlobj.h> |
| |
| #include "base/win/wrapped_window_proc.h" |
| #include "components/storage_monitor/portable_device_watcher_win.h" |
| #include "components/storage_monitor/removable_device_constants.h" |
| #include "components/storage_monitor/storage_info.h" |
| #include "components/storage_monitor/volume_mount_watcher_win.h" |
| |
| #define WM_USER_MEDIACHANGED (WM_USER + 5) |
| |
| // StorageMonitorWin ------------------------------------------------------- |
| |
| namespace storage_monitor { |
| |
| StorageMonitorWin::StorageMonitorWin( |
| VolumeMountWatcherWin* volume_mount_watcher, |
| PortableDeviceWatcherWin* portable_device_watcher) |
| : window_class_(0), |
| instance_(NULL), |
| window_(NULL), |
| shell_change_notify_id_(0), |
| volume_mount_watcher_(volume_mount_watcher), |
| portable_device_watcher_(portable_device_watcher) { |
| DCHECK(volume_mount_watcher_); |
| DCHECK(portable_device_watcher_); |
| volume_mount_watcher_->SetNotifications(receiver()); |
| portable_device_watcher_->SetNotifications(receiver()); |
| } |
| |
| StorageMonitorWin::~StorageMonitorWin() { |
| if (shell_change_notify_id_) |
| SHChangeNotifyDeregister(shell_change_notify_id_); |
| volume_mount_watcher_->SetNotifications(NULL); |
| portable_device_watcher_->SetNotifications(NULL); |
| |
| if (window_) |
| DestroyWindow(window_); |
| |
| if (window_class_) |
| UnregisterClass(MAKEINTATOM(window_class_), instance_); |
| } |
| |
| void StorageMonitorWin::Init() { |
| WNDCLASSEX window_class; |
| base::win::InitializeWindowClass( |
| L"Chrome_StorageMonitorWindow", |
| &base::win::WrappedWindowProc<StorageMonitorWin::WndProcThunk>, |
| 0, 0, 0, NULL, NULL, NULL, NULL, NULL, |
| &window_class); |
| instance_ = window_class.hInstance; |
| window_class_ = RegisterClassEx(&window_class); |
| DCHECK(window_class_); |
| |
| window_ = CreateWindow(MAKEINTATOM(window_class_), 0, 0, 0, 0, 0, 0, 0, 0, |
| instance_, 0); |
| SetWindowLongPtr(window_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this)); |
| volume_mount_watcher_->Init(); |
| portable_device_watcher_->Init(window_); |
| MediaChangeNotificationRegister(); |
| } |
| |
| bool StorageMonitorWin::GetStorageInfoForPath(const base::FilePath& path, |
| StorageInfo* device_info) const { |
| DCHECK(device_info); |
| |
| // TODO(gbillock): Move this logic up to StorageMonitor. |
| // If we already know the StorageInfo for the path, just return it. |
| // This will account for portable devices as well. |
| std::vector<StorageInfo> attached_devices = GetAllAvailableStorages(); |
| size_t best_parent = attached_devices.size(); |
| size_t best_length = 0; |
| for (size_t i = 0; i < attached_devices.size(); i++) { |
| if (!StorageInfo::IsRemovableDevice(attached_devices[i].device_id())) |
| continue; |
| base::FilePath relative; |
| if (base::FilePath(attached_devices[i].location()).AppendRelativePath( |
| path, &relative)) { |
| // Note: the relative path is longer for shorter shared path between |
| // the path and the device mount point, so we want the shortest |
| // relative path. |
| if (relative.value().size() < best_length) { |
| best_parent = i; |
| best_length = relative.value().size(); |
| } |
| } |
| } |
| if (best_parent != attached_devices.size()) { |
| *device_info = attached_devices[best_parent]; |
| return true; |
| } |
| |
| return GetDeviceInfo(path, device_info); |
| } |
| |
| void StorageMonitorWin::EjectDevice( |
| const std::string& device_id, |
| base::Callback<void(EjectStatus)> callback) { |
| StorageInfo::Type type; |
| if (!StorageInfo::CrackDeviceId(device_id, &type, NULL)) { |
| callback.Run(EJECT_FAILURE); |
| return; |
| } |
| |
| if (type == StorageInfo::MTP_OR_PTP) |
| portable_device_watcher_->EjectDevice(device_id, callback); |
| else if (StorageInfo::IsRemovableDevice(device_id)) |
| volume_mount_watcher_->EjectDevice(device_id, callback); |
| else |
| callback.Run(EJECT_FAILURE); |
| } |
| |
| bool StorageMonitorWin::GetMTPStorageInfoFromDeviceId( |
| const std::string& storage_device_id, |
| base::string16* device_location, |
| base::string16* storage_object_id) const { |
| StorageInfo::Type type; |
| StorageInfo::CrackDeviceId(storage_device_id, &type, NULL); |
| return ((type == StorageInfo::MTP_OR_PTP) && |
| portable_device_watcher_->GetMTPStorageInfoFromDeviceId( |
| storage_device_id, device_location, storage_object_id)); |
| } |
| |
| // static |
| LRESULT CALLBACK StorageMonitorWin::WndProcThunk(HWND hwnd, UINT message, |
| WPARAM wparam, LPARAM lparam) { |
| StorageMonitorWin* msg_wnd = reinterpret_cast<StorageMonitorWin*>( |
| GetWindowLongPtr(hwnd, GWLP_USERDATA)); |
| if (msg_wnd) |
| return msg_wnd->WndProc(hwnd, message, wparam, lparam); |
| return ::DefWindowProc(hwnd, message, wparam, lparam); |
| } |
| |
| LRESULT CALLBACK StorageMonitorWin::WndProc(HWND hwnd, UINT message, |
| WPARAM wparam, LPARAM lparam) { |
| switch (message) { |
| case WM_DEVICECHANGE: |
| OnDeviceChange(static_cast<UINT>(wparam), lparam); |
| return TRUE; |
| case WM_USER_MEDIACHANGED: |
| OnMediaChange(wparam, lparam); |
| return TRUE; |
| default: |
| break; |
| } |
| |
| return ::DefWindowProc(hwnd, message, wparam, lparam); |
| } |
| |
| void StorageMonitorWin::MediaChangeNotificationRegister() { |
| LPITEMIDLIST id_list; |
| if (SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &id_list) == NOERROR) { |
| SHChangeNotifyEntry notify_entry; |
| notify_entry.pidl = id_list; |
| notify_entry.fRecursive = TRUE; |
| shell_change_notify_id_ = SHChangeNotifyRegister( |
| window_, SHCNRF_ShellLevel, SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED, |
| WM_USER_MEDIACHANGED, 1, ¬ify_entry); |
| if (!shell_change_notify_id_) |
| DVLOG(1) << "SHChangeNotifyRegister FAILED"; |
| } else { |
| DVLOG(1) << "SHGetSpecialFolderLocation FAILED"; |
| } |
| } |
| |
| bool StorageMonitorWin::GetDeviceInfo(const base::FilePath& device_path, |
| StorageInfo* info) const { |
| DCHECK(info); |
| |
| // TODO(kmadhusu) Implement PortableDeviceWatcherWin::GetDeviceInfo() |
| // function when we have the functionality to add a sub directory of |
| // portable device as a media gallery. |
| return volume_mount_watcher_->GetDeviceInfo(device_path, info); |
| } |
| |
| void StorageMonitorWin::OnDeviceChange(UINT event_type, LPARAM data) { |
| DVLOG(1) << "OnDeviceChange " << event_type << " " << data; |
| volume_mount_watcher_->OnWindowMessage(event_type, data); |
| portable_device_watcher_->OnWindowMessage(event_type, data); |
| } |
| |
| void StorageMonitorWin::OnMediaChange(WPARAM wparam, LPARAM lparam) { |
| volume_mount_watcher_->OnMediaChange(wparam, lparam); |
| } |
| |
| StorageMonitor* StorageMonitor::CreateInternal() { |
| return new StorageMonitorWin(new VolumeMountWatcherWin(), |
| new PortableDeviceWatcherWin()); |
| } |
| |
| } // namespace storage_monitor |