Use Reg Query whilst Registry Editing Tools Disabl

2019-02-23 11:57发布

问题:

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

回答1:

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);