Проблема синхронизации данных в пакетном скрипте при запросе данных регистра, мне нужно что-то вроде RegFlushKey, но в пакетном режиме

Я запрашиваю значения регистров в рамках модульного тестирования пакетного сценария, который настроит сеть для наших клиентов. Я обнаружил состояние гонки, при котором запрос регистра после установки для него значения в той же функции приводит к тому, что старое значение отображается до тех пор, пока вы повторно не запустите командный файл. Пример в моем блоке кода выглядит следующим образом:

:MyFunctionName
REM some initialization code

powercfg /change monitor-timeout-ac 2 1> nul 
powercfg /change standby-timeout-ac 2 1> nul 
powercfg /change hibernate-timeout-ac 2 1> nul

REM code that gets register paths setup and does some other irrelevant stuff
REM MonitorTimeoutPath is the path to the first powercfg setting
REM StandbyTimeoutPath is the path to the second setting
REM HibernateTimeoutPath is the path to the third setting

FOR /F "tokens=1-3 skip=2" %%A IN ('reg query "!MonitorTimeoutPath!" /v ACSettingIndex') DO (set /A "ActualMonitorTimeout=%%C/60")
FOR /F "tokens=1-3 skip=2" %%A IN ('reg query "!StandbyTimeoutPath!" /v ACSettingIndex') DO (set /A "ActualStandbyTimeout=%%C/60")
FOR /F "tokens=1-3 skip=2" %%A IN ('reg query "!HibernateTimeoutPath!" /v ACSettingIndex') DO (set /A "ActualHibernateTimeout=%%C/60")

REM code that tests values but is important for now

echo M:!ActualMonitorTimeout! S:!ActualStandbyTimeout! H:!ActualHibernateTimeout!
goto:EOF

Теперь вот что важно: когда я устанавливаю три параметра powercfg на разные значения (скажем, 1,1,2 соответственно), в ПЕРВЫЙ раз, когда я запускаю командный файл, оператор echo показывает мне значения 0,0,0 и только тогда, когда Я запускаю файл ВТОРОЙ раз и вижу истинные значения 1,1,2. Поскольку я на 99,99% уверен, что неправильно обращаюсь к нему, потому что я использую ОЧЕНЬ похожие операторы FOR для захвата данных регистра, когда я делаю это за пределами той же функции, например:

reg add "HKEY_CURRENT_USER\Control Panel\Accessibility\StickyKeys" /v Flags /t REG_SZ /d 10 /f 1> nul 
call :RegTestFunc "HKEY_CURRENT_USER\Control Panel\Accessibility\StickyKeys" Flags 10

:RegTestFunc
REM some code
FOR /F "tokens=1-3 skip=2" %%A IN ('reg query "%~1" /v %~2 2^>nul') DO (set /A "RegisterData=%%C")
REM some more code
goto:EOF

Таким образом, практически нет никакой разницы, кроме некоторых математических различий (поэтому я не мог использовать эту функцию) и некоторого синтаксиса (потому что это функция). Дело в том, что мой синтаксис здесь в порядке, и он не вызывает состояния гонки, когда я использую RegTestFunc. Это происходит только тогда, когда я напрямую запускаю операцию запроса в той же функции, в которой я установил значение регистра.

Чтобы попытаться решить эту проблему, я попытался исследовать, какая (если есть) форма пакета управления временем, и я наткнулся на команду тайм-аута. Я использовал его, думая, что могу задержать процессор, чтобы значение регистра изменилось к моменту запроса. В частности, я помещаю его перед тремя операторами FOR, которые запрашивают данные из регистра. Это не сработало, поэтому я спрашиваю здесь, потому что Google - бесполезная поисковая машина для чего-либо интересного. Есть ли простой способ заблокировать операцию запроса или ее результаты, чтобы я всегда получал самые последние изменения? Я почти уверен, что cmd работает так быстро, что команда powercfg / change не может выполнить свою работу до того, как появится команда reg query и запросит устаревшие данные. В случае необходимости я мог бы просто создать другую функцию и вызвать ее и посмотреть, изменится ли это, но это беспорядочно, поэтому я бы хотел этого избежать.

