using System; using System.Text; using System.IO; using Microsoft.Build.BuildEngine; using NaGet.Packages; using NaGet.Packages.Install; namespace ArchiveInstall { class Program { public const string InstalledFileListName = ".applistation.installedfiles.xml"; public const string InstalledPackageFileName = ".applistation.package.xml"; private static void extract(string arcFile, string extractDestDir) { StringBuilder output = new StringBuilder(1024); int res = NaGet.InteropServices.CommonArchiverExtracter.ExtractArchive(arcFile, extractDestDir, output, IntPtr.Zero); if (res != 0) { Environment.Exit(res); } } private static void install(string fakeTargetDir, string targetDir) { // ハッシュ比較 if (Directory.Exists(targetDir)) { InstalledFileList list = null; try { list = NaGet.Utils.GetDeserializedObject(Path.Combine(targetDir, InstalledFileListName)); } catch { } if (list != null) { // 変更されたファイル(設定ファイル)をキープする処理 foreach (InstalledFile changedFile in list.Verify(targetDir)) { string changedFilePath = Path.Combine(targetDir, changedFile.Path); string toBeChangedFilePath = Path.Combine(fakeTargetDir, changedFile.Path); // 新規のファイルは退避させる if (File.Exists(toBeChangedFilePath)) { File.Move(toBeChangedFilePath, toBeChangedFilePath + ".newfile"); } // 変更済みファイルをinstallSrcの方に反映させる if (File.Exists(changedFilePath)) { if (! Directory.Exists(Path.GetDirectoryName(toBeChangedFilePath))) { // フォルダーがない場合は作る Directory.CreateDirectory(Path.GetDirectoryName(toBeChangedFilePath)); } File.Copy(changedFilePath, toBeChangedFilePath); } } } } else { Directory.CreateDirectory(targetDir); } // まずはフォルダーツリーを作成 foreach (string dir in Directory.GetDirectories(fakeTargetDir, "*", SearchOption.AllDirectories)) { string dirPath = NaGet.Utils.GetRelativePath(fakeTargetDir, dir); string targetDirPath = Path.Combine(targetDir, dirPath); if (! Directory.Exists(targetDirPath)) { Directory.CreateDirectory(targetDirPath); } } // ファイルをインストール(高速化のため移動) foreach (string file in Directory.GetFiles(fakeTargetDir, "*", SearchOption.AllDirectories)) { string filePath = NaGet.Utils.GetRelativePath(fakeTargetDir, file); string targetFilePath = Path.Combine(targetDir, filePath); if (File.Exists(targetFilePath)) { try { File.SetAttributes(targetFilePath, FileAttributes.Normal); File.Delete(targetFilePath); } catch (UnauthorizedAccessException e) { throw new IOException(string.Format("Access denied '{0}'", targetFilePath), e); } } File.Move(file, targetFilePath); } } private static void _installDllTo(string sys32path, string srcDir, Package package) { if (!string.IsNullOrEmpty(package.System32CopyFiles)) { if (! Directory.Exists(sys32path)) { Directory.CreateDirectory(sys32path); } string logfile = Path.Combine(sys32path, ".applistation.install."+package.Name+".log"); if (File.Exists(logfile)) { File.SetAttributes(logfile, FileAttributes.Normal); } using (FileStream fs = new FileStream(logfile, FileMode.Create)) using (StreamWriter sw = new StreamWriter(fs)){ foreach (string pathpat in package.System32CopyFiles.Split(';')) { foreach (string path in NaGet.Utils.ExtendWildcardFile(srcDir, pathpat)) { if ((File.GetAttributes(path) & FileAttributes.Directory) == 0) { // もしファイルならば string destPath = Path.Combine(sys32path, Path.GetFileName(path)); File.Copy(path, destPath, true); sw.WriteLine(Path.GetFileName(destPath)); } } } } File.SetAttributes(logfile, FileAttributes.Hidden); } } private static void postInstall(string targetDir, Package package) { // SYSTEM32へのコピーの実行 _installDllTo(NaGet.Env.ArchiveSystem32, targetDir, package); // インストールスクリプトの実行 if (! string.IsNullOrEmpty(package.InstallScript) ) { Engine engine = MSBuild.Engine; Project proj = new Project(engine); try { proj.LoadXml(package.InstallScript); } catch (InvalidProjectFileException e) { throw new ApplicationException("InstallScript is invalid", e); } engine.BuildProject(proj, "Install"); } // 直下のファイルで*.exeがGUIプログラムならプログラムフォルダーに登録 foreach (string exeFile in Directory.GetFiles(targetDir, "*.exe")) { if (NaGet.InteropServices.PEFileInfoUtils.GetPEFileType(exeFile) == NaGet.InteropServices.PEFileType.WinGUI) { string progGrpPath = Path.Combine(NaGet.Env.ArchiveProgramGroup, package.Name); if (! Directory.Exists(progGrpPath)) Directory.CreateDirectory(progGrpPath); using (NaGet.InteropServices.ShellLink lnk = new NaGet.InteropServices.ShellLink() ) { //string path = NaGet.Utils.GetRelativePath(progGrpPath, exeFile);// lnkファイルに相対パス指定不能 string path = exeFile; lnk.Path = path; //lnk.SetIconLocation(path, 0); // .lnk ファイル名 string lnkFilePath = Path.Combine(progGrpPath, lnk.GetSuitableShellLinkNameFor() + ".lnk"); if (File.Exists(lnkFilePath)) { // ファイル名がかぶってしまったとき lnkFilePath = Path.Combine(progGrpPath, Path.GetFileNameWithoutExtension(exeFile) + ".lnk"); } // 保存 lnk.ToPersistFile().Save(lnkFilePath, true); } } } } private static void storeInstalledFileList(string targetDir) { string installedFileListPath = Path.Combine(targetDir, InstalledFileListName); if (File.Exists(installedFileListPath)) { File.SetAttributes(installedFileListPath, FileAttributes.Normal); } InstalledFileList installedFileList = InstalledFileList.CreateFromFiles(targetDir); NaGet.Utils.PutSerializeObject(installedFileListPath, installedFileList); File.SetAttributes(installedFileListPath, FileAttributes.Hidden | FileAttributes.ReadOnly); } private static void storePackageXml(Package package, string destDir) { if (package == null) { return; } InstalledPackage pkg = InstalledPackage.PackageConverter(package); UninstallInformation uninfo = pkg.UninstallInfo; uninfo.InstallLocation = destDir; uninfo.UninstallString = string.Format("archive-inst -x \"{0}\"", package.Name); uninfo.EstimatedSize = (int) (NaGet.Utils.GetFileSize(destDir) >> 10); uninfo.InstallDateString = DateTime.Now.ToString("yyyyMMdd"); pkg.UninstallInfo = uninfo; string packageXmlFilePath = Path.Combine(destDir, InstalledPackageFileName); if (File.Exists(packageXmlFilePath)) { File.SetAttributes(packageXmlFilePath, FileAttributes.Normal); } NaGet.Utils.PutSerializeObject(packageXmlFilePath, pkg); File.SetAttributes(packageXmlFilePath, FileAttributes.Hidden | FileAttributes.ReadOnly); } private static void _removeDllFrom(string sys32path, InstalledPackage package) { if (! string.IsNullOrEmpty(package.System32CopyFiles) ) { string logfile = Path.Combine(sys32path, ".applistation.install."+package.Name+".log"); if (File.Exists(logfile)) { using (FileStream fs = new FileStream(logfile, FileMode.Open)) using (StreamReader sr = new StreamReader(fs)){ string fileName = sr.ReadLine().Trim(); string filePath = Path.Combine(sys32path, fileName); if (File.Exists(filePath)) { File.SetAttributes(filePath, FileAttributes.Normal); File.Delete(filePath); } } File.SetAttributes(logfile, FileAttributes.Normal); File.Delete(logfile); } } } private static void removePackage(InstalledPackage package, string targetDir) { // アンインストールスクリプトの実行 if (! string.IsNullOrEmpty(package.InstallScript)) { Engine engine = MSBuild.Engine; Project proj = new Project(engine); try { proj.LoadXml(package.InstallScript); } catch (InvalidProjectFileException e) { throw new ApplicationException("InstallScript is invalid", e); } engine.BuildProject(proj, "Uninstall"); } // GUIプログラムでプログラムフォルダーに登録しているのを解除 string progGrpPath = Path.Combine(NaGet.Env.ArchiveProgramGroup, package.Name); if (Directory.Exists(progGrpPath)) { NaGet.Utils.SetAttributeRecursive(progGrpPath, FileAttributes.Normal); Directory.Delete(progGrpPath, true); } // SYSTEM32からの削除の実行 _removeDllFrom(NaGet.Env.ArchiveSystem32, package); try { NaGet.Utils.SetAttributeRecursive(targetDir, FileAttributes.Normal); Directory.Delete(targetDir, true); } catch (UnauthorizedAccessException) { Console.Error.WriteLine("W: could not complete to delete '{0}': Permission denied", targetDir); Environment.Exit(1); } } private static void parseMainArguments(string[] args, out string arcFile, out string targetDir, out Package package) { if (args.Length < 1) { throw new ArgumentException(); } switch (args[0].ToLower()) { case "-t": if (args.Length != 3) { throw new ArgumentException(); } arcFile = args[1]; targetDir = args[2]; package = null; break; case "-i": if (args.Length != 3) { throw new ArgumentException(); } arcFile = args[1]; PackageList pkgList = NaGet.Utils.GetDeserializedObject>(NaGet.Env.PackageListFile); package = pkgList.GetPackageForName(args[2]); targetDir = Path.Combine(NaGet.Env.ArchiveProgramFiles, package.Name); break; case "-x": if (args.Length != 2) { throw new ArgumentException(); } arcFile = null; targetDir = Path.Combine(NaGet.Env.ArchiveProgramFiles, args[1]); package = null; string filepath = Path.Combine(targetDir, InstalledPackageFileName); if (File.Exists(filepath)) { package = NaGet.Utils.GetDeserializedObject(filepath); } else { Console.Error.WriteLine("Not found or already removed package : {0}", args[1]); Environment.Exit(100); } break; default: arcFile = null; targetDir = null; package = null; Console.Error.WriteLine("Unreconized command \"{0}\".", args[0]); Environment.Exit(100); break; } } [STAThread] public static void Main(string[] args) { // アーカイブSYSTEM32をパスに足す NaGet.Utils.AddDirectoryToPath(NaGet.Env.ArchiveSystem32); string arcFile = null; string targetDir = null; Package package = null; // 引数パースおよびヘルプ表示 try { parseMainArguments(args, out arcFile, out targetDir, out package); } catch (ArgumentException) { string executeFileName = System.AppDomain.CurrentDomain.FriendlyName; Console.Write("Usage:"); Console.WriteLine("\t{0} -t archive.zip target_dir\tExtraction", executeFileName); Console.WriteLine("\t{0} -i archive.zip PackageName\tInstall", executeFileName); Console.WriteLine("\t{0} -x PackageName\t\tUninstall", executeFileName); Console.WriteLine(); } // インストールおよび展開処理 if (arcFile != null) { string tempExtractDir = targetDir + "___temp___"; // HACK Directory.CreateDirectory(tempExtractDir); try { // STEP1. 書庫の展開 if (package != null && package.Type == InstallerType.ITSELF) { // 書庫でない場合展開せずにそのままコピーする string destFile = Path.Combine(tempExtractDir, Path.GetFileName(arcFile)); File.Copy(arcFile, destFile); } else { extract(arcFile, tempExtractDir); } // インストールの元となるフォルダを決定する string installSrc = tempExtractDir; // 展開先のトップに唯一のフォルダーしかないのであれば、そのフォルダー内を基準とする if (Directory.GetFiles(tempExtractDir).Length == 0 && Directory.GetDirectories(tempExtractDir).Length == 1) { installSrc = Directory.GetDirectories(tempExtractDir)[0]; } // STEP2. インストール元フォルダでインストールファイルリストの作成 storeInstalledFileList(installSrc); // STEP3. インストール install(installSrc, targetDir); if (package != null) { // STEP4. カスタマイズ可能な後処理 postInstall(targetDir, package); // STEP5. パッケージ情報をインストール先(targetDir)に置く storePackageXml(package, targetDir); } } catch (DllNotFoundException) { Console.Error.WriteLine("E: Does not exist archive dll for {0}", arcFile); // TODO Environment.Exit(10); } catch (IOException e) { Console.Error.WriteLine("E: File I/O Error : {0}", e.Message); Environment.Exit(1); } finally { Directory.Delete(tempExtractDir, true); } } else if (package is InstalledPackage) { removePackage((InstalledPackage) package, targetDir); } } } }