2 * This code is based on /mozilla/source/toolkit/components/downloads/src/nsDownloadScanner.cpp
\r
3 * and sample code at https://bugzilla.mozilla.org/show_bug.cgi?id=103487,
\r
4 * created by Rob Arnold.
\r
8 using System.Reflection;
\r
9 using System.Collections.Generic;
\r
10 using System.Runtime.InteropServices;
\r
11 using System.Runtime.InteropServices.ComTypes;
\r
12 using NaGet.InteropServices;
\r
16 public enum DownloadScannerResult : uint {
\r
18 InfectedAndCleaned = 1, // S_FALSE
\r
19 InfectedButNotCleaned = 0x80004005, // E_FAIL
\r
20 ErrorNotFound = 2, // ERROR_NOT_FOUND
\r
22 ScannerNotFound = 0xFFFFFFFF,
\r
26 /// ダウンロードしたファイルをスキャンする
\r
28 public class DownloadScannerService : IDisposable
\r
32 private enum MSOAVINFOFLAG : uint {
\r
34 fReadOnlyRequest = 2,
\r
39 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
\r
40 private struct MSOAVINFO {
\r
43 [MarshalAs(UnmanagedType.U4)]
\r
44 public MSOAVINFOFLAG uFlags;
\r
48 [MarshalAs(UnmanagedType.LPWStr)]
\r
49 public string pwzFullPath;
\r
50 [MarshalAs(UnmanagedType.LPWStr)]
\r
51 public string pwzHostName;
\r
52 [MarshalAs(UnmanagedType.LPWStr)]
\r
53 public string pwzOrigURL;
\r
57 [Guid("56FFCC30-D398-11D0-B2AE-00A0C908FA49")]
\r
58 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
\r
59 private interface IOfficeAntiVirus {
\r
61 uint Scan(ref MSOAVINFO pmsoavinfo);
\r
66 /// ウイルススキャナーに渡すプログラム・ホスト名。
\r
68 public static string HostName {
\r
71 return Assembly.GetExecutingAssembly().GetName().FullName;
\r
75 private List<IOfficeAntiVirus> scanners;
\r
78 /// コンストラクタ。内部でCOM呼び出し初期化(CoInitialize)されます。
\r
80 public DownloadScannerService()
\r
82 int result = ComDirectAccess.CoInitialize(IntPtr.Zero);
\r
84 throw new System.ComponentModel.Win32Exception();
\r
89 /// 内部でCOM開放(CoUninitialize)します。必ず呼び出す必要があります。
\r
91 public void Dispose()
\r
94 ComDirectAccess.CoUninitialize();
\r
95 GC.SuppressFinalize(this);
\r
101 /// <remarks>Init()呼出し後に使える</remarks>
\r
102 public bool HasScanner {
\r
103 get { return scanners.Count > 0; }
\r
107 /// 初期化処理としてウイルススキャンを探す。
\r
111 scanners = new List<IOfficeAntiVirus>();
\r
113 Guid IID_MSOfficeAntiVirus = new Guid(((GuidAttribute) Attribute.GetCustomAttribute(typeof(IOfficeAntiVirus), typeof(GuidAttribute))).Value);
\r
115 using (GuidEnumeratorForCategories guids = new GuidEnumeratorForCategories(IID_MSOfficeAntiVirus)) {
\r
116 foreach (Guid guid in guids) {
\r
117 IOfficeAntiVirus oav = ComDirectAccess.CreateInstance<IOfficeAntiVirus>(guid, ComDirectAccess.CLSCTX.CLSCTX_INPROC_SERVER);
\r
125 /// ウイルススキャンのオブジェクトを開放しInitの前の状態に戻す。
\r
127 public void Release()
\r
129 if ((scanners != null) && (scanners.Count > 0)) {
\r
130 foreach (IOfficeAntiVirus i in scanners) {
\r
131 Marshal.ReleaseComObject(i);
\r
138 /// ファイルをスキャンする。ウイルススキャンが複数個見つかっている
\r
139 /// ならばそれらすべてでスキャンする。
\r
140 /// ウイルススキャンの実装によるが、ウイルス発見時にはダイアログが開く。
\r
141 /// ウイルスの処理はユーザに委ねられるので、それの制御は一切できない。
\r
143 /// <remarks>ウイルスが見つかったか否かは取得できない。</remarks>
\r
144 /// <remarks>本メソッド呼び出し後にウイルスが退避されているかもしれないが、ファイルの存在確認でしかそれをチェックできない</remarks>
\r
145 /// <param name="path">ファイルのパス</param>
\r
146 /// <param name="origin">ファイルをダウンロードしたURL。nullであってはならない</param>
\r
147 /// <exception cref="COMException">COMのエラー発生時。たとえば、AVGではウイルスと検出されたのにユーザが「無視」を指定したときにも投げられる。</exception>
\r
148 /// <returns>ウイルススキャン結果。</returns>
\r
149 /// <remarks>Init()呼出し後に使える</remarks>
\r
150 public DownloadScannerResult Scan(string path, string origin)
\r
152 MSOAVINFO info = new MSOAVINFO();
\r
153 info.cbsize = Marshal.SizeOf(info);
\r
154 info.uFlags = MSOAVINFOFLAG.fPath | MSOAVINFOFLAG.fHttpDownload;
\r
155 info.hwnd = IntPtr.Zero;
\r
156 info.pwzFullPath = path;
\r
157 info.pwzHostName = HostName;
\r
158 info.pwzOrigURL = origin;
\r
160 DownloadScannerResult result = DownloadScannerResult.ScannerNotFound;
\r
161 foreach (IOfficeAntiVirus i in scanners) {
\r
162 if (System.IO.File.Exists(path)) {
\r
163 result = (DownloadScannerResult) i.Scan(ref info);
\r
164 if (result == DownloadScannerResult.OK && System.IO.File.Exists(path) == false) {
\r
165 result = DownloadScannerResult.InfectedAndCleaned;
\r
168 result = DownloadScannerResult.ErrorNotFound;
\r
171 if (result != DownloadScannerResult.OK) {
\r