Проблемы с функцией RegEnumKeyEx() на Win7
Пытаюсь собрать список установленных в системе программ.
Начал с функций MsiEnumProducts() + MsiGetProductInfo() из библиотеки msi.dll, но они возвращают только те программы, которые имеют GUID, т.е. исключительно продукты Microsoft. В результате список получается не полный - в моём случае всего 25 программ из 103.
Для решения этой проблемы в гугле советуют перечислить (под)ключи в ветке реестра: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, где вижу все установленные программы, но теперь функция RegEnumKeyEx() возвращает только пару программ от сторонних производителей, и в упор невидит те, которые зарегистрированны с GUID. На выходе в цикле она возвращает ERROR_MORE_DATA с кодом 0x00EA, мол есть ещё данные, но в буфер "lpName" сбрасывает имя подраздела в одном случае из 20-ти. Пишу на ассме и код выглядит так:
;// Открыть раздел реестра
invoke RegOpenKeyEx,HKEY_LOCAL_MACHINE,\
<'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',0>,0,\
KEY_QUERY_VALUE + KEY_ENUMERATE_SUB_KEYS,\
hKey
;// Получить кол-во подразделов
invoke RegQueryInfoKeyA,[hKey],0,0,0,subKey,0,0,0,0,0,0,0
cinvoke printf,<10,' Uninstall. Total subkey = %d',\
10,' -----------------------------',10,0>,[subKey]
dec [subKey] ;//<----- Индекс для цикла
;// Цикл перечисления (под)разделов "Uninstall"
@@: mov ecx,256/4 ;//<----- очистить приёмный буфер
xor eax,eax
mov edi,lpName
rep stosd
invoke RegEnumKeyEx,[hKey],[subKey],lpName,nameLen,0,0,0,0
invoke CharToOem,lpName,lpName
cinvoke printf,<10,' SubKey: %s'>,lpName
dec [subKey] ;//<----- следующий индекс.. (обратный отсчёт)
jnz @b
Хоть и запускаю код под админом, пробовал устанавливать себе и различные привилегии типа "SeSecurityPrivilege" и прочие, менял флаги в RegOpenKeyEx() - всё бесполезно. На данный момент картина такая.. Буду благодарен за любые советы. Спасибо.
Ответы (1 шт):
@Blackmeser указал мне на возможную ошибку, и проблема решилась. Оказывается функция RegEnumKeyEx() на каждой итерации цикла перезаписывает содержимое переменной "lpcName", сбрасывая в неё длину имени текущего найденного (под)раздела. Собственно и MSDN об этом сообщает прямым текстом, но я не обратил внимания:
Если функция завершается успешно, переменная, на которую указывает lpcName, содержит кол-во символов, хранящихся в буфере, не включая завершающий нулевой символ.
Поэтому нужно внутри цикла постоянно обновлять её содержимое, выставляя на макс.256, в соответствии с размером выделенного буфера. Вот исправленный вариант моего кода на ассемблере FASM, и остаётся только считывать значение "DisplayName" в каждом подразделе:
format pe console
entry start
;//-------
section '.inc' data readable
include 'win32ax.inc'
include 'equates\advapi32.inc'
;//-------
.data
hKey dd 0
subKey dd 0
index dd 0
counter dd 1
align 16
lpName rb 256
nameLen dd 256
buff db 0
;//---------
.code
start: invoke SetConsoleTitle,<'*** Programm List v1.0 ***',0>
;// Открыть раздел реестра
invoke RegOpenKeyEx,HKEY_LOCAL_MACHINE,\
<'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',0>,0,\
KEY_QUERY_VALUE + KEY_ENUMERATE_SUB_KEYS,\
hKey
;// Вычислить кол-во подразделов
invoke RegQueryInfoKeyA,[hKey],0,0,0,subKey,0,0,0,0,0,0,0
cinvoke printf,<10,' Uninstall. Total subkey = %d',\
10,' -----------------------------',10,0>,[subKey]
dec [subKey]
@@: mov ecx,256/4 ;//<---- Очистить буф
xor eax,eax
mov edi,lpName
rep stosd
mov [nameLen],256 ;//<---- Ошибка была здесь! Размер на макс.
invoke RegEnumKeyEx,[hKey],[subKey],lpName,nameLen,0,0,0,0
invoke CharToOem,lpName,lpName
cinvoke printf,<10,' %03d. %s'>,[counter],lpName
inc [counter] ;//<---- Счётчик найденных +1
dec [subKey] ;//<---- Следующий индекс..
cmp [subKey],-1 ;//<---- Переполнение индекса?
jnz @b ;//<---- Нет: повторить.
@exit: cinvoke _getch
cinvoke exit,0
;//-------
section '.idata' import data readable
library msvcrt,'msvcrt.dll',kernel32,'kernel32.dll',user32,'user32.dll',\
advapi32,'advapi32.dll'
include 'api\msvcrt.inc'
include 'api\kernel32.inc'
include 'api\user32.inc'
include 'api\advapi32.inc'
