LoginSignup
9
7

More than 3 years have passed since last update.

【BAT】アプリケーションのアンインストールを行う

Last updated at Posted at 2020-08-23

はじめに

お仕事でアプリケーションの旧バージョンからのアップグレード処理をバッチファイルで作成しました。
今回、その中のアンインストールする処理部分を一般公開用に作り直しました。

アンインストール情報の取得

アンインストール情報はレジストリのアンインストールから取得していますので、[コントロール パネル]-[プログラム]-[プログラムと機能]にある一覧のアプリケーションが対象となります。

64bitと32bitのプラットフォームごとに取得するレジストリキーを変更しています。

if "%PROCESSOR_ARCHITECTURE%" EQU "AMD64" call :ARCH_X64 "%installTarget%" "%varsionTarget%"
if "%PROCESSOR_ARCHITECTURE%" EQU "x86" call :ARCH_X86 "%installTarget%" "%varsionTarget%"

64bit

レジストリのアンインストール情報
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\
HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\

32bit

レジストリのアンインストール情報
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\

仕様

管理者権限で実行するようにしています。

引数 内容
第1引数 アプリケーション名(空白がある場合はダブルクォーテーションで囲む)
第2引数 バージョン(指定がない場合、バージョンチェックはしない)

※アプリケーション名とバージョンの文字列検索は完全一致となっています。

> uninstall.bat "Microsoft Edge" 84.0.522.63

> uninstall.bat "Python 3.7.8 Executables (64-bit)"

アンインストールは、対象アプリケーションのレジストリ情報にある「UninstallString」を取得して実行しています。
文字列検索でFindを使用していることで「QuietUninstallString」の方を優先して取得してしまいます。
その為、「/SILENT」または「/S」のオプションを除外するようにしています。

%TEMP%\soft.txt ファイルにパイプ(|)区切りで、アプリケーション名、バージョン、アンインストール情報を出力しています。

ソースコード

uninstall.bat
@echo off
openfiles > nul 2>&1 
if %ERRORLEVEL% equ 0 goto Start
echo 管理者権限で実行してください。
pause
exit

cd /d %~dp0

:Start
setlocal enabledelayedexpansion
set installTarget=%~1
set versionTarget=%~2

rem アンインストール情報で確認
del /q %TEMP%\soft.txt >nul 2>&1
if "%PROCESSOR_ARCHITECTURE%" EQU "AMD64" call :ARCH_X64 "%installTarget%" "%versionTarget%"
if "%PROCESSOR_ARCHITECTURE%" EQU "x86" call :ARCH_X86 "%installTarget%" "%versionTarget%"

find "%installTarget%" %TEMP%\soft.txt >nul 2>&1
if not ERRORLEVEL 1 (
  echo インストール済み
  for /f "delims=| tokens=1,2*" %%a in ('find "%installTarget%" %TEMP%\soft.txt') do (
    set version=%%b
    set uninstall=%%c
  )
  echo !version!
  echo !uninstall!

  rem アンインストール処理
  echo !uninstall! | find "MsiExec.exe" >nul 2>&1
  if not ERRORLEVEL 1 (
    set msiStr=!uninstall:"=!
    set msiStr=!msiStr:/I=/X!
    !msiStr! /norestart
  ) else (
    set exeStr=!uninstall:/SILENT=!
    set exeStr=!exeStr:/S=!

    call !exeStr!
  )
) else (
  echo 未インストール
)

endlocal

:END
echo.
echo 終了します。何かキーを押してください。
echo.
pause >nul
exit /b

rem ---------------------------------------------------------
rem アンインストール情報の取得
rem ---------------------------------------------------------
:ARCH_X64
rem x64環境
call :GET_UNINSTALL %1 %2 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
if ERRORLEVEL 1 exit /b
call :GET_UNINSTALL %1 %2 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\
if ERRORLEVEL 1 exit /b
call :GET_UNINSTALL %1 %2 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\

