Solved

Modify a key in the NTUSER.DAT file for multiple existing profiles programatically. Must be done in one shot / all profiles at changed in one run.

Posted on 2004-09-11
9
1,100 Views
Last Modified: 2012-06-22
I am working on packaging several applications that require a registry change be made to the HKCU. Problem is that I must modify the HKCU for existing profiles as well.

What I want to so is to access all the existing NTUSER.DAT files and make the neccessary changes. I want to package this and make the changes all at once. This necesitates the changes be made in a script or program, doesnt matter which. I am familiar with Java, VBS, and dos and can work with any program that can do the job.

Solutions so far. These are not acceptable.

1. Create a script that makes changes to HKCU each time the application is run. (least efficient and undesireable for obvious reasons)
2. Create a seperate desktop script the user can run on any errors or place in the startup folder. (same issues)
3. Delete all current profiles (not possible)
4. ????
0
Comment
Question by:kidnme
  • 5
  • 4
9 Comments
 
LVL 41

Expert Comment

by:graye
ID: 12034896
That's not gonna be easy to do with any scripting language...  The mounting of the user's "hives" requires API calls.

I've included a chunk of a program that remotely searchs all of the user's profiles for information about their Outlook PST files.   It's obviously not a complete application, but you'll probably be able to use it as a guide to accomplish your objectives.

    'LONG RegLoadKey(
    '  HKEY hKey,
    '  LPCTSTR lpSubKey,
    '  LPCTSTR lpFile
    ');
    Private Declare Auto Function RegLoadKey Lib "advapi32.dll" (ByVal hKey As Integer, ByVal lpSubKey As String, ByVal lpFile As String) As Integer

    'LONG RegUnLoadKey(
    '  HKEY hKey,
    '  LPCTSTR lpSubKey
    ');
    Private Declare Auto Function RegUnLoadKey Lib "advapi32.dll" (ByVal hKey As Integer, ByVal lpSubKey As String) As Integer

    'LONG RegConnectRegistry(
    '  LPCTSTR lpMachineName,
    '  HKEY hKey,
    '  PHKEY phkResult
    ');
    Private Declare Auto Function RegConnectRegistry Lib "advapi32.dll" (ByVal lpMachineName As String, ByVal hKey As Integer, ByRef phkResult As Integer) As Integer

    'LONG RegCloseKey(
    '  HKEY hKey
    ');
    Private Declare Auto Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Integer) As Integer

    Private Const HKEY_USERS = &H80000003

    Public Overrides Function Execute(ByVal RemotePC As String, ByVal dt As DataTable) As Boolean
        Dim dr As DataRow
        Dim Profiles, profile, ntuser_path, user, temp() As String
        Dim Users As New StringCollection
        Dim hReg, ret As Integer
        Dim reg_hklm, reg_hku, key, subkey, sub2 As RegistryKey
        Dim keyname, subkeyname, userkey, Owner, pst_path As String
        Dim pst_byte() As Byte
        Dim pst_size As Double
        Dim Win32Error As Win32Exception
        Dim fi As FileInfo

        ' OK... the first thing we do is figure out where the user profiles are stored
        ' on the remote system.
        Try
            reg_hklm = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, RemotePC)
        Catch ex As Exception
            ' ignore this common error
            If Err.Number = 57 Then
                Return False
            End If
            If MsgBox("Yikes!, Can't connect to " & RemotePC & "'s registry" & vbCr & ex.Message, MsgBoxStyle.Critical Or MsgBoxStyle.OKCancel, "Error!") = MsgBoxResult.OK Then
                Return False
            End If
            Return True
        End Try

        key = reg_hklm.OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList", False)
        If Not IsNothing(key) Then
            Profiles = key.GetValue("ProfilesDirectory").ToString
        End If
        reg_hklm.Close()

        ' We start with connecting to HKEY_USERS on the remote PC using
        ' the API calls
        ret = RegConnectRegistry(RemotePC, HKEY_USERS, hReg)
        If ret <> 0 Then
            Win32Error = New Win32Exception(ret)
            If MsgBox("Yikes!, Can't connect to " & RemotePC & "'s registry" & vbCr & Win32Error.Message, MsgBoxStyle.Critical Or MsgBoxStyle.OKCancel, "Error!") = MsgBoxResult.OK Then
                Return False
            End If
            Return True
        End If

        ' Now we need to load all of the remote User's registry hives, so we can
        ' do a complete search on the registry keys.
        For Each profile In Directory.GetDirectories(ConvUNC(RemotePC, Profiles))
            ntuser_path = profile & "\ntuser.dat"
            temp = profile.Split("\")
            user = temp(UBound(temp))

            If File.Exists(ntuser_path) Then
                ' You'd expect to get a failure on NTUSER.DAT files that
                ' are already loaded, since they are obviously "in use".
                If RegLoadKey(hReg, user, ntuser_path) = 0 Then
                    Users.Add(user)
                End If
            End If
        Next

        ' OK.. now we're ready to go thru the list of keys that are under HKEY_USERS
        reg_hku = RegistryKey.OpenRemoteBaseKey(RegistryHive.Users, RemotePC)
        For Each userkey In reg_hku.GetSubKeyNames
            If Not userkey.EndsWith("_Classes") Then
                ' is it a SID-style keyname?
                If userkey.StartsWith("S-1-") Then
                    Owner = GetUserFromSID(RemotePC, userkey)
                Else ' or is it "one of ours"
                    ntuser_path = ConvUNC(RemotePC, Profiles & "\" & userkey & "\ntuser.dat")
                    If File.Exists(ntuser_path) Then
                        Owner = GetFileOwner(ntuser_path)
                    End If
                End If

                ' We *finally* get to the part where we look for the PST files!
                key = reg_hku.OpenSubKey(userkey & "\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles", False)
                If Not IsNothing(key) Then
                    For Each keyname In key.GetSubKeyNames()
                        subkey = key.OpenSubKey(keyname, False)
                        For Each subkeyname In subkey.GetSubKeyNames()
                            sub2 = subkey.OpenSubKey(subkeyname, False)
                            ' versions of Outlook prior to 2003
                            pst_path = sub2.GetValue("001e6700")
                            If Not IsNothing(pst_path) Then
                                ' we may get a permission denied (particularly in
                                ' a workgroup environment)
                                pst_size = 0
                                If File.Exists(ConvUNC(RemotePC, pst_path)) Then
                                    fi = New FileInfo(ConvUNC(RemotePC, pst_path))
                                    pst_size = fi.Length / 1024.0
                                End If
                                dr = dt.NewRow
                                dr("PC_Name") = RemotePC
                                dr("User_Name") = Owner
                                dr("Profile") = keyname
                                dr("PST_Path") = pst_path
                                dr("PST_Size") = pst_size
                                dt.Rows.Add(dr)
                                Record_Count += 1
                            End If
                            ' Outlook 2003
                            pst_byte = sub2.GetValue("001f6700")
                            If Not IsNothing(pst_byte) Then
                                pst_path = System.Text.Encoding.Unicode.GetString(pst_byte, 0, UBound(pst_byte) - 1)
                                pst_size = 0
                                If File.Exists(ConvUNC(RemotePC, pst_path)) Then
                                    fi = New FileInfo(ConvUNC(RemotePC, pst_path))
                                    pst_size = fi.Length / 1024.0
                                End If
                                dr = dt.NewRow
                                dr("PC_Name") = RemotePC
                                dr("User_Name") = Owner
                                dr("Profile") = keyname
                                dr("PST_Path") = pst_path
                                dr("PST_Size") = pst_size
                                dt.Rows.Add(dr)
                                Record_Count += 1
                            End If
                            sub2.Close()
                        Next
                        subkey.Close()
                    Next
                    key.Close()
                End If
            End If
        Next
        reg_hku.Close()

        ' Don't forget to unmount the user hives
        For Each user In Users
            RegUnLoadKey(hReg, user)
        Next
        RegCloseKey(hReg)

        Return False
    End Function
0
 
LVL 1

Author Comment

by:kidnme
ID: 12039055
Sweet Graye!
Looks like vbs. This looks very promising. Give me a few days to process...have to do this in my spare time. I will be in touch.
0
 
LVL 1

Author Comment

by:kidnme
ID: 12039520
Ok,
This is actually Visual Basic which I don't have a  copy of. Is it posible to get a script that would work with the windows 2k WSH?
0
 
LVL 41

Expert Comment

by:graye
ID: 12040113
But that was the point of my first comment...   Scripting languages don't typically have support for making API calls.

As you can imagine... with the current imphasis on internet security, firewalls, viruses, etc, that any script that did manage to take advantage of API calls would not be tollerated.
0
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 1

Author Comment

by:kidnme
ID: 12040473
Well if you can help me out, this is what I need it to do.

called from a command line
pass the key, a switch to delete or set, value to set

The program must run locally, parse the existing user profiles NTUSER.DAT file, set or delete the specified key or value in CURRENT_USER.

Return success or failure
0
 
LVL 41

Expert Comment

by:graye
ID: 12040808
Visual Basic .Net is well suited for this type of application... it can create Console applications that run in a batch file.

Sure, I can "rough out"  an application for you...  but you'll need to find somebody who's got Visual Basic .Net 2003 to compile it for you.

I'm envisioning something like:

       EditProfile /delete key
       EditProfile /set key value

What type of data is the value... a string perhaps?   Will it always be a string?
Will the user running the application have Administrator-style permissions?
0
 
LVL 1

Author Comment

by:kidnme
ID: 12040828
If you could I would be so greatful.

The value would change from one application to the next
The application would be installed with Administrator permisions yes.
Thanks.
0
 
LVL 41

Accepted Solution

by:
graye earned 500 total points
ID: 12045410
Okey dokey... here it is...   It's only been marginally tested (Hey, it works on my PC)

Imports Microsoft.Win32
Imports System.IO
Imports System.Collections.Specialized
Imports System.ComponentModel

Module Module1
    'LONG RegLoadKey(
    '  HKEY hKey,
    '  LPCTSTR lpSubKey,
    '  LPCTSTR lpFile
    ');
    Private Declare Auto Function RegLoadKey Lib "advapi32.dll" ( _
    ByVal hKey As Integer, _
    ByVal lpSubKey As String, _
    ByVal lpFile As String) As Integer

    'LONG RegUnLoadKey(
    '  HKEY hKey,
    '  LPCTSTR lpSubKey
    ');
    Private Declare Auto Function RegUnLoadKey Lib "advapi32.dll" ( _
    ByVal hKey As Integer, _
    ByVal lpSubKey As String) As Integer

    Const HKEY_USERS = &H80000003

    Sub Main()
        Dim args() As String
        Dim key_name, key_value As String
        Dim ret, OperatingMode As Integer

        ' parse the command line
        args = ParseCommandLine(Command)

        ' a quick sanity check
        If UBound(args) < 1 Then
            Console.WriteLine(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name & " Usage:")
            Console.WriteLine(vbTab & "/delete key_name")
            Console.WriteLine(vbTab & "/set key_name key_value")
            Exit Sub
        End If

        ' pick out the operating mode, key name, and value
        Select Case args(0).ToLower
            Case "/delete"
                OperatingMode = 0
                If args(1).StartsWith("\") Then
                    key_name = args(1)
                Else
                    key_name = "\" & args(1)
                End If
            Case "/set"
                OperatingMode = 1
                If UBound(args) < 2 Then
                    Console.WriteLine("Error: missing value argument")
                    Exit Sub
                End If
                If args(1).StartsWith("\") Then
                    key_name = args(1)
                Else
                    key_name = "\" & args(1)
                End If
                key_value = args(2)
            Case Else
                Console.WriteLine("Error: invalid operating mode")
                Exit Sub
        End Select

        ' do the deed...
        ret = EditProfile(OperatingMode, key_name, key_value)
        Err.Number = ret

    End Sub
    Public Function EditProfile(ByVal operatingmode As Integer, ByVal key_name As String, ByVal key_value As String) As Boolean
        Dim Profiles, profile, ntuser_path, user, temp() As String
        Dim Users As New StringCollection
        Dim i, hReg, ret As Integer
        Dim key As RegistryKey
        Dim keyname, valuename, userkey As String

        ' OK... the first thing we do is figure out where the user profiles are stored
        key = Registry.LocalMachine.OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList", False)
        If Not IsNothing(key) Then
            Profiles = key.GetValue("ProfilesDirectory").ToString
        Else
            Return False
        End If

        ' Now we need to load all of the remote User's registry hives, so we can
        ' do a complete search on the registry keys.
        For Each profile In Directory.GetDirectories(Profiles)
            ntuser_path = profile & "\ntuser.dat"
            temp = profile.Split("\")
            user = temp(UBound(temp))

            If File.Exists(ntuser_path) Then
                ' You'd expect to get a failure on NTUSER.DAT files that
                ' are already loaded, since they are obviously "in use".
                If RegLoadKey(HKEY_USERS, user, ntuser_path) = 0 Then
                    Users.Add(user)
                End If
            End If
        Next

        ' The key_name is really a key/value pair, while key_value is really the data
        ' (Strange terms when dealing with the registry)
        i = key_name.LastIndexOf("\")
        keyname = key_name.Substring(0, i)
        valuename = key_name.Substring(i + 1)

        ' OK.. now we're ready to go thru the list of keys that are under HKEY_USERS
        ret = True
        For Each userkey In Registry.Users.GetSubKeyNames
            If Not userkey.EndsWith("_Classes") Then
                ' delete
                If operatingmode = 0 Then
                    Try
                        key = Registry.Users.OpenSubKey(userkey & keyname, True)
                        If Not IsNothing(key) Then
                            key.DeleteValue(valuename)
                            key.Close()
                        Else
                            ret = False
                        End If
                    Catch ex As Exception
                        ret = False
                    End Try
                Else
                    Try
                        key = Registry.Users.OpenSubKey(userkey & keyname, True)
                        If Not IsNothing(key) Then
                            key.SetValue(valuename, key_value)
                            key.Close()
                        Else
                            ret = False
                        End If
                    Catch ex As Exception
                        ret = False
                    End Try
                End If
            End If
        Next

        ' Don't forget to unmount the user hives
        For Each user In Users
            RegUnLoadKey(HKEY_USERS, user)
        Next

        Return ret
    End Function
    Public Function ParseCommandLine(ByVal CommandLine As String) As String()
        Dim ans(10), temp As String
        Dim c, buf() As Char
        Dim Quoted As Boolean
        Dim i As Integer

        ' convert the command line into an array of characters
        buf = CommandLine.Trim.ToCharArray
        i = 0
        For Each c In buf
            If [Char].IsWhiteSpace(c) And Not Quoted Then
                If temp <> "" Then
                    ans(i) = temp
                    temp = ""
                    i += 1
                    If i > UBound(ans) Then
                        ReDim Preserve ans(i + 10)
                    End If
                End If
            Else
                If c = Chr(34) Then
                    If Quoted Then
                        ans(i) = temp
                        temp = ""
                        i += 1
                        If i > UBound(ans) Then
                            ReDim Preserve ans(i + 10)
                        End If
                        Quoted = False
                    Else
                        Quoted = True
                    End If
                Else
                    temp &= c
                End If
            End If
        Next
        If Not CommandLine.Trim.EndsWith(Chr(34)) Then
            ans(i) = temp
            i += 1
        End If

        ReDim Preserve ans(i - 1)
        Return ans
    End Function
End Module
0
 
LVL 1

Author Comment

by:kidnme
ID: 12050554
Looks great and should do exactly what I need it to, thank you. I will still have to compile it but I am working on getting Visual Basic .Net 2003 now.

great job!

0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Navigation is an important part of web design from a usability perspective. But it is often a pain when it comes to a developer’s perspective. By navigation, it often means menuing. This is less theory and more practical of how to get a specific gro…
Introduction: Recently, I got a requirement to zip all files individually with batch file script in Windows OS. I don't know much about scripting, but I searched Google and found a lot of examples and websites to complete my task. Finally, I was ab…
The goal of this video is to provide viewers with basic examples to understand and use switch statements in the C programming language.
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now