- RegCloseKey
- RegEnumKeyExA
- RegEnumValueA
- RegOpenKeyExA
- RegQueryInfoKeyA
The Windows Registry is where programs, the OS, drivers, and other items store settings and data for later use. Personally for my programs I use the good old fashioned ini files.
The Registry is a dangerous place! One small mistake and you could render the OS non bootable. We won't have that issue here as we are only going to read from the Registy. When I first started the Tutorial, it was very complex so I started over and made it very simple. Wasn't good for what I wanted to show, so I settled on somewhat complex.
We will use a TreeView and ListView control to display all keys, subkeys, and values under HKEY_CURRENT_USER\Software. You can modify it for any purpose/keys.
*** Note ***
I do not enumerate all keys and subkeys when loading. When the program starts, we only enumerate the subkeys under HKCU\Software and set a flag if the key has subkeys, we then fill these children in when the TVITEM is expanded.
The "splitter" code is nothing fancy, modfiy it at your will. There is a mix of invokes and push/calls, I started with push/calls and go tired of typing so switch to invokes for intellisense.
This will be a bit long, so get comfy!
We will be using the following structures:
TVITEM
TV_INSERTSTRUCT
TVHITTESTINFO
LVITEM and LVCOLUMN
The "Magic" happens in our WM_NOTIFY handler of our WindowProc. We will handle 4 messages from the Treeview:
TVN_SELCHANGED
This is where we will handle displaying the values of selected node from the Registry.
TVN_ITEMEXPANDING
This is where we will fill all children of node if it has not already been filled.
TVN_GETDISPINFO
This is where we will change the icon to either an open folder or closed folder depending on action.
NM_CLICK
This is where we check to see if the item expand button was clicked and select Treeview item.
TVN_SELCHANGED
First thing we have to do when we get this notification is get the handle of selected item and its parent.
ebx will contain our treeview handle
invoke SendMessage, ebx, TVM_GETNEXTITEM, TVGN_CARET, NULL
push eax
invoke SendMessage, ebx, TVM_GETNEXTITEM, TVGN_PARENT, eax
test eax, eax
jz Done
cmp eax, hRoot
jne ProcessChild
If the handle does not equal hRoot, a child was selected; if so, then a parent node was selected and we get the text of the item:
mov tvi._mask, TVIF_TEXT
mov tvi.pszText, offset lpszBuffer
mov tvi.cchTextMax, sizeof lpszBuffer
pop tvi.hItem
invoke SendMessage, ebx, TVM_GETITEM, 0, addr tvi
jmp ShowValues
No furthure processing is required since the Registry path will be HKEY_CURRENT_USER\Software\TextOfParent so we will go onto displaying the values.
If a child was selected, we have to build the registry path from the child all the way back to the parent. For example, if we have a node like so:
SomeParent
Child1
Child2
Parent2
Child3
and Child3 was selected, we have to scan backwards till we reach the root of the treevew. So after our call to BuildRegPath, the path will be HKCU\Software\SomeParent\Parent2\Child3 then we call ShowInfo.
TVN_ITEMEXPANDING
As I mentioned earlier, we don't enumerate all subkeys under HKCU\Software only the toplevel keys. When we add a toplevel key to the treeview its lParam will be 0. When we get TVN_ITEMEXPANDING, we check the lParam of selected item, if it is 0 then we enumerate the registry children and set lParam of the item to 1. Same as TVN_SELCHANGED, if it is a child we have to build a reg path before we enumerate:
invoke SendMessage, hLVValues, LVM_DELETEALLITEMS, 0, 0
invoke SendMessage, ebx, TVM_GETNEXTITEM, TVGN_CARET, NULL
push eax
invoke SendMessage, ebx, TVM_GETNEXTITEM, TVGN_PARENT, eax
test eax, eax
jz Done
cmp eax, hRoot
jne ProcessChild
; parent selected
mov tvi._mask, TVIF_TEXT
mov tvi.pszText, offset lpszBuffer
mov tvi.cchTextMax, sizeof lpszBuffer
pop tvi.hItem
invoke SendMessage, ebx, TVM_GETITEM, 0, addr tvi
jmp ShowValues
ProcessChild:
; child selected
mov tvi._mask, TVIF_TEXT
mov tvi.pszText, offset lpszBuffer
mov tvi.cchTextMax, MAX_PATH
pop tvi.hItem
invoke SendMessage, ebx, TVM_GETITEM, 0, addr tvi
invoke BuildRegPath, tvi.hItem, offset lpszBuffer
ShowValues:
invoke RegOpenKeyEx, HKEY_CURRENT_USER, offset szSoftware, NULL, KEY_QUERY_VALUE or KEY_ENUMERATE_SUB_KEYS, addr hKey
invoke RegOpenKeyEx, hKey, offset lpszBuffer, NULL, KEY_QUERY_VALUE or KEY_ENUMERATE_SUB_KEYS, addr hSubKey
test eax, eax
jnz Done
invoke ShowInfo, hSubKey
invoke RegCloseKey, hSubKey
invoke RegCloseKey, hKey
jmp Done
TVN_GETDISPINFO
First we get the state of current item:
mov eax, (NMTVDISPINFO ptr [edi]).item.hItem
mov tvi.hItem, eax
mov tvi._mask, TVIF_STATE
invoke SendMessage, ebx, TVM_GETITEM, 0, addr tvi
Then we check to see if the TVIS_EXPANDED bit is set
mov eax, tvi.state
test eax, TVIS_EXPANDED
jnz @F
If the ZERO flag is not zero, then the item is expanded and we fill in 2 members of the NMTVDISPINFO structure - item.iSelectedImage/iImage with the index of our open folder icon. Otherwise the closed folder icon
mov (NMTVDISPINFO ptr [edi]).item.iSelectedImage, ICON_FOLDER_IDX
mov (NMTVDISPINFO ptr [edi]).item.iImage, ICON_FOLDER_IDX
jmp Done
@@:
mov (NMTVDISPINFO ptr [edi]).item.iSelectedImage, ICON_FOLDER_OPEN_IDX
mov (NMTVDISPINFO ptr [edi]).item.iImage, ICON_FOLDER_OPEN_IDX
jmp Done
NM_CLICK
; see if the item button was clicked
invoke GetCursorPos, addr pt1
invoke ScreenToClient, ebx, addr pt1
m2m tvhti.pt.x, pt1.x
m2m tvhti.pt.y, pt1. y
invoke SendMessage, ebx, TVM_HITTEST, 0, addr tvhti
mov eax, tvhti.flags
cmp eax, TVHT_ONITEMBUTTON
jne Done
; it was, now select item
invoke SendMessage, ebx, TVM_SELECTITEM,TVGN_CARET, tvhti.hItem
jmp Done
All we do here, is get the position of the cursor, and check if the expand button was clicked. If it was we select the item so we can "fire" the TVN_SELCHANGED code. If we didn't handle this message, then things will get screwed up when we use TVM_GETNEXTITEM/TVGN_CARET. To see what happens, just comment out these lines:
cmp dword ptr(NMHDR ptr[edi]).code, NM_CLICK
je _NM_CLICK
The functions EnumChildren and ShowInfo both require a handle to a reg key to work on. This will be the currently selected Treeview item and as mentioned, if it is not a parent key, then we use BuildRegPath to get the reg path. Then we must open 2 keys: HKCU\Software and with that handle, we call RegOpenKeyEx and the path returned from BuildRegPath.
Now for the code that does the work:
ShowInfo will enumerate the values in open reg key.
Spoiler
I will use the register EDI for 0, since it is a smaller opcode to push a register vs. a number.
At the beginning of the procedure, I turn of painting/drawing of the listview to speed up inserting items. We do this with a SendMessage and a wParam value of FALSE
push edi
push edi
push WM_SETREDRAW
push hLVValues
call SendMessage
Now for this piece of code, I need to know 2 things about the registry key:
- Total values in key
- Size of the longest data component among the key's values
Since we don't need any other info, we can pass NULL to most of the Parameters of RegQueryInfoKey
push edi ; lpftLastWriteTime - N/A
push edi ; lpcbSecurityDescriptor - N/A
lea eax, lpcMaxValueLen ;
push eax ; longest data length
push edi ; lpcMaxValueNameLen - N/A
lea ecx, lpcValues ;
push ecx ; total values in key
push edi ; lpcMaxClassLen - N/A
push edi ; lpcMaxSubKeyLen - N/A
push edi ; lpcSubKeys - N/A
push edi ; lpReserved - NULL
push edi ; lpcClass - N/A
push edi ; lpClass - N/A
push hRegKey ; handle of open reg key to query info from
call RegQueryInfoKey ;
Once the call returns, we check the value of lpcValues and if it is zero, we add a "default" value to the listview.
We need a buffer to hold the enumerated data:
push lpcMaxValueLen ; create a buffer to hold value data
push HEAP_ZERO_MEMORY ; size == length of longest value
push hHeap ;
call HeapAlloc ;
xchg eax, esi ; move pointer to esi
Now we can use RegEnumValueEx to enumerate all values in the key. We call it in a loop until it returns ERROR_NO_MORE_ITEMS. When you first call RegEnumValueEx, you need to pass 0 in the dwIndex param and increment it each loop iteration.
GetNextValue:
mov [esi], edi ; zero buffer
mov lpcValueName, MAX_VALNAME + 1
push lpcMaxValueLen
pop lpcbData
lea eax, lpcbData ; how big our buffer is
push eax ;
push esi ; pointer to our buffer to hold current enum'd values data
lea ecx, lpType ; var to hold values data type
push ecx ;
push NULL ; lpReserved - NULL
lea edx, lpcValueName ; how big our valuename buffer is
push edx ;
lea eax, lpValueName ; buffer to hold value name
push eax ;
push ebx ; dwIndex
push hRegKey ; handle of open reg key to enumerate values
call RegEnumValue ;
cmp eax, ERROR_NO_MORE_ITEMS ; any more values?
je NoMoreValues ; nope, exit loop
cmp lpcValueName, edi ; does key have values?
jnz AddValue ; yes, add it
When we are done adding values to the listview, we turn back on painting/drawing, we do this with a SendMessage and a wParam value of TRUE:
; turn back on listview drawing
push 0
push TRUE
push WM_SETREDRAW
push hLVValues
call SendMessage
AddLVItem adds a registry value to listview - Name, Type, and Data
Spoiler
First thing we do is fill in some members of the LVITEM structure with passed data. When we insert an item into column 0 we use LVM_INSERTITEM and we will use the flags LVIF_TEXT and LVIF_IMAGE to tell the listview we want to set the icon and text of the item.
mov lvi.imask, LVIF_TEXT or LVIF_IMAGE
push dwItem
pop lvi.iItem
mov lvi.iSubItem, 0
push pszName
pop lvi.pszText
Next we see what data type was passed and fill in lvi.iImage accordingly:
mov eax, dwType
cmp eax, REG_SZ
je StringData
cmp eax, REG_MULTI_SZ
je StringData
cmp eax, REG_EXPAND_SZ
jne OtherData
StringData:
mov lvi.iImage, ICON_STRING_IDX
jmp AddIt
OtherData:
mov lvi.iImage, ICON_DATA_IDX
Once that is all done, we insert the item into the listview:
AddIt:
lea eax, lvi ;
push eax ;
push 0 ;
push LVM_INSERTITEM ;
push hLVValues ;
call SendMessage ; add icon and value name
Now to "add" subitems to the listview item we just inserted, we use LVM_SETITEM. We set lvi.iSubitem to the index of the subitem to set. When we first insert an item, lvi.iSubitem is zero so we just increase the value for each subitem:
inc lvi.iSubItem
For the data type column, we check to see the data type that was grabbed from the registry and use that number as an index into a table of string pointers for the data type:
lea esi, RegTypesTable
.if dwType > RegTypesSize
mov lvi.pszText, offset szUnknown
.else
mov ecx, dwType
mov eax, [esi + 4 * ecx]
mov lvi.pszText, eax
.endif
invoke SendMessage, hLVValues, LVM_SETITEM, 0, addr lvi
Now there is one more column to set - data
inc lvi.iSubItem
If it is one of the string types, we just use the data passed:
.if dwType == REG_SZ || dwType == REG_MULTI_SZ || dwType == REG_EXPAND_SZ
push lpData
pop lvi.pszText
invoke SendMessage, hLVValues, LVM_SETITEM, 0, addr lvi
If it is one of the DWORD types we create a temp buffer to hold our formated number then convert to hex, convert the dword to string and multicat it all together to get a nicely formated string like this:

