常駐アプリケーションを作ったはいいが、アンインストールしようとしたときにプロセスが起動していると、削除できなくて困る。
というわけで、プロセスを殺す目的のプラグイン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ファイルを記述。
ファイルは下記リンクからダウンロードしてお使いください。