Unable to Enumerate and Delete Registry Values Using VBScript - vbscript

I need to iterate through the registry foe each application for which the following key exists:
HKCU\Software\Microsoft\Office\15.0\[NAME OF APPLICATION]\Resiliency\DisabledItems
So that I can delete any values stored here.
I wrote the below script, but all this seems to do is iterate through the keys until it reaches Outlook, at which point it assures me the key does not exist (even though it definitely does), and then the script stops running.
'***********************************************'
'------------------------------------Add-In Keys'
'***********************************************'
Sub AddInKeys()
On Error Resume Next
strOfficePath = "Software\Microsoft\Office\15.0\"
objReg.EnumKey conHKEY_CURRENT_USER, strOfficePath, arrOfficeSubKeys
for Each key in arrOfficeSubKeys
' Check if our DisabledItems key exists
If regExists("HKCU\Software\Microsoft\Office\15.0\" & key & "\Resiliency\DisabledItems\") Then
' If it does - enumerate the values under this key ...
objReg.EnumValues conHKEY_CURRENT_USER, strOfficePath & key & "\Resiliency\DisabledItems\", arrKeyValues
For Each value in arrKeyValues
' Delete key VALUE, but only IF it is not blank (this will be the default value)
If value <> "" Then
objShell.RegDelete "HKCU\Software\Microsoft\Office\15.0\" & key & "\Resiliency\DisabledItems\" & value
End If
Next
End If
Next
If Err <> 0 Then
strMessage = "ERROR: Sub - Add-In Keys"
End If
End Sub
'***********************************************'
'---Function to check existence of registry keys'
'***********************************************'
Function regExists(sKey)
ON ERROR RESUME NEXT
regExists = objShell.RegRead(sKey)
If Err.Number = 0 Then
regExists = True
Else
regExists = False
Err.Number = 0
End If
If Err <> 0 Then
strMessage = "ERROR: Sub - regExists"
End If
End Function
Some background: The script seems to work perfectly when I run it on my dev machine. It enumerates all keys and all values and deletes the ones which I need deleting. However, when I run this from a thin client (which is where the script will be deployed - in a log on script), I am seeing the behaviour described above.
When I load the registry hive from the test user (who is logged into the thin client), I can see that there are many more keys in addition to those being checked, but for some reason it stops checking after Outlook.
Am I missing some sort of error, or am I misunderstanding how the registry works?

This was due to the way our Citrix and Windows environment is set up.
The logon script is called from the server via Active Directory User -> Properties -> Profile
The script above where I was having the issue was executing locally on the machine, and so none of the application's registry keys were present, as these applications are installed on the server.
After adding my code to the server-hosted logon script, it was able to detect and delete all the required key values.

Related

Exporting the SAS Project Log file

I used to run 3 SAS EG Projects on a daily basis. Since a couple of days, we have a "SAS Scheduler" that is basically running those latter during the night (the first one at 00:00 AM, second one at 01:00 AM, third one at 03:00 AM). Each SAS Project has multiple SAS Programs.
All in all, that is great news, but this also mean I can't check the logs directly anymore.
To keep track of the night jobs, I am trying to find what could be the best way to export the log files for each project. I found out about the SAS Project Log recently, which basically summarize the logs from all the programs within a SAS Project.
I discovered CaseySmith's answer on the SAS Community forum, basically tweaking the .vbs script to save the SAS Project log file to a .txt using the following code:
Set objProjectLog = objProject.ProjectLog
objProjectLog.Clear()
objProjectLog.Enabled = True
'strProjectLog = objProjectLog.Text
objProjectLog.SaveAs "c:\temp\projectLog.txt"
But, 1) It is a .txt file not a log file and 2) I don't know where to add it in my current .vbs script:
Option Explicit
Dim app
Call dowork
'shut down the app
If not (app Is Nothing) Then
app.Quit
Set app = Nothing
End If
Sub dowork()
On Error Resume Next
'----
' Start up Enterprise Guide using the project name
'----
Dim prjName
Dim prjObject
prjName = "C:\Users\kermit\Desktop\Project.egp" 'Project Name
Set app = CreateObject("SASEGObjectModel.Application.8.1")
If Checkerror("CreateObject") = True Then
Exit Sub
End If
'-----
' open the project
'-----
Set prjObject = app.Open(prjName,"")
If Checkerror("app.Open") = True Then
Exit Sub
End If
'-----
' run the project
'-----
prjObject.run
If Checkerror("Project.run") = True Then
Exit Sub
End If
'-----
' Save the new project
'-----
prjObject.Save
If Checkerror("Project.Save") = True Then
Exit Sub
End If
'-----
' Close the project
'-----
prjObject.Close
If Checkerror("Project.Close") = True Then
Exit Sub
End If
End Sub
Function Checkerror(fnName)
Checkerror = False
Dim strmsg
Dim errNum
If Err.Number <> 0 Then
strmsg = "Error #" & Hex(Err.Number) & vbCrLf & "In Function " & fnName & vbCrLf & Err.Description
'MsgBox strmsg 'Uncomment this line if you want to be notified via MessageBox of Errors in the script.
Checkerror = True
End If
End Function
In the end, what I would like is that on the morning, I run a program that scan the 3 project log files for Notes, Warning and Errors and send to myself an email with the results. Hence, is there a way to export the SAS Project Log (not manually) in a folder?
So, first, what is this code doing?
Set objProjectLog = objProject.ProjectLog
objProjectLog.Clear()
This clears the project log. This needs to be done before your project is run - otherwise the log contains data from past runs. So put this before the prjOBject.Run().
objProjectLog.Enabled = True
'strProjectLog = objProjectLog.Text
objProjectLog.SaveAs "c:\temp\projectLog.txt"
This then exports the project log to a text file. You of course can call that text file whatever you want. You need this code to appear after your program runs, and somewhere before it closes. Right after PrjObject.Run() is probably fine.
You will need to update the names to match your vbs file's names - they use objproject and your vbs uses prjObject, but those are the same thing, just match the names.
Second - what else could you do? If VBS isn't your thing, you have a lot of other ways you could do this.
Export your EG project to a .sas file, then schedule this in base SAS with the normal output options. This may also be possible via the scheduling interface.
Use PROC PRINTTO to redirect your log inside your SAS code.
Copy your EG project to a location you can see. The EG project does contain the log of everything that was run - so there's no reason you couldn't just open the .egp and look at it, just make sure you're not doing that with the production file since you might forget to close out.
My preference is not to schedule EG projects, but to schedule .sas programs; use EG as the development environment and then export to .sas. This gives you more flexibility. But there are a lot of different ways to skin this cat.