.elseif dwType == REG_DWORD || dwType == REG_DWORD_BIG_ENDIAN
invoke HeapAlloc, hHeap, HEAP_ZERO_MEMORY, 32
mov TempBuf, eax
mov ebx, lpData
mov ebx, [ebx]
invoke dw2hex, ebx, addr HexNum
invoke dwtoa, ebx, addr Num
invoke szMultiCat, 5, TempBuf, offset szHex, addr HexNum, offset szParenLeft, addr Num, offset szParenRight
push TempBuf
pop lvi.pszText
invoke SendMessage, hLVValues, LVM_SETITEM, 0, addr lvi
invoke HeapFree, hHeap, 0, TempBuf
If the data type is anything other than the above types, we will treat it as binary and convert it to hex.
ConvertToHex is code modified from the MASM32 library - bin2hex to format the string as I need it to be.
First we need a buffer to hold our hex string. We take the value in dwDataLen and shift left by 2 to multiply the value by 4 so our buffer is large enough, then we call ConvertToHex to give us a string like this:

.else
mov eax, dwDataLen
shl eax, 2
invoke HeapAlloc, hHeap, HEAP_ZERO_MEMORY, eax
mov TempBuf, eax
invoke ConvertToHex, lpData, dwDataLen, TempBuf
push TempBuf
pop lvi.pszText
invoke SendMessage, hLVValues, LVM_SETITEM, 0, addr lvi
invoke HeapFree, hHeap, 0, TempBuf
EnumChildren is called anytime we expand a parent node and it need to be filled
Spoiler
EnumChildren takes 2 parameters: handle to an open registry key to enumerate and handle to its treeview parent.
We use RegEnumKeyEx in a loop until it returns ERROR_NO_MORE_ITEMS. As with RegEnumValue we pass 0 for dwIndex the first time we call it. We are only interested in the currently enumerated key name.
lea esi, lpName
xor ebx, ebx
xor edi, edi
GetNextKey:
mov [esi], edi
mov lpcName, sizeof lpName
invoke RegEnumKeyEx, hKey, ebx, esi, addr lpcName, edi, edi, edi, edi
cmp eax, ERROR_NO_MORE_ITEMS
je Done
invoke RegOpenKeyEx, hKey, esi, edi, KEY_QUERY_VALUE, addr hSubKey
invoke RegQueryInfoKey, hSubKey, edi, edi, edi, addr lpcSubkeys, edi, edi, edi, edi, edi, edi, edi
invoke RegCloseKey, hSubKey
invoke AddKeyToTV, hParentNode, esi
.if lpcSubkeys > edi
; notify TV item has children so we
; get the expand button
mov tvi._mask, TVIF_CHILDREN or TVIF_PARAM
mov tvi.lParam, 0
mov tvi.hItem, eax
mov tvi.cChildren, 1
invoke SendMessage, hTVKeys, TVM_SETITEM, edi, addr tvi
.endif
inc ebx
jmp GetNextKey
Done:
ret
Once we get a key name from RegEnumKeyEx, we open it with RegOpenKeyEx and get the number of subkeys with RegQueryInfoEx. We add the reg key to the Treeview with AddkeyToTV which returns a handle of the inserted item. Now we check to see if there were subkeys, and if so, we set the tvi.cChildren member to 1 so we get the button to expand the item.
Many Registry API functions have a samDesired parameter. This is the desired access right to the key. The calls WILL fail if you request a right that your user account does not have. I see folks using KEY_ALL_ACCESS, WHY?!?!? Some also use KEY_READ for anything having to do with enumerating, reading, querying, etc.. the same with KEY_WRITE. Well I was taught long ago when Windows was a bit more tempermental, to use only the access rights that you need. If I just need to query a value, I will use KEY_QUERY_VALUE. Enumerate keys, then it is KEY_ENUMERATE_SUB_KEYS, if I need to do more, I will OR them together. By using ONLY what you require, you reduce the chances of bugs popping up.
PHEW!!! That was a bit long, but worth it I hope!
This is what all of that put together looks like:

Full source and sample exe
Registry.zip (17.82K)
Number of downloads: 183







MultiQuote





|