2
\$\begingroup\$

I have used below code, code is working fine, also retrieved the data, but there are performance issues. Retrieving the HKLM registry nodes is taking around 6 mins time. Please suggest any solution.

Private Function f_AddNodes(ByVal WindowsRegistryKey As RegistryKey, ByVal TreeNode As TreeNodeCollection, ByVal RegParent As Integer) As Integer
 Dim root As TreeNode
 If count = 1 Then
 root = TreeNode(RegParent)
 Else
 Dim KeyName = WindowsRegistryKey.Name
 Dim nLastIndex = KeyName.LastIndexOf("\")
 KeyName = KeyName.Substring(nLastIndex + 1, (KeyName.Length - 1) - nLastIndex)
 root = TreeNode.Add(KeyName) 'Node adding
 End If
 For Each subkeyName As String In WindowsRegistryKey.GetSubKeyNames
 Try
 Form_LoadingBar.Value(WindowsRegistryKey.Name)
 count = count + 1
 Dim vSubKey As RegistryKey = WindowsRegistryKey.OpenSubKey(subkeyName)
 f_AddNodes(vSubKey, root.Nodes, -1)
 Catch ex As SecurityException
 sExceptionHandling.Append(WindowsRegistryKey.Name + "\" + subkeyName + "," + ex.Message + vbNewLine)
 Catch ex As Exception
 MsgBox(ex.ToString)
 Return -1
 End Try
 Next`enter code here`
 Return 0
 End Function
AlexV
7,3532 gold badges24 silver badges47 bronze badges
asked Nov 12, 2019 at 9:55
\$\endgroup\$
1
  • \$\begingroup\$ When submitting code, one thing to remember, you need to explain and/or show code for those variables that are created outside your code. For instance: What is Form_LoadingBar? What is sExceptionHandling? Where does count come from? \$\endgroup\$ Commented Nov 12, 2019 at 19:09

1 Answer 1

2
\$\begingroup\$

It looks to me, one of your biggest problems is working directly with the Nodes of the TreeView. This necessitates calling Paint() everytime you add a node. If you start with a new TreeNode as root, and build the tree on that, you can add the whole thing to the TreeView by adding the root to the Nodes.

I also found that running VS as administrator greatly reduced the number of errors, which also sped it up.

By using a custom TreeNode class that inherits from Forms.TreeNode, you can recursively fill the tree inside the class. This also allows you to store the actual RegistryKey object in each node. Here's a relatively simple way of doing it:

Public Class RegistryTreeNode
 Inherits TreeNode
 Private Class SecurityExceptionLogger
 Private Shared Property Exceptions As New List(Of (String, SecurityException))
 Private Shared TempFileName As String = Path.GetTempFileName()
 Private Shared writer As StreamWriter
 Private Shared reader As StreamReader
 Public Shared Async Sub Add(exception As SecurityException)
 Exceptions.Add((Now.ToUniversalTime, exception))
 Using writer = New StreamWriter(TempFileName, True)
 Await writer.WriteLineAsync($"{Now.ToUniversalTime} - {exception.Message}")
 End Using
 End Sub
 Public Shared Sub PrintExceptions(stream As StreamWriter)
 Using stream
 Exceptions.ForEach(Async Sub(x)
 Await stream.WriteLineAsync($"{x.Item1} - {x.Item2.Message}")
 End Sub)
 End Using
 End Sub
 End Class
 Private _RegKey As RegistryKey
 Private Property RegKey As RegistryKey
 Get
 Return _RegKey
 End Get
 Set
 _RegKey = Value
 Name = _RegKey.Name
 Text = Name
 For Each key In _RegKey.GetSubKeyNames
 Try
 Nodes.Add(New RegistryTreeNode(_RegKey.OpenSubKey(key)))
 Catch ex As SecurityException
 SecurityExceptionLogger.Add(ex)
 Catch ex As Exception
 MsgBox(ex.Message)
 End Try
 Next
 End Set
 End Property
 Public Sub New()
 MyBase.New()
 RegKey = Registry.LocalMachine
 End Sub
 Public Sub New(newRegistryKey As RegistryKey)
 MyBase.New()
 RegKey = newRegistryKey
 End Sub
End Class

I hardcoded the default key to Registry.LocalMachine for your uses. Simply Dim root = New RegistryTreeNode() and the whole tree will build itself.

You'll notice I made a simple logger that writes to file as well as keeping a record in memory, instead of just in memory, just as an exercise.

Another way to speed things up, is to only load the first sub level of nodes on start up. You can then use the AfterSelect event handler to load the sub nodes of the key you want to expand. Something like this:

Public Class Form1
 Dim root = New RegistryTreeNode(Registry.LocalMachine)
 Public Sub New()
 ' This call is required by the designer.
 InitializeComponent()
 ' Add any initialization after the InitializeComponent() call.
 TreeView1.Nodes.Add(root)
 End Sub
 Private Sub TreeView1_AfterSelect(sender As Object, e As TreeViewEventArgs) Handles TreeView1.AfterSelect
 Dim node = DirectCast(TreeView1.SelectedNode, RegistryTreeNode)
 For Each key In node.GetSubKeyNames
 Try
 Dim regKey = New RegistryTreeNode(node.OpenSubKey(key))
 If Not regKey Is Nothing Then
 node.Nodes.Add(regKey)
 End If
 Catch ex As SecurityException
 SecurityExceptionLogger.Add(ex)
 Catch ex As Exception
 MsgBox(ex.Message)
 End Try
 Next
 End Sub
End Class
Class SecurityExceptionLogger
 Private Shared Property Exceptions As New List(Of (String, SecurityException))
 Private Shared TempFileName As String = Path.GetTempFileName()
 Private Shared writer As StreamWriter
 Private Shared reader As StreamReader
 Public Shared Async Sub Add(exception As SecurityException)
 Exceptions.Add((Now.ToUniversalTime, exception))
 Using writer = New StreamWriter(TempFileName, True)
 Await writer.WriteLineAsync($"{Now.ToUniversalTime} - {exception.Message}")
 End Using
 End Sub
 Public Shared Sub PrintExceptions(stream As StreamWriter)
 Using stream
 Exceptions.ForEach(Async Sub(x)
 Await stream.WriteLineAsync($"{x.Item1} - {x.Item2.Message}")
 End Sub)
 End Using
 End Sub
End Class
Public Class RegistryTreeNode
 Inherits TreeNode
 Private _RegKey As RegistryKey
 Private Property RegKey As RegistryKey
 Get
 Return _RegKey
 End Get
 Set
 _RegKey = Value
 Name = _RegKey.Name
 Text = Name.Substring(Name.LastIndexOf("\"c) + 1)
 End Set
 End Property
 Public Sub New()
 MyBase.New()
 RegKey = Registry.LocalMachine
 End Sub
 Public Sub New(newRegistryKey As RegistryKey)
 MyBase.New()
 RegKey = newRegistryKey
 End Sub
 Public Function GetValueNames() As List(Of String)
 Return _RegKey.GetValueNames().ToList()
 End Function
 Public Function GetValueKind(name As String) As RegistryValueKind
 Return _RegKey.GetValueKind(name)
 End Function
 Public Function GetValue(name As String) As Object
 Return _RegKey.GetValue(name)
 End Function
 Public Function GetSubKeyNames() As String()
 Return _RegKey.GetSubKeyNames()
 End Function
 Public Function OpenSubKey(key As String) As RegistryKey
 Return _RegKey.OpenSubKey(key)
 End Function
End Class

I moved the SecurityExceptionLogger outside the RegistryTreeNode class. You can also load the values for the keys, if needed, by looping through them after the sub nodes are added.

answered Nov 13, 2019 at 4:00
\$\endgroup\$
3
  • \$\begingroup\$ Thanks for your reply. I tried with this source code. Still it is slow as retrieving time taking around 4 mins. \$\endgroup\$ Commented Nov 14, 2019 at 4:38
  • \$\begingroup\$ @SankarS - I added more code, which shows loading only the first sub level. then any others on demand. Not sure how this will fit your needs but it is really fast. \$\endgroup\$ Commented Nov 14, 2019 at 20:33
  • \$\begingroup\$ Thanks, Yes, it its very fast.but my tree has checkboxes, hence, when i checked HKLM alone, automatically all the sub nodes(entire HKLM data) should be retrieved with values. I tried this one , i have extracted all the nodes and saved it to LIST, it takes 4 mins. this time is ok. But when i looping LIST, to take the values , it takes around 10 mins. \$\endgroup\$ Commented Nov 15, 2019 at 10:50

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.