‘NSIS’ タグのついている投稿

WindowsにISC BIND9をNSISでインストールする

WindowsでDNSサーバを立てるにあたってBINDを使ったのですが、標準のBINDInstall.exeが使いにくいのと、サイレントインストールしたかったので、NSISを使ってインストーラーを作る方向にしました。

基本的には全部展開して設置したあと、VC2005再頒布可能パッケージをインストールして、named.exeをサービス登録するだけです。

ファイアウォールの許可もしています。

プラグインを3つ使っています。
Nsisunz plug-in – NSIS Zipの展開
NSIS Simple Service Plugin – NSIS サービス登録
NSIS Simple Firewall Plugin – NSIS ファイアウォール登録
Zipを二重にするのももったいないので、あらかじめ展開しておくのが妥当かとは思います。

設定ファイルは適当に作ってください。

あと肝心のBINDはこちら。
BIND | Internet Systems Consortium

!include "MUI.nsh"
!include "Sections.nsh"

!define TEMP $R0
!define TEMP2 $R1
!define VAL1 $R2
!define VAL2 $R3

Name "BIND9 Installer"
OutFile "setup.exe"

InstallDir "C:\dns"

!define BIND9_FILENAME "BIND9.8.1-P1.zip"

Section auto_setup
  
  SetOutPath "$TEMP"
  File "${BIND9_FILENAME}"
  
  SetOutPath "$INSTDIR\bin"
  nsisunz::Unzip "$TEMP\${BIND9_FILENAME}" "$INSTDIR\bin"
  
  ; VC++ 2005 redistインストール
  ExecWait '"$INSTDIR\bin\vcredist_x86.exe" /q:a /c:"VCREDI~1.EXE /q:a /c:""msiexec /i vcredist.msi /qb!"" "' 
  
  ; BINDのインストールディレクトリをレジストリに登録
  WriteRegStr HKLM "SOFTWARE\ISC\BIND" "InstallDir" "$INSTDIR"
  
  ; etcとconfに設定ファイルを配置する
  SetOutPath "$INSTDIR\conf"
  File /r "conf\*.txt"
  
  SetOutPath "$INSTDIR\etc"
  File "named.conf"
  ExecWait '"$INSTDIR\bin\rndc-confgen" -a -b 512'
  
  ; Firewall許可
  SimpleFC::AddApplication "ISC BIND" "$INSTDIR\bin\named.exe" 0 2 "" 1
  
  ; BINDをサービスに登録
  SimpleSC::InstallService "BIND" "ISC BIND" "16" "2" "$INSTDIR\bin\named.exe" "" "NT AUTHORITY\NetworkService" ""
  
  ; BINDサービス起動
  SimpleSC::StartService "BIND" "" 30

SectionEnd

Section "Uninstall"
  
  ; サービス停止と解除
  SimpleSC::StopService "BIND" 1 30
  SimpleSC::RemoveService "BIND"
  
  ; レジストリ削除
  DeleteRegKey HKLM "SOFTWARE\ISC\BIND"
  
  ; Firewall解除
  SimpleFC::RemoveApplication "$INSTDIR\bin\named.exe"
  
  ; ファイル削除
  RMDir /r "$INSTDIR"
  
SectionEnd

NSISから使える、起動中のプロセスを削除するプラグイン

常駐アプリケーションを作ったはいいが、アンインストールしようとしたときにプロセスが起動していると、削除できなくて困る。

というわけで、プロセスを殺す目的のプラグインDLLを作成。コンパイル済みファイルはエントリの最後に。

taskkillコマンド使えという話ですが、Windows XP Home Editionにはtaskkillコマンドが含まれていないので。

今回もさっくりC#で。C#.NETで作ってしまうと、そのままではNSISで使えないのだけど、.NETで作ったDLLをNSISのプラグインとして使う方法は以下のエントリを参照。

NSISで、C#で書かれた自作のDLLを使う : blog.loadlimit – digital matter –