Для справки: Каким будет технический термин для обозначения этой проблемы? Я не рассматриваю это как состояние гонки, потому что не существует нескольких потоков, конкурирующих за одни и те же данные регистра, это не проблема производителя-потребителя, потому что данные есть, они просто не распознаются. Однако эти концепции очень похожи на то, что происходит на самом деле (по крайней мере, для меня), так как бы назвать эту «проблему»? Данные меняются / запрашиваются слишком быстро, но как это формально называться (если есть)? Моей первой мыслью было состояние гонки, затем потребитель-производитель, затем устаревшие данные, после чего я решил, что проблема синхронизации - это лучшие слова, которые я бы использовал для описания того, что, по моему мнению, происходит.

ОБНОВИТЬ

Я попытался изменить все важные переменные с отложенным расширением (!) В моем коде на без задержки (%), думая, что, возможно, он захватил старые значения с помощью расширения и области, но это не сработало, регистры все еще устарели после одного запуска сценария. Я также попытался дважды запросить регистры и использовать вспомогательную функцию, чтобы отделить код и посмотреть, сработало ли это, но все равно это не сработало. Я только предполагаю, что изменения в powercfg не завершаются до тех пор, пока не будет достигнута последняя инструкция пакетного скрипта, поэтому мой вопрос: как мне запросить точные данные до этого?

ОБНОВЛЕНИЕ 2

Я пробовал экспортировать данные регистра сразу после того, как установил его.

REM RUNNING THE FILE THE FIRST TIME ASSUMING THE VALUES WERE ALL 0 BEFORE
powercfg /change monitor-timeout-ac 3 1> nul 
powercfg /change standby-timeout-ac 3 1> nul 
powercfg /change hibernate-timeout-ac 3 1> nul

REM some code
REM the below isn't runnable but the idea is that it contains the path to 
REM powercfg /change monitor-timeout-ac
reg export %MonitorTimeoutPath% foo.reg

(в foo.reg)

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\User\PowerSchemes\f4e62c59-ee57-456a-94c4-3662e9d6ceb9\7516b95f-f776-4464-8c53-06167f40cc99\3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e] "ACSettingIndex"=dword:00000000

Обратите внимание на шестнадцатеричное значение 00 в конце (0 секунд или 0 минут)

Затем, если вы запустите приведенный выше скрипт ДАЖЕ без строк:

REM RUNNING THE FILE THE SECOND TIME
powercfg /change monitor-timeout-ac 3 1> nul 
powercfg /change standby-timeout-ac 3 1> nul 
powercfg /change hibernate-timeout-ac 3 1> nul

Вы получаете правильное значение

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\User\PowerSchemes\f4e62c59-ee57-456a-94c4-3662e9d6ceb9\7516b95f-f776-4464-8c53-06167f40cc99\3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e] "ACSettingIndex"=dword:000000B4

(шестнадцатеричный 180 секунд или 3 минуты)

И только после двойного запуска командного файла фактическое значение обновляется ДАЖЕ ЕСЛИ я закомментирую строки записи powercfg при втором запуске, чтобы запись в реестр происходила только один раз. Это означает, что сам реестр еще не получил данные, когда мы запрашиваем сразу после изменения настроек powercfg. Я изучил проблему, и мне нужна реализация

RegFlushKey(hkey key);

Что, по-видимому, существует в C и заставляет обновить реестр указанного ключа (он немедленно записывает этот реестр на «диск» и останавливает все остальное, замедляя тем самым). Все, что подробно описано здесь: https://docs.microsoft.com/en-gb/windows/desktop/api/winreg/nf-winreg-regflushkey, и мне нужен способ сделать это в основном в пакетном режиме.

Что такое компоненты React? Введение в компоненты | Типы компонентов
Что такое компоненты React? Введение в компоненты | Типы компонентов
Компонент - это независимый, многократно используемый фрагмент кода, который делит пользовательский интерфейс на более мелкие части. Например, если мы...
0
0
152
1

Ответы 1

Проблема

Я выяснил, в чем дело. Я запрашивал DuplicateScheme, а не ActiveScheme.