Unzip file silently and without dialog box to overwrite

I am working on vbscript to unzip the multiple files one after another.
I am using below code
Dim folder(3)
folder(0) = "UBO90R1"
folder(1) = "UBO90R2"
folder(2) = "UBO100R1"
folder(3) = "UBO100R2"
For i = 0 To 3
unzip_Source = "D:\Autobackup\" & folder(i) & ".zip"
unzip_destination = "D:\Autobackup_unzip\" & folder(i) &"\"
Call ExtractFilesFromZip(unzip_Source,unzip_destination)
WScript.Echo "unzip Finished"
Next
Sub ExtractFilesFromZip(pathToZipFile, dirToExtractFiles)
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
pathToZipFile = fso.GetAbsolutePathName(pathToZipFile)
dirToExtractFiles = fso.GetAbsolutePathName(dirToExtractFiles)
If (Not fso.FileExists(pathToZipFile)) Then
WScript.Echo "Zip file does not exist: " & pathToZipFile
Exit Sub
End If
If Not fso.FolderExists(dirToExtractFiles) Then
WScript.Echo "Directory does not exist: " & dirToExtractFiles
Exit Sub
End If
dim sa : Set sa = CreateObject("Shell.Application")
Dim zip : Set zip = sa.NameSpace(pathToZipFile)
Dim d : Set d = sa.NameSpace(dirToExtractFiles)
d.CopyHere zip.items, 4
Do Until zip.Items.Count <= d.Items.Count
WScript.Sleep(200)
Loop
End Sub
The Problem currently I have that if the folder or file is already exist than it open the dialog to the user to select any option Overwrite, keep both file etc.
If I change the following line from the code
d.CopyHere zip.items, 4
'Integer 4 didn't show the progress bar
to
d.CopyHere zip.items, 16
'Integer 16 overwrite the existing file but it shows the progress bar.
I would like to overwrite the existing file without any dialog box and without any progress bar.
PS: Code to unzip copied from here.
Looking at help we would find the follwing.
Type: FILEOP_FLAGS
Flags that control the file operation. This member can take a combination of the following flags.
FOF_ALLOWUNDO
Preserve undo information, if possible.
Prior to Windows Vista, operations could be undone only from the same process that performed the original operation.
In Windows Vista and later systems, the scope of the undo is a user session. Any process running in the user session can undo another operation. The undo state is held in the Explorer.exe process, and as long as that process is running, it can coordinate the undo functions.
If the source file parameter does not contain fully qualified path and file names, this flag is ignored.
FOF_CONFIRMMOUSE
Not used.
FOF_FILESONLY
Perform the operation only on files (not on folders) if a wildcard file name (.) is specified.
FOF_MULTIDESTFILES
The pTo member specifies multiple destination files (one for each source file in pFrom) rather than one directory where all source files are to be deposited.
FOF_NOCONFIRMATION
Respond with Yes to All for any dialog box that is displayed.
FOF_NOCONFIRMMKDIR
Do not ask the user to confirm the creation of a new directory if the operation requires one to be created.
FOF_NO_CONNECTED_ELEMENTS
Version 5.0. Do not move connected files as a group. Only move the specified files.
FOF_NOCOPYSECURITYATTRIBS
Version 4.71. Do not copy the security attributes of the file. The destination file receives the security attributes of its new folder.
FOF_NOERRORUI
Do not display a dialog to the user if an error occurs.
FOF_NORECURSEREPARSE
Not used.
FOF_NORECURSION
Only perform the operation in the local directory. Do not operate recursively into subdirectories, which is the default behavior.
FOF_NO_UI
Windows Vista. Perform the operation silently, presenting no UI to the user. This is equivalent to FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR.
FOF_RENAMEONCOLLISION
Give the file being operated on a new name in a move, copy, or rename operation if a file with the target name already exists at the destination.
FOF_SILENT
Do not display a progress dialog box.
FOF_SIMPLEPROGRESS
Display a progress dialog box but do not show individual file names as they are operated on.
FOF_WANTMAPPINGHANDLE
If FOF_RENAMEONCOLLISION is specified and any files were renamed, assign a name mapping object that contains their old and new names to the hNameMappings member. This object must be freed using SHFreeNameMappings when it is no longer needed.
FOF_WANTNUKEWARNING
Version 5.0. Send a warning if a file is being permanently destroyed during a delete operation rather than recycled. This flag partially overrides FOF_NOCONFIRMATION.

