OSDN Git Service

AppliStation-all,ウイルススキャン対応
authorttp <ttp@users.sourceforge.jp>
Wed, 3 Dec 2008 11:40:35 +0000 (11:40 +0000)
committerttp <ttp@users.sourceforge.jp>
Wed, 3 Dec 2008 11:40:35 +0000 (11:40 +0000)
 * na-getlibにウイルススキャンのコード、およびそのためのCOMを叩くクラス群を追加した。
 * COMを叩く際にスレッドをSTAにしないとダメなのでall-getとAppliStationの該当部を編集した。

git-svn-id: http://localhost/svn/AppliStation/trunk@997 34ed2c89-c49f-4a4b-abdb-c318350108cf

AppliStation/AppliStation.Util/ExecutionProgressViewer.cs
all-get/Main.cs
na-get-lib/NaGet.InteropServices/ComDirectAccess.cs [new file with mode: 0644]
na-get-lib/NaGet.Net/DownloadScanner.cs [new file with mode: 0644]
na-get-lib/NaGet.Net/GuidEnumeratorForCategories.cs [new file with mode: 0644]
na-get-lib/NaGet.Packages.Install/Installation.cs
na-get-lib/NaGet.SubCommands/NaGetInstall.cs
na-get-lib/na-get-lib.csproj

index 6ae5799..8742246 100644 (file)
@@ -223,6 +223,8 @@ namespace AppliStation.Util
                public void StartTaskSet()\r
                {\r
                        tasksetRunningThread = new Thread(taskSet.Run);\r
+                       // スレッドをSTAにしないとCOMアクセスできず、ウイルススキャンができない。\r
+                       tasksetRunningThread.SetApartmentState(ApartmentState.STA);\r
                        tasksetRunningThread.Start();\r
                }\r
                \r
index e3f7672..c0df0f1 100644 (file)
@@ -487,6 +487,7 @@ namespace AllGet
                        Console.WriteLine("誰か {0} をスーパー牛さんパワー化してくれ", executeFileName);\r
                }\r
                \r
