docs: def Enumerate():
      """See base class."""
      hid_guid = GUID()
      hid.HidD_GetHidGuid(ctypes.byref(hid_guid))
  
      devices = setupapi.SetupDiGetClassDevsA(
          ctypes.byref(hid_guid), None, None, 0x12)
      index = 0
      interface_info = DeviceInterfaceData()
      interface_info.cbSize = ctypes.sizeof(DeviceInterfaceData)  # pylint: disable=invalid-name
  
      out = []
      while True:
        result = setupapi.SetupDiEnumDeviceInterfaces(
            devices, 0, ctypes.byref(hid_guid), index,
            ctypes.byref(interface_info))
        index += 1
        if not result:
          break
  
        detail_len = wintypes.DWORD()
        result = setupapi.SetupDiGetDeviceInterfaceDetailA(
            devices, ctypes.byref(interface_info), None, 0,
            ctypes.byref(detail_len), None)
  
        detail_len = detail_len.value
        if detail_len == 0:
          # skip this device, some kind of error
          continue
  
        buf = ctypes.create_string_buffer(detail_len)
        interface_detail = DeviceInterfaceDetailData.from_buffer(buf)
        interface_detail.cbSize = ctypes.sizeof(DeviceInterfaceDetailData)
  
        result = setupapi.SetupDiGetDeviceInterfaceDetailA(
            devices, ctypes.byref(interface_info),
            ctypes.byref(interface_detail), detail_len, None, None)
  
        if not result:
          raise ctypes.WinError()
  
        descriptor = base.DeviceDescriptor()
        # This is a bit of a hack to work around a limitation of ctypes and
        # "header" structures that are common in windows.  DevicePath is a
        # ctypes array of length 1, but it is backed with a buffer that is much
        # longer and contains a null terminated string.  So, we read the null
        # terminated string off DevicePath here.  Per the comment above, the
        # alignment of this struct varies depending on architecture, but
        # in all cases the path string starts 1 DWORD into the structure.
        #
        # The path length is:
        #   length of detail buffer - header length (1 DWORD)
        path_len = detail_len - ctypes.sizeof(wintypes.DWORD)
        descriptor.path = ctypes.string_at(
            ctypes.addressof(interface_detail.DevicePath), path_len)
  
        device = None
        try:
          device = OpenDevice(descriptor.path, True)
        except WindowsError as e:  # pylint: disable=undefined-variable
          if e.winerror == ERROR_ACCESS_DENIED:  # Access Denied, e.g. a keyboard
            continue
          else:
            raise e
  
        try:
          FillDeviceAttributes(device, descriptor)
          FillDeviceCapabilities(device, descriptor)
          out.append(descriptor.ToPublicDict())
        finally:
          kernel32.CloseHandle(device)
  
      return out