При разработке этого скрипта нам пришлось включить функцию изменения настроек питания в Windows 10 pro. Обычно вы не можете сразу установить активную схему на GUID по умолчанию для обеспечения высокой производительности, потому что Windows 10 Pro - это странно. Вместо этого вы должны использовать DuplicateScheme в высокопроизводительном гиде, чтобы вы могли создать новый гид с теми же настройками. Эта же строка кода также устанавливает его как ActiveScheme (этот термин будет иметь важное значение).

Когда сценарий изначально находился в разработке, он также предназначался для работы с серверной версией Windows 2016 (а также с некоторыми другими версиями). Эти версии не требовали техники DuplicateScheme, и вместо этого вы должны были напрямую настроить ActiveScheme на высокую мощность. Попытка использовать метод DuplicateScheme в версиях Windows 2016 не должен и не сделал работает должным образом.

Моя ошибка заключалась в том, что, хотя я запускал метод DuplicateScheme в версии Windows 2016 Server, я думал, что это не повлияет ни на что другое. Я был неправ. Поскольку метод DuplicateScheme не работает на Windows 2016 Server verison, PowerScheme Guid, который я запрашивал, никогда не был настроен на ActiveScheme. У меня была проверка ошибок, чтобы найти эту ошибку, потому что я ожидал, чтобы это произошло (и это произошло, что хорошо). Однако, поскольку я запрашивал результаты изменений Powercfg, я пытался запросить свой DuplicateScheme на предмет изменений, которые были сделаны на ActiveScheme. Поскольку изменения были впервые внесены на ActiveScheme, я не успел их увидеть на DuplicateScheme.

Стандартное поведение выглядит следующим образом:

  1. запуск команд Powercfg, которые обновляют регистры для ActiveScheme, вступают в силу немедленно и могут быть запрошены, не сбрасывая их, как я думал, это было необходимо

  2. выполнение команд Powercfg, которые обновляют регистры для дубликатов ActiveScheme (то есть нашего DuplicateScheme), вступают в силу после завершения выполнения командного файла. Ожидание их обновления с таймаутами, паузами или другими способами не работает. Моя гипотеза заключается в том, что DuplicateSchemes принимает на себя изменения своих родителей после завершения работы командного файла. Мое замешательство было из-за того, что я не распознал это поведение, я думал, что всегда редактирую ActiveScheme, но я ошибался.

Пример кода проблемы:

REM setting the ActiveScheme as per win10 spec
REM this creates the DuplicateScheme but only in windows 10 pro does it set this new 
REM scheme as the ActiveScheme also. In my version it did NOT do this
FOR /F "tokens=*" %%F IN ('powercfg /duplicatescheme 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c') DO (SET PowerScheme=%%F)

REM some code to process things

REM code to modify the ActiveScheme
powercfg /change monitor-timeout-ac 1 1> nul 

REM some code to create MonitorTimeoutPath and other things

REM code to query the DuplicateScheme
FOR /F "tokens=1-3 skip=2" %%A IN ('reg query "%MonitorTimeoutPath%" /v ACSettingIndex') DO (set /A "ActualMonitorTimeout=%%C/60" & echo C:%%C)

REM IF we assume that we had ran 
REM [powercfg /change monitor-timeout-ac 0 1> nul]
REM previously, then running the above code results in output of:
C: 0

REM when whats expected is:
C: 1

Решение состоит в том, чтобы заменить %MonitorTimeoutPath% на присягу ActiveScheme, а не DuplicateScheme.

Оказывается, настоящая проблема заключалась в том, что совершенно не связанный очистил регистр, и мне вообще не нужно было спускаться в кроличью нору. Спасибо, мозг. Я случайно обнаружил решение, запросив ActiveScheme напрямую по ключевому пути, вместо того, чтобы использовать %MonitorTimeoutPath%, который содержал путь DuplicateScheme.

Tl; dr

Проблема заключалась в том, что я обновлял ActiveScheme и запрашивал DuplicateScheme, и это должно работать только так, как я ожидал, в Windows 10 Pro, а не в версии Windows 2016 Server (которую я запускал)

Другие вопросы по теме