3 using System.Diagnostics;
\r
5 using NaGet.SubCommands;
\r
7 namespace NaGet.Packages.Install
\r
11 /// ダウンロード・インストール処理をカプセル化するクラス
\r
13 public class Installation
\r
18 public Package InstalledPackage;
\r
21 /// (保存される)インストーラのファイルのパス
\r
23 public string InstallerFile;
\r
26 /// インストールが完了されたか否かのフラグ
\r
28 private bool installed = false;
\r
31 /// 起動するインストーラのインデックス番号
\r
33 protected int installerIndex = 0;
\r
36 /// 外部アプリのエラー出力の受信ハンドラ
\r
38 public event EventHandler<NaGet.Utils.AnyDataEventArgs<string>> ErrorDataReceived;
\r
41 /// 外部アプリの標準出力の受信ハンドラ
\r
43 public event EventHandler<NaGet.Utils.AnyDataEventArgs<string>> OutputDataReceived;
\r
48 /// <param name="package">インストールするパッケージ</param>
\r
49 public Installation(Package package)
\r
51 InstalledPackage = package;
\r
52 InstallerFile = getArchiveFilePath();
\r
53 installerIndex = GetPreferInstallerIndex(package);
\r
59 /// <param name="package">インストールするパッケージ</param>
\r
60 /// <param name="installerfile">(保存される)インストーラのファイルのパス</param>
\r
61 protected Installation(Package package, string installerfile)
\r
63 InstalledPackage = package;
\r
64 InstallerFile = installerfile;
\r
65 installerIndex = GetPreferInstallerIndex(package);
\r
71 public bool IsInstallablePackage()
\r
73 return installerIndex >= 0;
\r
77 /// すでにインストールされているパッケージを取得する
\r
79 /// <param name="installedPkgs">
\r
83 /// インストールされているパッケージの情報。インストールされたパッケージが見つからないならばnullを返す
\r
85 public InstalledPackage GetInstalledPackage(PackageList<InstalledPackage> installedPkgs)
\r
87 return installedPkgs.GetPackageForName(InstalledPackage.Name);
\r
93 /// <param name="downloader">ダウンローダオブジェクト</param>
\r
94 public void Download(Downloader downloader)
\r
97 string url = InstalledPackage.Installer[installerIndex].Url.Href;
\r
98 downloader.Download(url, InstallerFile);
\r
100 // サーバ指定のファイル名に変更する
\r
101 if (! string.IsNullOrEmpty(downloader.DownloadedFileName)) {
\r
102 string newFile = Path.Combine(Path.GetDirectoryName(InstallerFile), downloader.DownloadedFileName);
\r
103 File.Move(InstallerFile, newFile);
\r
104 InstallerFile = newFile;
\r
110 /// ハッシュ検証のためのハッシュの種類の数を返す
\r
112 /// <returns>ハッシュの個数</returns>
\r
113 public int GetRegisteredHashCount()
\r
115 HashValue[] hashValues = InstalledPackage.Installer[installerIndex].Hash;
\r
116 return (hashValues == null)? 0 : hashValues.Length;
\r
122 /// <returns>検証にパスしたらtrue、パスしなかったらfalse</returns>
\r
123 public bool VerifyHashValues()
\r
125 HashValue[] hashValues = InstalledPackage.Installer[installerIndex].Hash;
\r
126 if (hashValues != null) {
\r
127 foreach (HashValue hash in hashValues) {
\r
128 if (! hash.Validate(InstallerFile)) {
\r
137 private int invokeInstaller(string installerfile, InstallerType type)
\r
139 if (! File.Exists(installerfile)) {
\r
140 throw new FileNotFoundException(string.Format("{0} is not found, perhaps failed to download.", installerfile), installerfile);
\r
143 Process hProcess = null;
\r
146 case InstallerType.EXEC_INSTALLER:
\r
147 hProcess = Process.Start(installerfile);
\r
148 hProcess.WaitForExit();
\r
151 case InstallerType.MSI_PACKAGE:
\r
152 hProcess = Process.Start("msiexec", string.Format("/i \"{0}\"", installerfile));
\r
153 hProcess.WaitForExit();
\r
155 case InstallerType.ARCHIVE:
\r
156 string argument = string.Format("-i \"{0}\" \"{1}\"", installerfile, this.InstalledPackage.Name);
\r
157 hProcess = createExtractArchiveProcess(argument,
\r
158 this.OutputDataReceived,
\r
159 this.ErrorDataReceived);
\r
162 throw new NotImplementedException("Not implemented archive installation yet");
\r
165 return hProcess.ExitCode;
\r
167 if (hProcess != null) {
\r
174 /// インストーラ等を起動してインストール作業を行う
\r
176 /// <returns>インストーラの終了コード</returns>
\r
177 public int Install()
\r
179 string installerFile = this.InstallerFile;
\r
180 string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
\r
182 // アーカイブされているなら一旦展開
\r
183 if (InstalledPackage.ArchivedInstaller) {
\r
184 Directory.CreateDirectory(tempDir);
\r
186 string argument = string.Format("-t \"{0}\" \"{1}\"", installerFile, tempDir);
\r
187 using (Process hProcess = createExtractArchiveProcess(argument,
\r
188 this.OutputDataReceived,
\r
189 this.ErrorDataReceived)) {
\r
190 hProcess.WaitForExit();
\r
192 if (hProcess.ExitCode != 0) {
\r
193 throw new ApplicationException("Extract error " + installerFile + " to " + tempDir);
\r
197 // System.Text.StringBuilder output = new System.Text.StringBuilder(1024);
\r
198 // int res = NaGet.InteropServices.CommonArchiverExtracter.ExtractArchive(installerFile, tempDir, output, IntPtr.Zero);
\r
200 // throw new ApplicationException("Extract error\n" + output.ToString());
\r
203 installerFile = seekInstallerFile(tempDir, InstalledPackage.Type);
\r
204 if (installerFile == null && Directory.GetDirectories(tempDir).Length == 1) {
\r
205 installerFile = seekInstallerFile(Path.Combine(tempDir, Directory.GetDirectories(tempDir)[0]), InstalledPackage.Type);
\r
210 int exitCode = invokeInstaller(installerFile, InstalledPackage.Type);
\r
220 public bool Downloaded {
\r
222 return File.Exists(InstallerFile) && ((File.GetAttributes(InstallerFile) & FileAttributes.Hidden) != FileAttributes.Hidden);
\r
227 /// インストール作業済みであるか否か。実際にインストールされたかどうかではありません。
\r
229 public bool Installed {
\r
230 get { return installed; }
\r
234 /// インストーラの処理が成功してインストールされたプログラムが確認できるか否か。
\r
236 public bool InstallSuccessed {
\r
239 case InstallerType.ARCHIVE: // アーカイブインストーラはフォルダの確認
\r
240 return Directory.Exists(Path.Combine(NaGet.Env.ArchiveProgramFiles, InstalledPackage.Name));
\r
241 case InstallerType.EXEC_INSTALLER:
\r
242 case InstallerType.MSI_PACKAGE:
\r
243 return RegistriedUninstallers.GetInstalledPackageFor(InstalledPackage) != null;
\r
253 public InstallerType Type {
\r
255 return InstalledPackage.Type;
\r
260 /// もっともふさわしいインストーラ番号を返す
\r
262 /// <returns></returns>
\r
263 public static int GetPreferInstallerIndex(Package InstalledPackage)
\r
265 if (InstalledPackage.Type == InstallerType.CANNOT_INSTALL) { // インストール不能パッケージ
\r
272 for (int i = 0; i < InstalledPackage.Installer.Length; i++) {
\r
273 Platform platform = InstalledPackage.Installer[i].Platform;
\r
276 if (platform != null) {
\r
277 pts = (platform.IsRunnable())? 10 : 0;
\r
278 pts += (platform.IsRunnableArchOnWow64() && platform.IsRunnableOS())? 1 : 0;
\r
279 } else { // if (platform == null) {
\r
280 pts = 1; // null の場合は動作プラットホーム扱い
\r
282 if (pts > bestVal) {
\r
292 /// インストーラの一時保存先パスを生成
\r
294 private string getArchiveFilePath()
\r
296 Package package = this.InstalledPackage;
\r
298 string folderName = string.Format("{0}({1})", package.Name, package.Version);
\r
299 string fileName = NaGet.Utils.Url2filename(package.Installer[0].Url.Href);
\r
301 string folder = Path.Combine(NaGet.Env.ArchiveFolderPath, folderName);
\r
303 if (! File.Exists(Path.Combine(folder, fileName))) {
\r
304 if (Directory.Exists(folder)) {
\r
305 if (! File.Exists(Path.Combine(folder, fileName))) {
\r
306 fileName = seekInstallerFile(folder, package.Type) ?? fileName;
\r
309 Directory.CreateDirectory(folder);
\r
313 return Path.Combine(folder, fileName);
\r
319 /// <param name="basedir">探すフォルダ</param>
\r
320 /// <param name="type">インストーラの種類</param>
\r
321 /// <returns>探し出されたインストーラファイルのフルパス。存在しないならばnull</returns>
\r
322 private static string seekInstallerFile(string basedir, InstallerType type)
\r
324 if (! Directory.Exists(basedir)) {
\r
328 System.Collections.Generic.List<string> list = new System.Collections.Generic.List<string>();
\r
330 case InstallerType.MSI_PACKAGE:
\r
331 list.AddRange(Directory.GetFiles(basedir, "*.msi"));
\r
333 case InstallerType.EXEC_INSTALLER:
\r
334 list.AddRange(Directory.GetFiles(basedir, "*.exe"));
\r
336 case InstallerType.ARCHIVE:
\r
337 list.AddRange(Directory.GetFiles(basedir, "*.zip"));
\r
338 list.AddRange(Directory.GetFiles(basedir, "*.lzh"));
\r
339 list.AddRange(Directory.GetFiles(basedir, "*.cab"));
\r
340 list.AddRange(Directory.GetFiles(basedir, "*.7z"));
\r
341 list.AddRange(Directory.GetFiles(basedir, "*.tar*"));
\r
349 delegate(string file) {
\r
350 return ! File.Exists(file);
\r
354 // "setup"の語が入ったファイルはインストーラとみなし、優先選択
\r
355 foreach (string path in list) {
\r
356 if (Path.GetFileName(path).ToLower().IndexOf("setup") >= 0) {
\r
362 return (list.Count > 0)? list[0] : null;
\r
367 /// アーカイブファイルの展開・インストールを行う
\r
369 /// <param name="archiveInstArgs">"archive-inst.exe"への引数</param>
\r
370 /// <param name="outputReceived">標準出力用リスナ(null可)</param>
\r
371 /// <param name="errorReceived">エラー出力用リスナ(null可)</param>
\r
372 /// <returns>実行プロセス</returns>
\r
373 private Process createExtractArchiveProcess(string archiveInstArgs,
\r
374 EventHandler<NaGet.Utils.AnyDataEventArgs<string>> outputReceived,
\r
375 EventHandler<NaGet.Utils.AnyDataEventArgs<string>> errorReceived)
\r
377 string archiveInstExe = Path.GetFullPath("archive-inst.exe");
\r
378 if (! File.Exists(archiveInstExe)) {
\r
379 string errMsg = string.Format("\"{0}\" does not found!");
\r
380 throw new ApplicationException(errMsg,
\r
381 new FileNotFoundException(errMsg, archiveInstExe));
\r
384 ProcessStartInfo procInfo = new ProcessStartInfo(archiveInstExe, archiveInstArgs);
\r
385 procInfo.UseShellExecute = false;
\r
386 procInfo.CreateNoWindow = true;
\r
387 procInfo.WorkingDirectory = Environment.CurrentDirectory;
\r
389 return NaGet.Utils.ProcessStartWithOutputCapture(procInfo, outputReceived, errorReceived);
\r
392 public override string ToString()
\r
394 return string.Format("{0}({1})", InstalledPackage.Name, InstalledPackage.Version);
\r
397 public static string ToString(Installation[] installations)
\r
399 string[] strs = new string[installations.Length];
\r
400 for (int i = 0; i < installations.Length; i++) {
\r
401 strs[i] = installations[i].ToString();
\r
403 return string.Join(" ", strs);
\r
407 /// パッケージ配列をインストール処理配列に変換する便利メソッド
\r
409 /// <param name="pkgs">パッケージ配列</param>
\r
410 /// <returns>変換されたインストール処理配列</returns>
\r
411 public static Installation[] ConvertInstallations(Package[] pkgs)
\r
413 Installation[] insts = new Installation[pkgs.Length];
\r
414 for (int i = 0; i < pkgs.Length; i++) {
\r
415 insts[i] = new Installation(pkgs[i]);
\r