Delete If Present at Destination, Copy To Destination If Error Move to Next

I have VBScript that I wrote a long while back to identify PDF based on the file name. It then appended data to the file name and moved it to the proper directory. I did it as a Select Case in order for it to loop for many file names. I am now attempting to modify the script to check if the file with the new name is already at the destination directory, and if so, delete the old file, and copy the new one (also if the file is open and can't be overwritten, ignore and move to the next). I've been searching on many forums, and have been able to find pieces of what I am attempting, but have been unable to successfully integrate the processes into my script. Here is what I have for my select case, this section is what gets repeated with the "VariableAddedtoFileName" changed.
Select Case Pname
Case "FileName"
sDestinationFolder = "\\Server\FileDir\"
sDestinationName = "VariableAddedtoFileName"
Set oFSO = CreateObject("Scripting.FileSystemObject")
sSourceFile = objStartFolder & "\" & objFile.Name
sDestinationFile = sDestinationFolder & "\" & Pname & " " & _
sDestinationName & Right(objFile.Name, 4)
If oFSO.FileExists(sDestinationFile) Then
Set oFSO = Nothing
Else
oFSO.MoveFile sSourceFile, sDestinationFile
Set oFSO = Nothing
End If
Case "StatementScriptTest"
Case Else
End Select
So if I change theSet oFSO line in the If oFSO.FileExists group to oFSO.DeleteFile sDestinationFile It deletes the file, but won't copy the new one. If Rerun, it then copies the file, since it is no longer there. I have tried multiple combinations of attempting to manipulate the if statements and then with no luck. I also attempted to delete the file prior to the if section with no avail. Any assistance would be greatly appreciated.
If the full script is needed I can provide, I only listed this section as it is the part that gets rerun numerous times. Also I am aware that there are multiple posts similar to this, but I want to figure out how to update my code to work.
Update: I have fixed the overwriting by using CopyFile:
If oFSO.FileExists(sDestinationFile) Then
oFSO.CopyFile sSourceFile, sDestinationFile, True
Else
oFSO.CopyFile sSourceFile, sDestinationFile, True
Set oFSO = Nothing
End If
But I am still getting errors if the file is open when the attempt to overwrite is made.
First, you won't need the IF statement if you will have the same code in each branch. Just use the oFSO.CopyFile sSourceFile, sDestinationFile, True and it will do the work for you.
Second, in order to catch the error, you will have to use On Error Resume Next declaration before the copy command and check if some error triggered:
On Error Resume Next ' this tells VB to not throw errors and to populate the Err object when an error occurs
oFSO.CopyFile sSourceFile, sDestinationFile, True
IF Err.Number <> 0 Then
' do something when error occurs
' ...
Err.Clear ' clears the error so it will not trigger this on the loop if no more errors occur
End IF
' When you want to stop ignoring the errors
On Error GoTo 0

Error reading registry entry for Firefox

I have an AutoIt v3 script (copied from the author of FF.au3):
#Include <FF.au3>
_FFStart("http://ff-au3-example.thorsten-willert.de/")
If _FFIsConnected() Then
Sleep(2000)
_FFAction("presentationmode", True)
Sleep(2000)
_FFOpenURL("http://www.google.com")
Sleep(2000)
_FFAction("back")
_FFAction("presentationmode", False)
Sleep(2000)
_FFOpenURL("chrome:bookmarks")
Sleep(2000)
_FFAction("alert", "Bye bye ...")
_FFQuit()
EndIf
Exit
But when I run it, I get an error message:
__FFStartProcess ==> General Error: Error reading registry entry for FireFox.
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mozilla\Mozilla Firefox\*CurrentVersion*\Main\PathToExe
Error from RegRead: 1
I have Firefox and AutoIt v3 installed, I downloaded the FF.au3 UDF to same directory as my script, and I have MozRepl Firefox plugin installed and activated (it's active at the menu->plug-ins, I don't see the "Activate on startup" option). I do have an entry at:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mozilla\Mozilla Firefox\26.0 (pl)\Main
That points to the right destination. Why is there an "Error reading registry entry for FireFox."?
Here is the relevant piece of code from the Firefox library:
Local $sHKLM = 'HKEY_LOCAL_MACHINE\SOFTWARE\'
If #OSArch <> 'X86' Then $sHKLM &= 'Wow6432Node\'
$sHKLM &= 'Mozilla\Mozilla Firefox'
Local $sFFExe = RegRead($sHKLM & "" & RegRead($sHKLM, "CurrentVersion") & "\Main", "PathToExe")
If #error Then
SetError(__FFError($sFuncName, $_FF_ERROR_GeneralError, "Error reading registry entry for FireFox." & #CRLF & _
$sHKLM & "\*CurrentVersion*\Main\PathToExe" & #CRLF & _
"Error from RegRead: " & #error))
Return 0
EndIf
It reads the key CurrentVersion (probably of type REG_SZ) from the path HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mozilla\Mozilla Firefox to get the current version of the application. Say for example that this returns the string "27.0".
Then it looks in HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mozilla\Mozilla Firefox\27.0\Main (based on the string it just found for the current version) for the key PathToExe (REG_SZ also probably). This is the first attempt and if it does not fail it uses this path for the executable.
If this fails though, it checks the path HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mozilla\Mozilla Firefox\*CurrentVersion*\Main (literally with the asterisks) for the key PathToExe. This fails as well and that's why you are getting the error.
Check all of the above registry paths for your system. It may be possible that the Firefox library needs to be updated for the later Firefox versions. Try an "as clean as possible" install of Firefox and see if that works. Also try repairing the installation via the setup/deinstaller if that is possible.
If nothing works and you are left to modifying your own system to get it to work, I will check and ask the author of the Firefox library to update it.
I think there's a missing backslash. In the FF.au3 UDF change the following:
Local $sFFExe = RegRead($sHKLM & "" & RegRead($sHKLM, "CurrentVersion") & "\Main", "PathToExe")
to:
Local $sFFExe = RegRead($sHKLM & "\" & RegRead($sHKLM, "CurrentVersion") & "\Main", "PathToExe")
Regards,
Gonnosuke
Most registry manipulation problems are about lack of permissions.
Add #RequireAdmin at the top of your code.
"When running on 64-bit Windows if you want to read a value specific to the 64-bit environment you have to suffix the HK... with 64 i.e. HKLM64."

VBS RegRead doesn't return value

I've got the following code:
Dim objShell,failing_path,working_path
failing_path = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Test"
working_path = "HKEY_LOCAL_MACHINE\SOFTWARE\7-zip\Path"
Set objShell = WScript.CreateObject("WScript.Shell")
WScript.Echo "Working: " & objShell.RegRead(working_path)
WScript.Echo "Not Working: " & objShell.RegRead(failing_path)
When executing I will get the Path from the 7-zip Registrykey, but the Test Key returns following error:
Error says: Registry wasn't opened for reading.
Here the proof that the Test-Key exists:
What am I doing wrong? I also tried to read the key via oReg.GetStringValue but this always returned null.
It seems like your script is running in a 32-Bit compatible scripting host but in a 64-bit OS.
Since 32 bit applications are automatically redirected to the WOW6432Node areas of the registry in 64-Bit OSes, RegRead method tries to read 32-Bit equivalent path like
HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Test
instead
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Test
So, if the redirected path does not exist you receive that error as expected.
You may need to force run your scripts in 64-bit compatible scripting hosts to get rid of that kind of implicit registry redirects.
When I tested this, I was able to read from non-Wow6432Node paths just fine, even when using a 32-bit cscript.exe from a 32-bit cmd.exe (Process Explorer showed image type 32-bit for both processes).
Perhaps it's something really simple? I was able to reproduce the behavior you described when I created a registry value with a spurious trailing space:
>>> key = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
>>> WScript.Echo sh.RegRead(key & "Test")
Unable to open registry key "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\
Uninstall\Test" for reading. (0x80070002)
>>> WScript.Echo sh.RegRead(key & "Test ")
asd

Resources