exit /b

:ARCH_X86
rem x86環境
call :GET_UNINSTALL %1 %2 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
if ERRORLEVEL 1 exit /b
call :GET_UNINSTALL %1 %2 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\
if ERRORLEVEL 1 exit /b

exit /b

rem ---------------------------------------------------------
rem レジストリからアンイストール検索
rem ---------------------------------------------------------
:GET_UNINSTALL
for /f "tokens=*" %%a in ('reg query %3') do (
  set str=""
  for /f "tokens=2,*" %%I in ('reg query "%%a" /s 2^>nul ^| find "DisplayName"') do (
    set str=%%J
    if "!str!"==%1 (
      for /f "tokens=2,*" %%I in ('reg query "%%a" /s 2^>nul ^| find "DisplayVersion"') do (
        set ver=%2
        if "!ver!"=="""" set ver="%%J"
        if !ver!=="%%J" (
          set str=!str!^|%%J
          for /f "tokens=2,*" %%I in ('reg query "%%a" /s 2^>nul ^| find "UninstallString"') do (
            echo !str!^|%%J>>%TEMP%\soft.txt
          )
          exit /b 1
        )
      )
    )
  )
)

exit /b

実施テスト

パターン1

UninstallStringが「MsiExec.exe /I」となっている場合、「MsiExec.exe /X /norestart」に変更して実行している。
サイレントアンインストールにしたい場合、プログラムを変更して「/quiet」オプションを追加すればいいです。
msiexec インストールオプション

> uninstall.bat "Python 3.7.8 Executables (64-bit)"

uninstall_pattern1.png

パターン2

UninstallStringが「setup.exe --uninstall」となっている。

> uninstall.bat "Microsoft Edge" 84.0.522.63

uninstall_pattern2.png

パターン3

UninstallStringが「unins000.exe」(専用アンインストール)となっている。
今回は実行時にサイレントオプション「/SILENT」を除外している。

> uninstall.bat "WinMerge 2.16.6.11 x64"

uninstall_pattern3.png

パターン4

UninstallStringが「Uninstall」(専用アンインストール)となっている。
今回は実行時にサイレントオプション「/S」を除外している。

このパターンだけ、callコマンドを付けているが次の処理に流れてしまう。

> uninstall.bat "Unity Hub 2.3.2"

uninstall_pattern4.png

パターン5

UninstallStringが「rundll32.exe」(ClickOnceアンインストール)となっている。

> uninstall.bat ClickOnceTest 3.1.0.0

uninstall_pattern5.png

最後に

今回はあえて、バッチファイルのみで書いてみました。
バッチファイルだと他言語では簡単なことを実現するのに分からなくてハマりますね。

2重引用符の削除
set installTarget=%~1
set msiStr=!uninstall:"=!
コマンドの実行結果を出力しない
rem >nul 2>&1 を付ける
find "%installTarget%" %TEMP%\soft.txt >nul 2>&1
遅延環境変数
rem 変数の中身を見るときに「%(パーセント)」ではなく「!(エクスクラメーションマーク)」で囲む
setlocal enabledelayedexpansion

  for /f "delims=| tokens=1,2*" %%a in ('find "%installTarget%" %TEMP%\soft.txt') do (
    set version=%%b
    set uninstall=%%c
  )
  echo !version!
  echo !uninstall!

endlocal
レジストリ情報のサブキーの値取得
rem 入れ子にしてサブキーの値取得
for /f "tokens=*" %%a in ('reg query %3') do (
  set str=""
  for /f "tokens=2,*" %%I in ('reg query "%%a" /s 2^>nul ^| find "DisplayName"') do (

あと、以前の記事「【BAT】Microsoft Visual C++ 再頒布可能パッケージのインストール確認」でも書いたが、バッチのreg queryコマンドは遅いです。どうしても気になるなら、WSHやPowerShellにする方がいいでしょう。

9
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7