Changing Windows Parental Controls in VB.NET
A little workaround to enable a VB.NET application to read and alter the WPC settings for any user.
Introduction
This is a "workaround" solution to enable programmatic changes to the Windows Parental Controls of a user whether or not they are logged on, and even if the machine is a standalone with no domain.
The Story...
I was trying to have one of my VB.NET programs change the Parental Control settings for a named user on a local standalone system with no domain. Every piece of code I could find worked for the current logged-on user, but, no matter how I tried, I could not get it to work for any user who was not logged on at the time. The problem is that, whilst there are several ways we can address the settings, to do so we need either a logon token (only available for logged on users), a UPN (only available from an active domain), or to know the SID for each username whose WPC settings we need to access. Turning the username into a SID gives much the same problems. So...
I'd found a code-snippet that worked nicely for any user I knew the SID for (Not very well formatted - Sorry!):
'First we create an Object Searcher for the Windows Management Interface
'You don't really need to know how that works, just that it does!
Dim searcher As New ManagementObjectSearcher("root\CIMV2\Applications\WindowsParentalControls", "SELECT * FROM WpcUserSettings")
'Check for the user whose settings we want to change (Change the SID to any "Standard" account)
'You can look up the SID's in the Registry HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
'The If/Then loop circles until it finds the WPC entry with the correct SID
If queryObj("SID") = "S-1-5-21-666666666-444444444-7777777777-1001" Then
'set the properties for that user here
queryObj("AppRestrictions") = True
queryObj("HourlyRestrictions") = True
queryObj("LoggingRequired") = False
queryObj("LogonHours") = False
queryObj("OverrideRequests") = False
queryObj("WpcEnabled") = True
'Just query out any which you don't want to use
'The next command commits the changes to the system
queryObj.Put()
End If
I then tried all ways and for hours to get the machine to give me the SID of the account from the username, but all attempts failed, for similar reasons to why the other ways of querying the WPC settings failed.
Finally, I realised that I already had a piece of code that worked the other way around: i.e. it gives the username for any given SID. Hence, all I had to do was to generate a list of ALL the SID's for a machine , and then run a simple If/Then loop to find the right one. It turns out that getting all the SID's from a machine is quite easy.
Here's what we end up with. Quick note here: If you're copying and pasting this code, remember the "Imports" lines, and to set the references to them in your project. ("Coding 101 - Egg Sucking for grandmothers", I know, but we all forget sometimes!)
'I've included a few console outputs so you can see what the code is doing when you run it.
'It also aids in debugging if you have a problem
'When you're happy it's working, just comment them out
'Import the external references
Imports System
Imports System.Collections.Generic
Imports System.Management
Imports System.Text
Module Module1
Sub Main()
Console.WriteLine("Starting Module")
Dim SidString As String = ""
Dim AccountName As String = ""
'The code which gets the username from the SID spits it out in the form "MachineName\UserName"
'Easier to deal with that here than splitting strings later.
'Hence the "Environment.MachineName" stuff
Dim FullName As String = Environment.MachineName & "\YOUR ACCOUNT NAME"
'It's always good coding practice to put loops inside a Try/Catch, in case things go wrong!
Try
Dim searcher As New ManagementObjectSearcher("root\CIMV2\Applications\WindowsParentalControls",
"SELECT * FROM WpcUserSettings")
Console.WriteLine("Path to the Parental Control settings - " & searcher.ToString)
For Each queryObj As ManagementObject In searcher.[Get]()
SidString = queryObj("SID")
Console.WriteLine("SID - " & SidString)
'This is how you turn each SID into a username - Clever, eh?
AccountName = New SecurityIdentifier(SidString).Translate(GetType(NTAccount)).ToString
Console.WriteLine(AccountName & " is " & FullName)
'Check the account name from the SID against the one you're looking for
If AccountName = FullName Then
'set the properties here
Console.WriteLine(queryObj("SID"))
queryObj("AppRestrictions") = True
queryObj("HourlyRestrictions") = True
queryObj("LoggingRequired") = False
queryObj("LogonHours") = False
queryObj("OverrideRequests") = False
queryObj("WpcEnabled") = True
queryObj.Put()
Else
Console.WriteLine(AccountName & " is not " & FullName)
End If
Next
Catch e As ManagementException
Console.WriteLine("An error occurred setting the WMI data: " + e.Message)
End Try
Console.ReadKey()
'(Stops the CMD window from closing until you've read it!)
End Sub
End Module
So, there you have it. A way to change Parental Control permissions for any account from any account... well, almost... You need Administrator rights to run it... Sorry kids!