Good day all,
So I wrote a simple .bat file I was going to call on user startup to get the MS Office versions. Tested fine for admins but unfortuatnely not so with standard users. I suspect this is because we have:
Prevent access to registry editing tools
setup in the GPO, though disable regedit from running silenty is set to 'no'.
So my questions is a) is that the likely reason why my script isn't working and b) if so is there a regedit alternative. Trying to keep it nice and simple.
Code below:
@echo off
reg query HKEY_CLASSES_ROOT\Access.Application\CurVer
if errorlevel 1 goto five
for /f "tokens=3" %%i in ('reg query HKEY_CLASSES_ROOT\Access.Application\CurVer') do (
if %%i equ Access.Application.15 goto one
if %%i equ Access.Application.14 goto two
if %%i equ Access.Application.12 goto three
if %%i equ Access.Application.11 goto four
goto five
)
:one REM 2013 Pro
echo %computername%,Office 2013 Pro" >>\\SERVERNAME\bslogs$\officeVersions.csv
exit
:two REM 2010 Pro
echo %computername%,Office 2010 Pro >>\\SERVERNAME\bslogs$\officeVersions.csv
exit
:three REM 2007 Pro
echo %computername%,Office 2007 Pro" >>\\SERVERNAME\bslogs$\officeVersions.csv
exit
:four REM 2003 Pro
echo %computername%,Office 2003 Pro" >>\\SERVERNAME\bslogs$\officeVersions.csv
exit
:five REM Not Pro Verion
reg query HKEY_CLASSES_ROOT\Word.Application\CurVer
if errorlevel 1 goto ten
for /f "tokens=3" %%i in ('reg query HKEY_CLASSES_ROOT\Word.Application\CurVer') do (
if %%i equ Word.Application.15 goto six
if %%i equ Word.Application.14 goto seven
if %%i equ Word.Application.12 goto eight
if %%i equ Word.Application.11 goto nine
goto ten
)
:six REM 2013 STD
echo %computername%,Office 2013 Std" >>\\SERVERNAME\bslogs$\officeVersions.csv
exit
:seven REM 2010 STD
echo %computername%,Office 2010 Std" >>\\SERVERNAME\bslogs$\officeVersions.csv
exit
:eight REM 2007 STD
echo %computername%,Office 2007 Std" >>\\SERVERNAME\bslogs$\officeVersions.csv
exit
:nine REM 2003 STD
echo %computername%,Office 2003 Std" >>\\SERVERNAME\bslogs$\officeVersions.csv
exit
:ten REM no idea
echo %computername%,????" >>\\SERVERNAME\bslogs$\officeVersions.csv exit
Many thanks,
Martin
a) is that the likely reason why my script isn't working
I dunno. Sounds plausible.
b) if so is there a regedit alternative
You can query registry values with WMI and the StdRegProv class.
Trying to keep it nice and simple.
Sorry. It is neither nice nor simple.
I've had to simulate reg.exe
using WMI queries in the past to query remote registries. I hacked together a :getRegValue
function that automates much of this. Run this and see whether it will work for what you have in mind.
@echo off
setlocal
call :getRegValue accessVer HKCR\Access.Application\CurVer\
echo Result: %accessVer%
call :getRegValue shell "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell"
echo Result: %shell%
goto :EOF
:getRegValue <return_var> <root\path\valname>
setlocal enabledelayedexpansion
rem // constants
set "hive[HKCR]=&H80000000"
set "hive[HKCU]=&H80000001"
set "hive[HKLM]=&H80000002"
set "hive[HKU]=&H80000003"
set "hive[HKCC]=&H80000005"
set "type[1]=GetStringValue"
set "type[2]=GetExpandedStringValue"
set "type[3]=GetBinaryValue"
set "type[4]=GetDWORDValue"
set "type[7]=GetMultiStringValue"
set "type[11]=GetQWORDValue"
SET "wmic=wmic /namespace:\\root\default class stdregprov call"
rem // split %~2 into hive, path, leaf
set "regpath=%~2"
if "%regpath:~-1%"=="\" set "regpath=%regpath%(Default)"
set hive=%regpath:\=&rem;%
set "regpath=!regpath:%hive%\=!"
set "leaf=%regpath%"
:leaf
set "leaf=%leaf:*\=%"
if not "%leaf%"=="%leaf:\=%" goto leaf
set "regpath=!regpath:\%leaf%=!"
set "hive=!hive[%hive%]!"
rem // get data type of leaf (default: string)
set "method=%type[1]%"
for %%I in (names types nameidx) do set "%%I="
2>NUL (
for /f "tokens=2 delims={}" %%I in (
'%wmic% EnumValues hDefKey^="%hive%" sSubkeyName^="%regpath:\=\\%"^
^| find "= {"'
) do (
if not defined names (set "names=%%I") else set "types=%%I"
)
)
if defined names (
for %%n in (names types) do (
set idx=0
for %%I in (!%%n!) do (
if defined nameidx (
if !idx! equ !nameidx! set "method=!type[%%~I]!"
) else if /i "%%~I"=="%leaf%" (
set "nameidx=!idx!"
)
set /a idx += 1
)
)
)
if /i "%leaf%"=="(Default)" set "leaf="
rem // get data value of leaf
2>NUL (
for /f "delims=" %%I in (
'%wmic% %method% hDefKey^="%hive%" sSubkeyName^="%regpath:\=\\%"^
sValueName^="%leaf%" ^| findstr "[su]Value"'
) do (
for %%# in (%%I) do set "ret=%%~#"
)
)
endlocal & set "%~1=%ret%" & goto :EOF
As additional food for thought, wmic.exe
supports remote queries with the following syntax:
wmic /node:remotePC /user:domain\remoteAdmin /password:password verbs...
Rather than having each user run your script at logon, you could use the remote switches to query all machines in batch.
Partly as an academic exercise and partly to head off anyone who might be thinking "You should use PowerShell", I rewrote the :getRegValue
function to invoke PowerShell hybrid code. Unfortunately, querying the registry via WMI with PowerShell is still complicated. Although the string operations and object retrieval are easier, the marginally more economical code doesn't quite justify the added execution time.
<# : regval.bat
@echo off
setlocal
call :getRegValue accessVer HKCR\Access.Application\CurVer\
echo Result: %accessVer%
call :getRegValue shell "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell"
echo Result: %shell%
goto :EOF
:getRegValue <return_var> <root\path\valname>
setlocal
set "regpath=%~2"
for /f "delims=" %%I in (
'powershell -noprofile -noninteractive "iex (gc \"%~f0\" | out-string)"'
) do (
endlocal
set "%~1=%%~I"
)
goto :EOF
: powershell hybrid code #>
$hiveConst = @{
"HKCR" = 2147483648
"HKCU" = 2147483649
"HKLM" = 2147483650
"HKU" = 2147483651
"HKCC" = 2147483653
}
# http://www.vistax64.com/powershell/10160-powershell-remote-registry-access-via-wmi.html
$reg = gwmi -List -Namespace root\default | ?{ $_.Name -eq "StdRegProv" }
# split $env:regpath into $hive, $regpath, $leaf
$regpath = $env:regpath -split "\\"
$hive = $hiveConst[$regpath[0]]
$leaf = $regpath[$regpath.length - 1]
if ($leaf) {
$regpath = $regpath[1..($regpath.length - 2)] -join "\"
} else {
$regpath = $regpath[1..($regpath.length - 1)] -join "\"
}
if ($leaf -match "^\(Default\)$") { $leaf = "" }
# get data type of leaf (default: string)
$method = 1
$res = $reg.EnumValues($hive, $regpath)
for ($i = 0; $i -lt $res.sNames.length; $i++) {
if ($res.sNames[$i] -eq $leaf) {
$method = $res.Types[$i]
}
}
# get data value of leaf
switch ($method) {
1 { $reg.GetStringValue($hive, $regpath, $leaf).sValue; break }
2 { $reg.GetExpandedStringValue($hive, $regpath, $leaf).sValue; break }
3 { $reg.GetBinaryValue($hive, $regpath, $leaf).uValue; break }
4 { $reg.GetDWORDValue($hive, $regpath, $leaf).uValue; break }
7 { $reg.GetMultiStringValue($hive, $regpath, $leaf).sValue; break }
11 { $reg.GetQWORDValue($hive, $regpath, $leaf).uValue; break }
}
Just because I felt like a challenge, I decided to try a third version employing JScript hybrid code this time. Believe it or not, this is the fastest of all three methods.
@if (@CodeSection == @Batch) @then
@echo off & setlocal
call :getRegValue accessVer HKCR\Access.Application\CurVer\
echo Result: %accessVer%
call :getRegValue shell "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell"
echo Result: %shell%
goto :EOF
:getRegValue <return_var> <root\path\valname>
for /f "delims=" %%I in ('cscript /nologo /e:JScript "%~f0" "%~2"') do set "%~1=%%~I"
goto :EOF
@end // end batch / begin JScript hybrid code
function reg(strMethod, objParams) {
try {
var winmgmts = GetObject('winmgmts:root\\default'),
StdRegProv = winmgmts.Get('StdRegProv');
params = StdRegProv.Methods_(strMethod).InParameters.SpawnInstance_();
for (var i in objParams) params[i] = objParams[i];
return winmgmts.ExecMethod('StdRegProv', strMethod, params);
}
catch(e) { return {'sValue':''} }
};
var hiveConst = {
"HKCR" : 2147483648,
"HKCU" : 2147483649,
"HKLM" : 2147483650,
"HKU" : 2147483651,
"HKCC" : 2147483653
},
methodConst = {
"1" : "GetStringValue",
"2" : "GetExpandedStringValue",
"3" : "GetBinaryValue",
"4" : "GetDWORDValue",
"7" : "GetMultiStringValue",
"11" : "GetQWORDValue"
},
regpath = WSH.Arguments(0).split('\\'),
hive = hiveConst[regpath.shift()],
leaf = regpath.pop(),
regpath = regpath.join('\\');
if (/^\(Default\)$/.test(leaf)) leaf = '';
// get data type of leaf (default: string)
try {
var params = {'hDefKey': hive, 'sSubKeyName': regpath},
res = reg('EnumValues', params),
sNames = res.sNames.toArray(),
Types = res.Types.toArray();
for (var i in sNames) {
if (sNames[i] == leaf) var method = methodConst[Types[i]];
}
}
catch(e) { var method = methodConst[1] }
// get and output data value of leaf
var params = {'hDefKey': hive, 'sSubKeyName': regpath, 'sValueName': leaf},
res = reg(method, params);
WSH.Echo(res.sValue || res.uValue);