При установке своих виртуальных звуковых драйверов я создаю устройство в Enum\Root при помощи SetupDiCreateDeviceInfo, как это делает devcon install. Все прекрасно работает у десятков тысяч пользователей, начиная с Win2k, и до последних версий десятки. Но изредка (несколько раз в год) пользователи восьмерок и десяток жалуются, что то ли удаление, то ли установка проходят неудачно, и последующие попытки установки не могут создать устройство, поскольку оно уже установлено, однако в Device Manager его не видно, даже с DEVMGR_SHOW_NONPRESENT_DEVICES.
В какой именно ситуации это возникает, никто внятно объяснить не смог — к моменту обращения в поддержку они уже успевали по нескольку раз перегрузиться, запустить траблшутеры, чистильщики реестра и прочую хрень. Закономерность пока только одна — в Enum\Root остается ключ недоустановленного устройства с параметром Phantom (REGSTR_VAL_PHANTOM), значение которого 1.
Этот параметр создается во всех системах, начиная с 2k, при выполнении SetupDiCreateDeviceInfo (через MapperSeedKey), и остается, пока не будет выполнено DIF_REGISTERDEVICE. Возможно, установщик в этих редких случаях падает до вызова DIF_REGISTERDEVICE (хотя там падать просто негде), или падает где-то внутри стандартного обработчика DIF_REGISTERDEVICE.
Но вся беда в том, что устройства с ненулевым параметром Phantom невозможно найти и удалить штатными средствами. Они недоступны даже для SetupDiOpenDeviceInfo по Instance ID, тем более — для SetupDiGetClassDevs/DIGCF_ALLCLASSES и SetupDiEnumDeviceInfo. Соответственно, DevCon их тоже не находит. Config Manager их не трогает, своих ключей/значений там не создает.
У CM_Locate_DevNode есть флаг CM_LOCATE_DEVNODE_PHANTOM, но он не для этого:
CM_LOCATE_DEVNODE_PHANTOM — Allows a device instance handle
to be returned for a device instance that is not
currently alive, but that does exist in the registry.
This may be used with other CM APIs that require a
devnode handle, but for which there currently is none
for a particular device (e.g., you want to set a device
registry property for a device not currently present).
This flag does not allow you to locate phantom devnodes
created by using CM_Create_DevNode with the
CM_CREATE_DEVNODE_PHANTOM flag (such device instances
are only accessible by the caller who holds the devnode
handle returned from that API).
Фактически, "phantom devices" в виндовой документации обычно называют "nonpresent devices", которые просто не подключены. Параметра Phantom у таких устройств нет, и все рекомендации, которые гуглятся по ключевым словам (вроде этой или этой), для такого случая не годятся.
Проблему можно легко воспроизвести вручную, создав в Enum\Root любой двухуровневый ключ (например, "00000000\0000") с параметрами ClassGUID, HardwareID и Phantom, со значением последнего 1. Никаким средствами не получается его найти ни в семерке, ни в десятках. Если Phantom обнулить, то устройство становится "phantom" уже в смысле вышеупомянутых рекомендаций. CM_Get_Device_ID_List и SetupDiEnumDeviceInfo его еще не находят, зато CM_Locate_DevNode/CM_LOCATE_DEVNODE_PHANTOM и SetupDiOpenDeviceInfo — уже да. Если выполнить перестройку дерева, то CM сам подхватывает устройство и делает из него DevNode (что, впрочем, не делает его доступным для перечисления).
Мне надо как-то дать пользователю возможность удалить этот зависший ключ, или хотя бы удалить/обнулить параметр Phantom. Править Enum напрямую от имени администратора нельзя — только от имени системы. Приходится отправлять таким юзерам длинную инструкцию по скачиванию и запуску psexec, через который запускать reg delete.
А сегодня один продвинутый юзер, который сам пытался вручную удалить ключ из Enum, еще до того, как я успел отправить ему инструкцию, радостно написал: "нашел в реестре другой ключ с почти с таким же именем, без проблем его удалил, и все заработало". Где и что именно удалил — не помнит.
И что со всем этим можно делать, кроме как клепать для таких юзеров скрипт с psexec или программу, загружающую драйвер, который удалит зависший ключ? Может, таки есть какой-нибудь менее кривой способ найти/поправить его через штатные API?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>... ЕМ>Мне надо как-то дать пользователю возможность удалить этот зависший ключ, или хотя бы удалить/обнулить параметр Phantom. Править Enum напрямую от имени администратора нельзя — только от имени системы. Приходится отправлять таким юзерам длинную инструкцию по скачиванию и запуску psexec, через который запускать reg delete.
О, знакомая проблема. Несколько лет назад сталкивался с похожей ситуацией при установке display driver.
На самом деле удалить ключ можно, не прибегая к psexec и запуску от системы, для этого сначала нужно сделать себя его владельцем,
затем назначить новые права, а затем уже удалить. См. "SeTakeOwnershipPrivilege" — она позволяет администраторам (и всем другим, у
кого есть эта привилегия) становиться владельцем объектов. Ну а владельцы могут устанавливать права в обход заданных разрешений.
Здравствуйте, okman, Вы писали:
O>знакомая проблема
Мне вот очень интересно, как она должна решаться с точки зрения MS. По уму, система должна сама удалять ключи, помеченные Phantom, хотя бы при перезагрузке, но почему-то этого не делает.
А еще интересно, почему я не наблюдал этого эффекта ни в одной из своих тестовых систем, где установка/удаление выполнялись сотни раз на протяжении нескольких лет, и в части этих экспериментов установщик падал. Сумел его воспроизвести, только принудительно завершая установщик до вызова DIF_REGISTERDEVICE.
O>сначала нужно сделать себя его владельцем, затем назначить новые права
О, спасибо, годная идея для юзеров, чтобы не делать для них отдельный удалятор. Я редко имею дело с правами доступа — не догадался проверить такую возможность.
А вот с другим проблемным ключом (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices) это уже не прокатывает — у администраторов нет права делать себя владельцами, им можно только менять значения в подключах. Удалять оттуда мусор приходится через ExecTI и подобные приблуды.
Re[3]: Безобразие с Phantom Marker у устройств Windows
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>А вот с другим проблемным ключом (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices) это уже не прокатывает — у администраторов нет права делать себя владельцами, им можно только менять значения в подключах. Удалять оттуда мусор приходится через ExecTI и подобные приблуды.
Так для этого и нужна вышеупомянутая привилегия. Включаешь SeTakeOwnershipPrivilege и после этого ты можешь открывать любой объект с
правами WRITE_OWNER (смена владельца). Далее можно вызывать SetSecurityInfo c OWNER_SECURITY_INFORMATION, передавая SID владельца,
например группы "Администраторы" (S-1-5-32-544). После этого объект открывается заново с правами WRITE_DAC (смена разрешений), для
владельца объекта это всегда разрешено, и права меняются на нужные. Т.е. снова SetSecurityInfo с DACL_SECURITY_INFORMATION.
И после этого можно с объектом делать все, что угодно. Кстати, есть еще более мощная привилегия — SeRestorePrivilege, она позволяет, в
числе прочего, назначать владельцем объекта любой SID, а не только тот, который входит в текущий контекст безопасности (access token).
Re[3]: Безобразие с Phantom Marker у устройств Windows
ЕМ>А еще интересно, почему я не наблюдал этого эффекта ни в одной из своих тестовых систем, где установка/удаление выполнялись сотни раз на протяжении нескольких лет, и в части этих экспериментов установщик падал. Сумел его воспроизвести, только принудительно завершая установщик до вызова DIF_REGISTERDEVICE.
А девайс случайно не USB? Если да — не может ли воспроизвестись проблема если выдернуть девайс из разъема во время установки драйвера?
Другой вариант — выключить систему или закрыть крышку лаптопа.
Как много веселых ребят, и все делают велосипед...
Здравствуйте, okman, Вы писали:
O>Так для этого и нужна вышеупомянутая привилегия. Включаешь SeTakeOwnershipPrivilege и после этого ты можешь открывать любой объект с O>правами WRITE_OWNER (смена владельца).
Хм, век живи — век учись. Всю жизнь считал, что менять владельца может лишь тот, кому разрешено менять DACL.
Re[4]: Безобразие с Phantom Marker у устройств Windows
Здравствуйте, ononim, Вы писали:
O>А девайс случайно не USB?
Не, чисто виртуальное устройство.
O>Если да — не может ли воспроизвестись проблема если выдернуть девайс из разъема во время установки драйвера?
Здесь установщик, скорее всего, отловит это, и удалит запись.
O>Другой вариант — выключить систему или закрыть крышку лаптопа.
Выключение тоже отловит, если правильно написан. Наверное, можно воспроизвести, если выключить питание при отсутствии аккумулятора. Но и это нужно сделать в короткий промежуток между созданием записи и ее регистрацией в Config Manager.