ソースは以下。C#でクラスライブラリとしてプロジェクトを作成。プロセス名を指定すると、マッチしたプロセスを全部終了してくれます。

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace TaskKill
{
    public class TaskKill
    {
        [DllImport("Psapi.dll", SetLastError = true)]
        static extern bool EnumProcesses(
           [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)] [In][Out] UInt32[] processIds,
             UInt32 arraySizeBytes,
             [MarshalAs(UnmanagedType.U4)] out UInt32 bytesCopied
          );

        [DllImport("kernel32.dll")]
        static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
           uint dwProcessId);

        [Flags]
        public enum ProcessAccessFlags : uint
        {
            All = 0x001F0FFF,
            Terminate = 0x00000001,
            CreateThread = 0x00000002,
            VMOperation = 0x00000008,
            VMRead = 0x00000010,
            VMWrite = 0x00000020,
            DupHandle = 0x00000040,
            SetInformation = 0x00000200,
            QueryInformation = 0x00000400,
            Synchronize = 0x00100000
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseHandle(IntPtr hObject);

        [StructLayout(LayoutKind.Sequential)]
        public struct StringBuffer
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string text;
        }

        [DllImport("psapi.dll")]
        private static extern bool EnumProcessModules(IntPtr hProcess, out IntPtr lphModule, int cb, out int lpcbNeeded);
        [DllImport("psapi.dll")]
        private static extern int GetModuleBaseName(IntPtr hProcess, IntPtr hModule, out StringBuffer lpBaseName, int nSize);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool TerminateProcess(IntPtr hProcess, int uExitCode);

        public static void KillByName(string pname)
        {

            UInt32 arraySize = 1024;
            UInt32 arrayBytesSize = arraySize * sizeof(UInt32);
            UInt32[] processIds = new UInt32[arraySize];
            UInt32 bytesCopied;

            bool success = EnumProcesses(processIds, arrayBytesSize, out bytesCopied);

            if (!success)
            {
                return;
            }
            if (0 == bytesCopied)
            {
                return;
            }

            UInt32 numIdsCopied = bytesCopied >> 2;

            if (0 != (bytesCopied & 3))
            {
                UInt32 partialDwordBytes = bytesCopied & 3;

                return;
            }

            for (UInt32 index = 0; index < numIdsCopied; index++)
            {
                IntPtr hProcess = OpenProcess(
                    ProcessAccessFlags.QueryInformation | ProcessAccessFlags.VMRead | ProcessAccessFlags.Terminate,
                    false,processIds[index]);
                if (hProcess.ToInt32() != 0)
                {
                    IntPtr hMod = IntPtr.Zero;
                    int cbNeeded;
                    StringBuffer szProcessName;
                    szProcessName.text = "<unknown>";

                    if (EnumProcessModules(hProcess, out hMod, Marshal.SizeOf(hMod), out cbNeeded))
                    {
                        GetModuleBaseName(hProcess, hMod, out szProcessName, Marshal.SizeOf(szProcessName));
                    }

                    if (szProcessName.text == pname)
                    {
                        TerminateProcess(hProcess, -1);
                    }

                    CloseHandle(hProcess);
                }
            }

        }

    }
}

で、これをNSISの配布ファイルに含めておいて、

; アンインストーラ
Section "Uninstall"
  ; アンインストールで使用するプラグインの準備
  InitPluginsDir
  SetOutPath $PLUGINSDIR
  File "TaskKill.dll"

  ; hogehoge.exeが起動していたら、それを落とす
  CLR::Call /NOUNLOAD "TaskKill.dll" "TaskKill.TaskKill" "KillByName" 1 "hogehoge.exe"

  ; インストールしたファイル群削除
  Delete $INSTDIR\*.*

  CLR::Destroy
SectionEnd

という感じでnsiファイルを記述。

ファイルは下記リンクからダウンロードしてお使いください。

download TaskKill.dll