проблема синхронизации данных в пакетном скрипте при запросе данных регистра, мне нужно что-то вроде RegFlushKey, но в пакетном - PullRequest
0 голосов
/ 29 октября 2018

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

: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. Это происходит только тогда, когда я непосредственно запускаю операцию запроса в той же функции, для которой я установил значение регистра.

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

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

UPDATE

Я пытался изменить все важные переменные с отложенным расширением (!) В моем коде на не задержанные (%), думая, что, возможно, он захватывал старые значения через расширение и область, но это не сработало, регистры все еще устарели одним запуском сценария. Я также попытался дважды запросить регистры и использовать вспомогательную функцию для разделения кода и посмотреть, сработало ли это, но все же не сработало. Мое единственное предположение, что изменения в 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 \ Энергетика \ 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 \ Энергетика \ 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 и мне нужен способ сделать это в основном в пакетном режиме.

1 Ответ

0 голосов
/ 30 октября 2018

выпуск

Я узнал, что случилось. Я запрашивал DuplicateScheme, а не ActiveScheme.

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

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

Моя ошибка заключалась в том, что, несмотря на то, что я запускал метод DuplicateScheme в версии для Windows 2016 Server, я думал, что это больше не повлияет. Я был неправ. Так как метод DuplicateScheme не работает на версии Windows 2016 Server, запрашиваемый мной 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, др

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...