Chromium Code Reviews| Index: device/serial/serial_device_enumerator_win.cc |
| diff --git a/device/serial/serial_device_enumerator_win.cc b/device/serial/serial_device_enumerator_win.cc |
| index 124c6b720466909d811e86384d65f2c616de8596..6ff2415f7036598192169c5a9ea82c08f7c9374b 100644 |
| --- a/device/serial/serial_device_enumerator_win.cc |
| +++ b/device/serial/serial_device_enumerator_win.cc |
| @@ -4,16 +4,77 @@ |
| #include "device/serial/serial_device_enumerator_win.h" |
| +// This has to be included before windows.h. |
| +#include "third_party/re2/re2/re2.h" |
| + |
| #include <windows.h> |
| +#include <ntddser.h> |
| +#include <setupapi.h> |
| #include "base/memory/scoped_ptr.h" |
| -#include "base/strings/string_util.h" |
| -#include "base/strings/stringprintf.h" |
| +#include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| -#include "base/win/registry.h" |
| namespace device { |
| +namespace { |
| + |
| +// Searches the specified device info for a property with the specified key, |
| +// assigns the result to value, and returns whether the operation was |
| +// successful. |
| +bool GetProperty(HDEVINFO dev_info, |
| + SP_DEVINFO_DATA dev_info_data, |
| + const int key, |
| + std::string* value) { |
| + // We don't know how much space the property's value will take up, so we call |
| + // the property retrieval function once to fetch the size of the required |
| + // value buffer, then again once we've allocated a sufficiently large buffer. |
| + scoped_ptr<wchar_t[]> buffer; |
| + DWORD buffer_size = 0; |
| + while (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, key, NULL, |
| + (PBYTE)buffer.get(), buffer_size, |
|
Ken Rockot(use gerrit already)
2015/09/23 17:30:18
buffer.get() is always nullptr here. Why not just
charliea (OOO until 10-5)
2015/09/28 13:22:53
Yea, the way that this was written with the while
|
| + &buffer_size)) { |
| + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) |
| + buffer.reset(new wchar_t[buffer_size]); |
| + else |
| + return false; |
| + } |
| + |
| + *value = base::WideToUTF8(buffer.get()); |
| + return true; |
| +} |
| + |
| +// Searches for the COM port in the device's friendly name, assigns its value to |
| +// com_port, and returns whether the operation was successful. |
| +bool GetCOMPort(const std::string friendly_name, std::string* com_port) { |
| + return RE2::PartialMatch(friendly_name, ".* \\((COM[0-9]+)\\)", com_port); |
| +} |
| + |
| +// Searches for the display name in the device's friendly name, assigns its |
| +// value to display_name, and returns whether the operation was successful. |
| +bool GetDisplayName(const std::string friendly_name, |
| + std::string* display_name) { |
| + return RE2::PartialMatch(friendly_name, "(.*) \\(COM[0-9]+\\)", display_name); |
| +} |
| + |
| +// Searches for the vendor ID in the device's hardware ID, assigns its value to |
| +// vendor_id, and returns whether the operation was successful. |
| +bool GetVendorID(const std::string hardware_id, uint32_t* vendor_id) { |
| + std::string vendor_id_str; |
| + return RE2::PartialMatch(hardware_id, "VID_([0-9]+)", &vendor_id_str) && |
| + base::HexStringToUInt(vendor_id_str, vendor_id); |
| +} |
| + |
| +// Searches for the product ID in the device's product ID, assigns its value to |
| +// product_id, and returns whether the operation was successful. |
| +bool GetProductID(const std::string hardware_id, uint32_t* product_id) { |
| + std::string product_id_str; |
| + return RE2::PartialMatch(hardware_id, "PID_([0-9]+)", &product_id_str) && |
| + base::HexStringToUInt(product_id_str, product_id); |
| +} |
| + |
| +} // namespace |
| + |
| // static |
| scoped_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create() { |
| return scoped_ptr<SerialDeviceEnumerator>(new SerialDeviceEnumeratorWin()); |
| @@ -23,18 +84,53 @@ SerialDeviceEnumeratorWin::SerialDeviceEnumeratorWin() {} |
| SerialDeviceEnumeratorWin::~SerialDeviceEnumeratorWin() {} |
| -// TODO(rockot): Query the system for more information than just device paths. |
| -// This may or may not require using a different strategy than scanning the |
| -// registry location below. |
| mojo::Array<serial::DeviceInfoPtr> SerialDeviceEnumeratorWin::GetDevices() { |
| - base::win::RegistryValueIterator iter_key( |
| - HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM\\"); |
| mojo::Array<serial::DeviceInfoPtr> devices(0); |
| - for (; iter_key.Valid(); ++iter_key) { |
| + |
| + // Make a device interface query to find all serial devices. |
| + HDEVINFO dev_info = |
| + SetupDiGetClassDevs(&GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR, 0, 0, |
| + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); |
| + if (dev_info == INVALID_HANDLE_VALUE) |
| + return devices.Pass(); |
| + |
| + SP_DEVINFO_DATA dev_info_data; |
| + dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA); |
| + for (DWORD i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { |
| + std::string friendly_name, com_port; |
| + // SPDRP_FRIENDLYNAME looks like "USB_SERIAL_PORT (COM3)". |
| + if (!GetProperty(dev_info, dev_info_data, SPDRP_FRIENDLYNAME, |
| + &friendly_name) || |
| + !GetCOMPort(friendly_name, &com_port)) |
| + // In Windows, the COM port is the path used to uniquely identify the |
| + // serial device. If the COM can't be found, ignore the device. |
| + continue; |
| + |
| serial::DeviceInfoPtr info(serial::DeviceInfo::New()); |
| - info->path = base::UTF16ToASCII(iter_key.Value()); |
| + info->path = com_port; |
| + |
| + std::string display_name; |
| + if (GetDisplayName(friendly_name, &display_name)) |
| + info->display_name = display_name; |
| + |
| + std::string hardware_id; |
| + // SPDRP_HARDWAREID looks like "FTDIBUS\COMPORT&VID_0403&PID_6001". |
| + if (GetProperty(dev_info, dev_info_data, SPDRP_HARDWAREID, &hardware_id)) { |
| + uint32_t vendor_id, product_id; |
| + if (GetVendorID(hardware_id, &vendor_id)) { |
| + info->has_vendor_id = true; |
| + info->vendor_id = vendor_id; |
| + } |
| + if (GetProductID(hardware_id, &product_id)) { |
| + info->has_product_id = true; |
| + info->product_id = product_id; |
| + } |
| + } |
| + |
| devices.push_back(info.Pass()); |
| } |
| + |
| + SetupDiDestroyDeviceInfoList(dev_info); |
| return devices.Pass(); |
| } |