+               [STAThread]\r
                public static void Main(string[] args)\r
                {\r
                        // アーカイブSYSTEM32をパスに足す\r
diff --git a/na-get-lib/NaGet.InteropServices/ComDirectAccess.cs b/na-get-lib/NaGet.InteropServices/ComDirectAccess.cs
new file mode 100644 (file)
index 0000000..f9ac556
--- /dev/null
@@ -0,0 +1,67 @@
+using System;\r
+using System.Runtime.InteropServices;\r
+using System.Runtime.InteropServices.ComTypes;\r
+\r
+namespace NaGet.InteropServices\r
+{\r
+       /// <summary>\r
+       /// COMをComInteropを経由せずにいじるための関数集\r
+       /// </summary>\r
+       public sealed class ComDirectAccess\r
+       {\r
+               [DllImport("ole32.dll")]\r
+               public static extern int CoInitialize(IntPtr pvReserved);\r
+               \r
+               [DllImport("ole32.dll")]\r
+               public static extern void CoUninitialize();\r
+               \r
+               [DllImport("ole32.dll", ExactSpelling=true, PreserveSig=false)]\r
+               [return: MarshalAs(UnmanagedType.Interface)]\r
+               static extern object CoCreateInstance(\r
+                       [In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,\r
+                       [MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter,\r
+                       CLSCTX dwClsContext,\r
+                       [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);\r
+               \r
+               [Flags()]\r
+               public enum CLSCTX : uint\r
+               {\r
+                   CLSCTX_INPROC_SERVER    = 0x1,\r
+                   CLSCTX_INPROC_HANDLER   = 0x2,\r
+                   CLSCTX_LOCAL_SERVER     = 0x4,\r
+                   CLSCTX_INPROC_SERVER16  = 0x8,\r
+                   CLSCTX_REMOTE_SERVER    = 0x10,\r
+                   CLSCTX_INPROC_HANDLER16 = 0x20,\r
+                   CLSCTX_RESERVED1        = 0x40,\r
+                   CLSCTX_RESERVED2        = 0x80,\r
+                   CLSCTX_RESERVED3        = 0x100,\r
+                   CLSCTX_RESERVED4        = 0x200,\r
+                   CLSCTX_NO_CODE_DOWNLOAD = 0x400,\r
+                   CLSCTX_RESERVED5        = 0x800,\r
+                   CLSCTX_NO_CUSTOM_MARSHAL    = 0x1000,\r
+                   CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000,\r
+                   CLSCTX_NO_FAILURE_LOG       = 0x4000,\r
+                   CLSCTX_DISABLE_AAA      = 0x8000,\r
+                   CLSCTX_ENABLE_AAA       = 0x10000,\r
+                   CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000,\r
+                   CLSCTX_INPROC           = CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,\r
+                   CLSCTX_SERVER           = CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER|CLSCTX_REMOTE_SERVER,\r
+                   CLSCTX_ALL              = CLSCTX_SERVER|CLSCTX_INPROC_HANDLER,\r
+               }\r
+               \r
+               private ComDirectAccess()\r
+               {\r
+               }\r
+               \r
+               public static object CreateInstance(Guid rclsid, object pUnkOuter, CLSCTX dwClsContext, Guid riid)\r
+               {\r
+                       return CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid);\r
+               }\r
+               \r
+               public static T CreateInstance<T>(Guid clsid, CLSCTX dwClsContext)\r
+               {\r
+                       Guid riid = new Guid(((GuidAttribute) Attribute.GetCustomAttribute(typeof(T), typeof(GuidAttribute))).Value);\r
+                       return (T) CreateInstance(clsid, null, dwClsContext, riid);\r
+               }\r
+       }\r
+}\r
diff --git a/na-get-lib/NaGet.Net/DownloadScanner.cs b/na-get-lib/NaGet.Net/DownloadScanner.cs
new file mode 100644 (file)
index 0000000..af3e39e
--- /dev/null
@@ -0,0 +1,114 @@
+using System;\r
+using System.Reflection;\r
+using System.Collections.Generic;\r
+using System.Runtime.InteropServices;\r
+using System.Runtime.InteropServices.ComTypes;\r
+using NaGet.InteropServices;\r
+\r
+namespace NaGet.Net\r
+{\r
+       public class DownloadScanner : IDisposable\r
+       {\r
+               #region COMInterop\r
+               [Flags()]\r
+               private enum MSOAVINFOFLAG : uint {\r
+                       fPath = 1,\r
+                       fReadOnlyRequest = 2,\r
+                       fInstalled = 4,\r
+                       fHttpDownload = 8,\r
+               }\r
+               \r
+               [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]\r
+               private struct MSOAVINFO {\r
+                       public int cbsize;\r
+\r
+                       [MarshalAs(UnmanagedType.U4)]\r
+                       public MSOAVINFOFLAG uFlags;\r
+                       \r
+                       public IntPtr hwnd;\r
+\r
+                       [MarshalAs(UnmanagedType.LPWStr)]\r
+                       public string pwzFullPath;\r
+                       [MarshalAs(UnmanagedType.LPWStr)]\r
+                       public string pwzHostName;\r
+                       [MarshalAs(UnmanagedType.LPWStr)]\r
+                       public string pwzOrigURL;\r
+               }\r
+               \r
+               [ComImport()]\r
+               [Guid("56FFCC30-D398-11D0-B2AE-00A0C908FA49")]\r
+               [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\r
+               private interface IOfficeAntiVirus {\r
+                       void Scan(ref MSOAVINFO pmsoavinfo);\r
+               }\r
+               #endregion\r
+               \r
+               /// <summary>\r
+               /// ウイルススキャナーに渡すプログラム・ホスト名。\r
+               /// </summary>\r
+               public string HostName {\r
+                       get {\r
+                               // 実行アセンブリ名を返す\r
+                               return Assembly.GetExecutingAssembly().GetName().FullName;\r
+                       }\r
+               }\r
+               \r
+               private List<IOfficeAntiVirus> scanners;\r
+               \r
+               public DownloadScanner()\r
+               {\r
+                       ComDirectAccess.CoInitialize(IntPtr.Zero);\r
+               }\r
+               \r
+               public void Dispose()\r
+               {\r
+                       if ((scanners != null) && (scanners.Count > 0)) {\r
+                               foreach (IOfficeAntiVirus i in scanners) {\r
+                                       Marshal.ReleaseComObject(i);\r
+                               }\r
+                               scanners.Clear();\r
+                       }\r
+                       \r
+                       ComDirectAccess.CoUninitialize();\r
+               }\r
+               \r
+               public bool HasScanner {\r
+                       get { return scanners.Count > 0; }\r
+               }\r
+               \r
+               public void Init()\r
+               {\r
+                       scanners = new List<IOfficeAntiVirus>();\r
+                       \r
+                       Guid IID_MSOfficeAntiVirus = new Guid(((GuidAttribute) Attribute.GetCustomAttribute(typeof(IOfficeAntiVirus), typeof(GuidAttribute))).Value);\r
+                       \r
+                       using (GuidEnumeratorForCategories guids = new GuidEnumeratorForCategories(IID_MSOfficeAntiVirus)) {\r
+                               foreach (Guid guid in guids) {\r
+                                       IOfficeAntiVirus oav = ComDirectAccess.CreateInstance<IOfficeAntiVirus>(guid, ComDirectAccess.CLSCTX.CLSCTX_INPROC_SERVER);\r
+                                       \r
+                                       scanners.Add(oav);\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               public bool Scan(string path, string origin)\r
+               {\r
+                       if (scanners.Count <= 0) {\r
+                               return false;\r
+                       }\r
+                       \r
+                       MSOAVINFO info = new MSOAVINFO();\r
+                       info.cbsize = Marshal.SizeOf(info);\r
+                       info.uFlags = MSOAVINFOFLAG.fPath | MSOAVINFOFLAG.fHttpDownload;\r
+                       info.hwnd = IntPtr.Zero;\r
+                       info.pwzFullPath = path;\r
+                       info.pwzHostName = HostName;\r
+                       info.pwzOrigURL = origin;\r
+                       \r
+                       foreach (IOfficeAntiVirus i in scanners) {\r
+                               i.Scan(ref info);\r
+                       }\r
+                       return true;\r
+               }\r
+       }\r
+}\r
diff --git a/na-get-lib/NaGet.Net/GuidEnumeratorForCategories.cs b/na-get-lib/NaGet.Net/GuidEnumeratorForCategories.cs
new file mode 100644 (file)
index 0000000..41bd433
--- /dev/null
@@ -0,0 +1,64 @@
+using System;\r
+using System.Collections.Generic;\r
+using Microsoft.Win32;\r
+\r
+namespace NaGet.Net\r
+{\r
+       /// <summary>\r
+       /// ICatManagerの暫定的かつピュアC#実装\r
+       /// </summary>\r
+       class GuidEnumeratorForCategories : IEnumerable<Guid>, IDisposable\r
+       {\r
+               private RegistryKey key;\r
+               \r
+               private string[] subkeys;\r
+               \r
+               private Guid catGuid;\r
+               \r
+               public GuidEnumeratorForCategories(Guid category)\r
+               {\r
+                       key = Registry.ClassesRoot.OpenSubKey(@"CLSID", false);\r
+                       subkeys = key.GetSubKeyNames();\r
+                       catGuid = category;\r
+               }\r
+               \r
+               public void Dispose()\r
+               {\r
+                       if (key != null) {\r
+                               key.Close();\r
+                       }\r
+               }\r
+               \r
+               \r
+               public IEnumerator<Guid> GetEnumerator()\r
+               {\r
+                       return _getEnumerator();\r
+               }\r
+               \r
+               System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()\r
+               {\r
+                       return _getEnumerator();\r
+               }\r
+               \r
+               private IEnumerator<Guid> _getEnumerator()\r
+               {\r
+                       // レジストリ内のImplemented Category内の値と比較する値\r
+                       string strCategory = catGuid.ToString("B").ToUpper();\r
+                       \r
+                       foreach (string subkey in subkeys) {\r
+                               // "CLSID"、基本タイプ("...046}")は無視する\r
+                               if ((subkey == "CLSID") ||\r
+                                    subkey.EndsWith("-0000-0000-C000-000000000046}", StringComparison.OrdinalIgnoreCase)) {\r
+                                       continue;\r
+                               }\r
+\r
+                               // Implemented Categoryにあれば、subkeyをGUIDに変換して返す\r
+                               RegistryKey guidKey = key.OpenSubKey(string.Format(@"{0}\Implemented Categories\{1}", subkey, strCategory), false);\r
+                               if (guidKey != null) {\r
+                                       guidKey.Close();\r
+                                       yield return new Guid(subkey);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
index 2cbfdab..f8ab19d 100644 (file)
@@ -74,6 +74,14 @@ namespace NaGet.Packages.Install
                        InstalledPackage = package;\r
                }\r
                \r
+               /// <summary>\r
+               /// インストーラファイルを取得する\r
+               /// </summary>\r
+               public string InstallerFile {\r
+                       get { return installerFile; }\r
+               }\r
+               \r
+               \r
                #region インストーラ状態チェック関連\r
                \r
                /// <summary>\r
@@ -183,6 +191,31 @@ namespace NaGet.Packages.Install
                }\r
                \r
                /// <summary>\r
+               /// インストーラファイルをスキャンする\r
+               /// </summary>\r
+               /// <remarks>ウイルスのため退避・削除されたときも例外を投げずにあたかも正常かのように動作しえます。</remarks>\r
+               /// <exception cref="ComException">スキャンで意図せぬ動作があったとき</exception>\r
+               /// <exception cref="FileNotFoundException">スキャン後にインストーラファイルが存在しないとき</exception>\r
+               /// <param name="scanner">スキャナーオブジェクト</param>\r
+               public void ScanInstallerFile(DownloadScanner scanner)\r
+               {\r
+                       Exception e = null;\r
+                       try {\r
+                               scanner.Scan(installerFile, InstalledPackage.Installer[installerIndex].Url.Href);\r
+                       } catch (System.Runtime.InteropServices.COMException ex) {\r
+                               e = ex;\r
+                       }\r
+                       \r
+                       if (! File.Exists(installerFile)) {\r
+                               // ファイルが消されているならばFileNotFoundExceptionを出す\r
+                               throw new FileNotFoundException(string.Empty, installerFile, e);\r
+                       } else if ( e != null ) {\r
+                               // ファイルが消されていないが例外が発生していたときは、その例外を投げる\r
+                               throw e;\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
                /// ハッシュ検証のためのハッシュの種類の数を返す\r
                /// </summary>\r
                /// <returns>ハッシュの個数</returns>\r
index 9e626b8..bfd003f 100644 (file)
@@ -65,6 +65,7 @@ namespace NaGet.SubCommands
                        \r
                        for (int i =0; i < Installations.Length; i++) {\r
                                taskSetNames.Add(string.Format("取得: {0}", Installations[i].ToString()));\r
+                               taskSetNames.Add(string.Format("ウイルススキャン: {0}", Installations[i].ToString()));\r
                        }\r
                        taskSetNames.Add("インストーラの検証");\r
                        for (int i =0; i < Installations.Length; i++) {\r
@@ -89,7 +90,7 @@ namespace NaGet.SubCommands
                                        currentTaskSetIndex = 0;\r
                                        packageInstallerDownloaded = false;\r
                                        \r
-                                       runDownloadInstallers();\r
+                                       runDownloadAndVirusCheckInstallers();\r
                                        if (done) return; // もしrunDownloadInstallers()内でエラー終了していたなら終了\r
                                        \r
                                        packageInstallerDownloaded = true;\r
@@ -130,7 +131,7 @@ namespace NaGet.SubCommands
                        }\r
                        \r
                        foreach (Installation inst in Installations) {\r
-                               string installTaskMsg = inst.ToString();\r
+                               string installTaskMsg = TaskSetNames[currentTaskSetIndex];\r
                                if (inst.Silent && (!inst.SupportsSilentOnly)) {\r
                                        installTaskMsg += " (サイレントインストール)";\r
                                }\r
@@ -173,10 +174,12 @@ namespace NaGet.SubCommands
                \r
                \r
                /// <summary>\r
-               /// 処理内容のダウンロード部分のサブルーチン\r
+               /// 処理内容のダウンロード・ウイルススキャン部分のサブルーチン\r
                /// </summary>\r
-               private void runDownloadInstallers()\r
+               private void runDownloadAndVirusCheckInstallers()\r
                {\r
+               using (DownloadScanner scanner = new DownloadScanner()) {\r
+               scanner.Init();\r
                        foreach (Installation inst in Installations) {\r
                                if (! inst.IsInstallablePackage()) {\r
                                        string msg = string.Format("{0}はインストールすることができません", inst.ToString());\r
@@ -186,7 +189,7 @@ namespace NaGet.SubCommands
                                        return;\r
                                }\r
                                \r
-                               RaiseTaskSetEvent(NaGetTaskSetEventType.STARTED_TASKSET, inst.ToString());\r
+                       RaiseTaskSetEvent(NaGetTaskSetEventType.STARTED_TASKSET, TaskSetNames[currentTaskSetIndex]);\r
                                \r
                                if (! inst.Downloaded) {\r
                                        try {\r
@@ -213,12 +216,36 @@ namespace NaGet.SubCommands
                                currentTaskSetIndex ++;\r
                                \r
                                if (inst.Downloaded) { // 正常終了\r
-                                       RaiseTaskSetEvent(NaGetTaskSetEventType.COMPLETED_TASKSET, inst.ToString());\r
+                                       RaiseTaskSetEvent(NaGetTaskSetEventType.COMPLETED_TASKSET, TaskSetNames[currentTaskSetIndex]);\r
                                } else { // インストールが完了せずに終わった=失敗=エラー\r
                                        RaiseTaskSetEvent(NaGetTaskSetEventType.ERROR, string.Format("{0}のインストーラを正常にダウンロードできませんでした", inst.ToString()));\r
+                                       done = true;\r
+                                       return;\r
                                }\r
+                               \r
+                               RaiseTaskSetEvent(NaGetTaskSetEventType.STARTED_TASKSET, TaskSetNames[currentTaskSetIndex]);\r
+                               if (scanner.HasScanner) {\r
+                                       try {\r
+                                               inst.ScanInstallerFile(scanner);\r
+                                       } catch (System.Runtime.InteropServices.COMException ex) {\r
+                                               RaiseTaskSetEvent(NaGetTaskSetEventType.WARNING,\r
+                                                                 string.Format("{0} (E{1})", ex.Message, ex.ErrorCode));\r
+                                       } catch (System.IO.FileNotFoundException ex) {\r
+                                               if (ex.InnerException is System.Runtime.InteropServices.COMException) {\r
+                                                       RaiseTaskSetEvent(NaGetTaskSetEventType.WARNING,\r
+                                                                         string.Format("{0} (E{1})", ex.InnerException.Message, ((System.Runtime.InteropServices.COMException) ex.InnerException).ErrorCode));\r
+                                               }\r
+                                               RaiseTaskSetEvent(NaGetTaskSetEventType.ERROR, "インストーラがウイルススキャナによって削除されました。");\r
+                                               done = true;\r
+                                               return;\r
+                                       }\r
+                               } else {\r
+                                       RaiseTaskSetEvent(NaGetTaskSetEventType.INFO, string.Format("ダウンロードしたファイルはウイルススキャンされませんでした(ウイルススキャンソフトが検出できませんでした)"));\r
+                               }\r
+                               RaiseTaskSetEvent(NaGetTaskSetEventType.COMPLETED_TASKSET, TaskSetNames[currentTaskSetIndex]);\r
                        }\r
                }\r
+               }\r
                \r
                /// <summary>\r
                /// ダウンロードしたパッケージが整合したか否かハッシュでチェック\r
index f8aa21e..c072962 100644 (file)
   </ItemGroup>\r
   <ItemGroup>\r
     <Compile Include="AssemblyInfo.cs" />\r
+    <Compile Include="NaGet.InteropServices\ComDirectAccess.cs" />\r
     <Compile Include="NaGet.InteropServices\CommonArchiverExtracter.cs" />\r
     <Compile Include="NaGet.InteropServices\CreateProcessCaller.cs" />\r
     <Compile Include="NaGet.InteropServices\DllAccess.cs" />\r
     <Compile Include="NaGet.InteropServices\PEFileInfoUtils.cs" />\r
     <Compile Include="NaGet.InteropServices\ShellLink.cs" />\r
+    <Compile Include="NaGet.Net\DownloadScanner.cs" />\r
+    <Compile Include="NaGet.Net\GuidEnumeratorForCategories.cs" />\r
     <Compile Include="NaGet.Packages.Install\DependeciesResolver.cs" />\r
     <Compile Include="NaGet.Packages.Install\InstallationLog.cs" />\r
     <Compile Include="NaGet.Packages\PackageListsManager.cs" />\r