Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / system

Changing Windows Parental Controls in VB.NET

5.00/5 (2 votes)
8 Oct 2012CPOL2 min read 16.8K  
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!):

VB.NET
'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!)

VB.NET
'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!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)