public void ScanFolderForFilesShouldAddFiles() { // Arrange var directoryProvider = new Mock <IDirectoryProvider>(); var fileProvider = new Mock <IIsoFileProvider>(); fileProvider.Setup(m => m.GetIsoFile(@"D:\file01.txt")).Returns(new IsoFile { Name = "file01.txt" }); fileProvider.Setup(m => m.GetIsoFile(@"D:\file02.txt")).Returns(new IsoFile { Name = "file02.txt" }); fileProvider.Setup(m => m.GetIsoFile(@"D:\file03.txt")).Returns(new IsoFile { Name = "file03.txt" }); directoryProvider.Setup(m => m.GetDirectories(It.IsAny <string>())).Returns(new string[0]); directoryProvider.Setup(m => m.GetFiles(@"D:\", It.IsAny <string>())).Returns(new string[] { @"D:\file01.txt", @"D:\file02.txt", @"D:\file03.txt" }); var fileScanner = new FileScanner(directoryProvider.Object, fileProvider.Object); IsoFolder rootFolder; string driveLetter = "D"; FileScanResult fileScanResult = new FileScanResult(); // Act rootFolder = fileScanner.ScanFolderForFiles(driveLetter, fileScanResult, null); // Assert Assert.AreEqual(3, rootFolder.ChildFiles.Count); Assert.AreEqual(0, rootFolder.ChildFolders.Count); Assert.AreEqual("file01.txt", rootFolder.ChildFiles.ToList()[0].Name); Assert.AreEqual(0, fileScanResult.FilesWithErrorCount); Assert.AreEqual(0, fileScanResult.FoldersWithErrorCount); Assert.AreEqual(3, fileScanResult.ProcessedFileCount); Assert.AreEqual(1, fileScanResult.ProcessedFolderCount); }
private Image GetIconFileScanStatus(FileScanResult result) { Image image; switch (result.Status) { case FileScanStatus.ChangedFile: image = Resources.changed; break; case FileScanStatus.UnchangedFile: image = Resources.unchanged; break; case FileScanStatus.NewFile: image = Resources._new; break; case FileScanStatus.FileRemoved: image = Resources.changed; break; default: //Error image = Resources.error; break; } return(image); }
private List <FileScanResult> GetScanResults(int page = 0, int number = 10) { if (ScanOne == null || ScanOther == null) { MessageBox.Show("One or more scans were not found!", "Scan(s) not found", MessageBoxButtons.OK, MessageBoxIcon.Warning); } Scan latestScan; Scan previousScan; if (ScanOne.Time >= ScanOther.Time) { latestScan = ScanOne; previousScan = ScanOther; } else { latestScan = ScanOther; previousScan = ScanOne; } if (latestScan.HashAlgorithm.Id != previousScan.HashAlgorithm.Id) { MessageBox.Show("Hashing algorithms used for boths scans differ!", "Different hashing algorithms", MessageBoxButtons.OK, MessageBoxIcon.Warning); } List <FileScanResult> results = FileScanResult.GetScanResultCompare2Scans(latestScan, previousScan, page, number); return(results); }
public void ScanFolderForFilesShouldReturnRootFolder() { // Arrange var directoryProvider = new Mock <IDirectoryProvider>(); var fileProvider = new Mock <IIsoFileProvider>(); directoryProvider.Setup(m => m.GetDirectories(It.IsAny <string>())).Returns(new string[0]); var fileScanner = new FileScanner(directoryProvider.Object, fileProvider.Object); IsoFolder rootFolder; string driveLetter = "D"; FileScanResult fileScanResult = new FileScanResult(); // Act rootFolder = fileScanner.ScanFolderForFiles(driveLetter, fileScanResult, null); // Assert Assert.IsNotNull(rootFolder); Assert.AreEqual("/", rootFolder.Name); Assert.AreEqual(driveLetter + ":\\", rootFolder.Path); Assert.IsNull(rootFolder.Parent); Assert.AreEqual(0, rootFolder.ChildFiles.Count); Assert.AreEqual(0, rootFolder.ChildFolders.Count); Assert.AreEqual(0, fileScanResult.FilesWithErrorCount); Assert.AreEqual(0, fileScanResult.FoldersWithErrorCount); Assert.AreEqual(0, fileScanResult.ProcessedFileCount); Assert.AreEqual(1, fileScanResult.ProcessedFolderCount); }
public void Success_sync() { MetadefenderCoreClient metadefenderCoreClient = new MetadefenderCoreClient(GetMockApiUrl()); string existingDataId = "61dffeaa728844adbf49eb090e4ece0e"; CreateStub("/file", "POST", 200, GetJsonFromFile("MetadefenderCoreClient.test.resources.apiResponses.scanFile.scanFile_success.json")); CreateStub("/file/" + existingDataId, "GET", 200, GetJsonFromFile("MetadefenderCoreClient.test.resources.apiResponses.fetchScanResult.fetchScanResult_success.json")); using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MetadefenderCoreClient.test.resources.testScanFile.txt")) { FileScanResult result = metadefenderCoreClient.ScanFileSync( stream, new FileScanOptions().SetFileName("fileName.txt"), 50, 1000 * 30, 4000); Assert.AreEqual("Allowed", result.process_info.result); Assert.AreEqual("Clean", result.scan_results.scan_all_result_a); Assert.Null(result.extracted_files); } HttpServer.AssertWasCalled(x => { return(x.CustomVerb("POST", "/file")); } ); HttpServer.AssertWasCalled(x => { return(x.CustomVerb("GET", "/file/" + existingDataId)); } ); }
private void DisplayScanResults(List <FileScanResult> results) { Records = new List <Panel>(); for (int i = 0; i < results.Count; i++) { FileScanResult currentResult = results[i]; DisplayScanResultRecord(currentResult, i); } }
public void FileScanResultConstructorShouldInitializeValues() { //Arrange FileScanResult fileScanResult; //Act fileScanResult = new FileScanResult(); //Assert Assert.AreEqual(0, fileScanResult.ProcessedFileCount); Assert.AreEqual(0, fileScanResult.FilesWithErrorCount); Assert.AreEqual(0, fileScanResult.ProcessedFolderCount); Assert.AreEqual(0, fileScanResult.FoldersWithErrorCount); Assert.IsNotNull(fileScanResult.Log); Assert.AreEqual(0, fileScanResult.Log.Length); }
private async void btnScan_Click(object sender, EventArgs e) { _scanResult = new FileScanResult(); chkShowLog.Enabled = false; txtLog.Text = "scanning in progress..."; await FileScanner.ScanFolderForFilesAsync(txtPath.Text, _scanResult); chkShowLog.Enabled = true; if (chkShowLog.Checked) { txtLog.Text = _scanResult.Log.ToString(); } else { txtLog.Text = "scan finished!"; MessageBox.Show("Scan Finished!"); } }
private static void FetchScanResultByHash(string apiUrl, string hash) { MetadefenderCoreClient metadefenderCoreClient = new MetadefenderCoreClient(apiUrl); try { FileScanResult result = metadefenderCoreClient.FetchScanResultByHash(hash); Console.WriteLine("Fetch result by file hash: " + result.process_info.result); if (result.process_info.post_processing != null) { Console.WriteLine("post processing: " + result.process_info.post_processing); } } catch (MetadefenderClientException e) { Console.WriteLine("Error during fetch scan by hash: " + e.GetDetailedMessage()); } }
public void Success() { MetadefenderCoreClient metadefenderCoreClient = new MetadefenderCoreClient(GetMockApiUrl()); string existingHash = "e981b537cff14c3fbbba923d7a71ff2e"; CreateStub("/hash/" + existingHash, "GET", 200, GetJsonFromFile("MetadefenderCoreClient.test.resources.apiResponses.fetchScanResultByHash.fetchScanResultByHash_success.json")); FileScanResult result = metadefenderCoreClient.FetchScanResultByHash(existingHash); Assert.AreEqual(existingHash, result.data_id); Assert.AreEqual("Allowed", result.process_info.result); Assert.AreEqual("Clean", result.scan_results.scan_all_result_a); HttpServer.AssertWasCalled(x => { return(x.CustomVerb("GET", "/hash/" + existingHash)); }); }
private FileScanComparison ExpandIndividualDetailsRecord(Panel record, int id) { FileScanResult fileScanResult = Results[id]; FileScanComparison expandedRecord = new FileScanComparison(); expandedRecord.Name = "ExpandedRecord_" + id; expandedRecord.SetFileScanResult(fileScanResult); expandedRecord.BackColor = Color.White; expandedRecord.ForeColor = Color.Black; int y = record.Location.Y + record.Height + 1; expandedRecord.Location = new Point(record.Location.X, y); //expandedRecord.AutoScaleMode = AutoScaleMode.Inherit; RecordDetails.Add(expandedRecord); FileScanComparisons.Controls.Add(expandedRecord); return(expandedRecord); }
public void Success() { MetadefenderCoreClient metadefenderCoreClient = new MetadefenderCoreClient(GetMockApiUrl()); string existingDataId = "59f92cb3e3194c6381d3f8819a0d47ed"; CreateStub("/file/" + existingDataId, "GET", 200, GetJsonFromFile("MetadefenderCoreClient.test.resources.apiResponses.fetchScanResult.fetchScanResult_success.json")); FileScanResult result = metadefenderCoreClient.FetchScanResult(existingDataId); Assert.AreEqual(existingDataId, result.data_id); Assert.AreEqual("Allowed", result.process_info.result); Assert.AreEqual("Clean", result.scan_results.scan_all_result_a); Assert.Null(result.extracted_files); HttpServer.AssertWasCalled(x => { return(x.CustomVerb("GET", "/file/" + existingDataId)); } ); }
public void SetFileScanResult(FileScanResult result) { this.StatusLabel.Text = result.StatusString; if (!string.IsNullOrEmpty(result.LatestFileScan.Error.ErrorMessage)) { this.LatestChecksum.Text = result.LatestFileScan.Error.ErrorMessage; } else if (result.LatestFileScan.Checksum != null) { this.LatestChecksum.Text = result.LatestFileScan.Checksum; } if (!string.IsNullOrEmpty(result.PreviousFileScan.Error.ErrorMessage)) { this.PreviousChecksum.Text = result.PreviousFileScan.Error.ErrorMessage; } else if (result.PreviousFileScan.Checksum != null) { this.PreviousChecksum.Text = result.PreviousFileScan.Checksum; } }
private void backgroundDataBlockScanner_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { ResetAllScanAttributes(); return; } bool resultInvalidated = false; if (e.Error == null) { // Get used sourcedata IFragment sourceFragment = (_currentCodecStreamIndex == -1) ? ((IFragment)_currentDataBlocks[_currentDataBlockIndex]) : ((IFragment)_currentValidCodecStreams[_currentCodecStreamIndex]); // Report result to users of this scanner FileScanResult result = new FileScanResult(sourceFragment, e.Result as IResultNode, _currentDataBlocks[_currentDataBlockIndex], (_currentCodecStreamIndex >= 0) ? _currentValidCodecStreams[_currentCodecStreamIndex] : null, _currentValidCodecStreams.Count); ResultDetected(this, result); // Check if the result is invalidated by one of the event listeners. resultInvalidated = ScanNextCodecStreamOnInvalidation && !result.IsValid; } // Continue scan operation if (_currentCodecStreamIndex == -1) { // We where scanning a Datablock ScanNextDataBlock(); } else { // We where scanning a CodecStream ScanNextCodecStream(resultInvalidated); } }
private void DisplayScanResultRecord(FileScanResult result, int number) { Panel record = new Panel(); PictureBox icon = new PictureBox(); ToolTip statusToolTip = new ToolTip(); Label filePath = new Label(); ToolTip filePathToolTip = new ToolTip(); icon.Image = GetIconFileScanStatus(result); statusToolTip.SetToolTip(icon, GetToolTipTextFileScanStatus(result)); icon.Size = new Size(28, 28); icon.Location = new Point(2, 2); filePath.Text = result.FilePath; filePath.ForeColor = Color.FromArgb(67, 70, 78); filePath.Size = new Size(this.Width - icon.Width - 10, 28); record.Name = "Record_" + number; record.Location = new Point(0, (number * RECORD_HEIGHT) + 1); record.Size = new Size(800, RECORD_HEIGHT); record.BackColor = Color.White; record.Controls.Add(icon); record.Controls.Add(filePath); FileScanComparisons.Controls.Add(record); filePath.TextAlign = ContentAlignment.MiddleLeft; filePath.Location = new Point(icon.Width + 2, (record.Size.Height - filePath.Height) / 2); icon.MouseEnter += RecordControl_MouseHover; filePath.MouseEnter += RecordControl_MouseHover; record.MouseEnter += Record_MouseHover; icon.Click += RecordControl_Click; record.Click += Record_Click; filePath.Click += RecordControl_Click; Records.Add(record); }
public void Success_withArchive() { MetadefenderCoreClient metadefenderCoreClient = new MetadefenderCoreClient(GetMockApiUrl()); string existingDataId = "fafb3a12b0d141909b3a3ba6b26e42c9"; CreateStub("/file/" + existingDataId, "GET", 200, GetJsonFromFile("MetadefenderCoreClient.test.resources.apiResponses.fetchScanResult.fetchScanResult_success_withArchive.json")); FileScanResult result = metadefenderCoreClient.FetchScanResult(existingDataId); Assert.AreEqual(existingDataId, result.data_id); Assert.AreEqual("Allowed", result.process_info.result); Assert.AreEqual("Clean", result.scan_results.scan_all_result_a); Assert.NotNull(result.extracted_files); Assert.AreEqual(2L, result.extracted_files.files_in_archive.Count); HttpServer.AssertWasCalled(x => { return(x.CustomVerb("GET", "/file/" + existingDataId)); } ); }
public void Success_syncTimeout() { MetadefenderCoreClient metadefenderCoreClient = new MetadefenderCoreClient(GetMockApiUrl()); string existingDataId = "61dffeaa728844adbf49eb090e4ece0e"; CreateStub("/file", "POST", 200, GetJsonFromFile("MetadefenderCoreClient.test.resources.apiResponses.scanFile.scanFile_success.json")); CreateStub("/file/" + existingDataId, "GET", 200, GetJsonFromFile("MetadefenderCoreClient.test.resources.apiResponses.fetchScanResult.fetchScanResult_inProgress.json")); bool isException = false; using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MetadefenderCoreClient.test.resources.testScanFile.txt")) { try { // it should be a timeout, because we return in progress response every time FileScanResult result = metadefenderCoreClient.ScanFileSync(stream, new FileScanOptions().SetFileName("fileName.txt"), 50, 1000 * 30, 2000); } catch (TimeoutException) { isException = true; } Assert.True(isException); } HttpServer.AssertWasCalled(x => { return(x.CustomVerb("POST", "/file")); } ); HttpServer.AssertWasCalled(x => { return(x.CustomVerb("GET", "/file/" + existingDataId)); } ); }
private static void ScanFileSync(string apiUrl, string file) { MetadefenderCoreClient metadefenderCoreClient = new MetadefenderCoreClient(apiUrl); try { Stream inputStream = File.Open(file, FileMode.Open); FileScanResult result = metadefenderCoreClient.ScanFileSync(inputStream, new FileScanOptions().SetFileName(GetFileNameFromPath(file)), 200, 1000 * 30, 5000); Console.WriteLine("File scan finished with result: " + result.process_info.result); if (result.process_info.post_processing != null) { Console.WriteLine("post processing: " + result.process_info.post_processing); } } catch (MetadefenderClientException e) { Console.WriteLine("Error during file scan: " + e.GetDetailedMessage()); } catch (FileNotFoundException e) { Console.WriteLine("File not found: " + file + " Exception: " + e.Message); } }
private static void ScanFileSync(string apiUrl, string file, string rule = "") { int TaskTimeout = 600 * 1000; // 10 minutes MetadefenderCoreClient metadefenderCoreClient = new MetadefenderCoreClient(apiUrl); // The Core ID is usefull when using external LB // It actually points the cookie value that identify the core server the LB sent the file to string CoreId = apiUrl; try { Stream inputStream = File.Open(file, FileMode.Open); FileScanOptions fso = new FileScanOptions(); fso.SetFileName(GetFileNameFromPath(file)); if (!string.IsNullOrEmpty(rule)) { fso.SetRule(rule); } FileScanResult result = metadefenderCoreClient.ScanFileSync(inputStream, fso, 200, 1000 * 30, TaskTimeout, out CoreId); Console.WriteLine("File scan finished with result: " + result.process_info.result); if (CoreId.ToLower() != apiUrl.ToLower()) { Console.WriteLine("Core Id: {0}", CoreId); } if (result.process_info.post_processing != null) { if (!string.IsNullOrEmpty(result.process_info.post_processing.actions_ran)) { Console.WriteLine("post processing: " + result.process_info.post_processing.actions_ran); } if (result.process_info.post_processing.actions_ran.ToLower().Contains("sanitized")) { Console.WriteLine("\nFile was sanitized. Where would you like to save the sanitized file?"); Console.Write("Enter folder path: "); string destinationPath = Console.ReadLine(); if (!Directory.Exists(destinationPath)) { throw new Exception(string.Format("Destination folder {0} does not exist!", destinationPath)); } string cdrErrorMessage = null; Stream sanitizedStream = metadefenderCoreClient.DownloadSanitizedFile(result.data_id, out cdrErrorMessage); if (string.IsNullOrEmpty(cdrErrorMessage)) { string sanitizedFilename = result.process_info.post_processing.converted_destination; // save the file on disk using (var fileStream = File.Create(Path.Combine(destinationPath, sanitizedFilename))) { sanitizedStream.Seek(0, SeekOrigin.Begin); sanitizedStream.CopyTo(fileStream, 81920); } } else { Console.WriteLine("Failed to download sanitized file! Reason: {0}", cdrErrorMessage); } } } } catch (MetadefenderClientException e) { Console.WriteLine("Error during file scan: " + e.GetDetailedMessage()); } catch (FileNotFoundException e) { Console.WriteLine("File not found: " + file + " Exception: " + e.Message); } catch (Exception e) { Console.WriteLine("General error: " + file + " Exception: " + e.Message); } }
private async Task ScanIsoCompilations() { IsoFilesPath = GetIsoFilesReadyToScan(); ShowLogMessage("Scanning ISO compilations..."); var isoFilesFoundMessage = string.Format("{0} ISO files found", IsoFilesPath.Count); ShowInformationMessage(isoFilesFoundMessage); ShowLogMessage(isoFilesFoundMessage); var currentIsoFilePathIndex = 0; var unmountResult = await UnMount(); if (!unmountResult) { ShowLogMessage(string.Format("Error trying to unmount file from unit. Process aborted.")); return; } foreach (var isoFilePath in IsoFilesPath) { var mountResult = await VirtualCloneDrive.MountAsync(isoFilePath); if (mountResult.HasError) { ShowLogMessage(string.Format("Error trying to mount: {0}", Path.GetFileName(isoFilePath))); } else { var volumeName = VirtualCloneDrive.VolumeLabel; ShowLogMessage(string.Format("Scanning: {0}...", volumeName)); ShowInformationMessage(string.Format("Scanning: {0} [{1} of {2}]", volumeName, currentIsoFilePathIndex + 1, IsoFilesPath.Count)); if (!Entities.IsoVolumes.Any(i => i.VolumeLabel == volumeName)) { var iso = new IsoVolume { DateCreated = DateTime.Now, FileName = Path.GetFileName(IsoFilesPath[currentIsoFilePathIndex]), Size = Convert.ToDecimal(VirtualCloneDrive.TotalSize), VolumeLabel = volumeName }; Entities.IsoVolumes.Add(iso); Entities.SaveChanges(); var fileScanResult = new FileScanResult(); await IsoFileScanner.ScanFolderForFilesAsync(Entities, iso, VirtualCloneDrive.UnitLetter, fileScanResult); iso.FileCount = fileScanResult.ProcessedFileCount; Entities.Entry(iso).State = EntityState.Modified; Entities.SaveChanges(); ShowLogMessage(string.Format( "Scan finished for {0} ({1}), scan time: {2}, files processed: {3}", volumeName, iso.FileName, fileScanResult.TotalTime.ToString("hh\\:mm\\:ss"), fileScanResult.ProcessedFileCount)); SaveScanLog(iso.FileName, fileScanResult.Log.ToString()); } else { ShowLogMessage(string.Format("ISO '{0}' already exists on the database.", volumeName)); } unmountResult = await UnMount(); if (!unmountResult) { ShowLogMessage(string.Format("Error trying to unmount '{0}'. Process aborted.", isoFilePath)); break; } } currentIsoFilePathIndex += 1; } }
private string GetToolTipTextFileScanStatus(FileScanResult result) { return(result.StatusString); }
public async void Execute() { txtConsole.Text = string.Empty; lblInformation.Visible = false; ShowLogMessage("Scan started..."); TotalScanStartTime = DateTime.Now; var currentIsoFilePathIndex = 0; var isoFilesCount = _isoScannerInfo.SelectedIsoFileNames.Count(); var isoFilesFoundMessage = string.Format("{0} iso files found", isoFilesCount); ShowInformationMessage(isoFilesFoundMessage); ShowLogMessage(isoFilesFoundMessage); var unmountResult = await UnMount(); if (!unmountResult) { ShowLogMessage(string.Format("Error trying to unmount file from unit. Process aborted.")); return; } foreach (var isoFileName in _isoScannerInfo.SelectedIsoFileNames) { var isoFilePath = Path.Combine(_isoScannerInfo.IsoFolderPath, isoFileName); var mountResult = await VirtualCloneDrive.MountAsync(isoFilePath); if (mountResult.HasError) { ShowLogMessage(string.Format("Error trying to mount: {0}", Path.GetFileName(isoFilePath))); } else { var volumeName = VirtualCloneDrive.VolumeLabel; ShowLogMessage(string.Format("Scanning: {0}...", volumeName)); ShowInformationMessage(string.Format("Scanning: {0} [{1} of {2}]", volumeName, currentIsoFilePathIndex + 1, isoFilesCount)); if (!_unitOfWork.IsoVolumeRepository.Search(i => i.VolumeLabel == volumeName).Any()) { var initialFolder = new IsoFolder { Name = "$", Path = "/", IsoVolumeId = 0 }; var iso = new IsoVolume { Created = DateTime.Now, FileName = Path.GetFileName(isoFileName), Size = Convert.ToDecimal(VirtualCloneDrive.TotalSize), VolumeLabel = volumeName, FileCount = 0, RootFolder = initialFolder }; _unitOfWork.IsoVolumeRepository.Insert(iso); _unitOfWork.Save(); var fileScanResult = new FileScanResult(); try { var rootFolder = _fileScanner.ScanFolderForFiles(VirtualCloneDrive.UnitLetter, fileScanResult, iso); iso.FileCount = fileScanResult.ProcessedFileCount; iso.RootFolder = rootFolder; } catch (Exception ex) { ShowLogMessage(string.Format("Error scanning iso file: {0}", Path.GetFileName(isoFilePath))); ShowLogMessage(ex.Message); } ShowLogMessage(string.Format( "Scan finished for {0} ({1}), scan duration: {2}, files processed: {3}", volumeName, iso.FileName, fileScanResult.TotalTime.ToString("hh\\:mm\\:ss"), fileScanResult.ProcessedFileCount)); try { ShowLogMessage("Saving changes to the database..."); _unitOfWork.IsoVolumeRepository.Update(iso); _unitOfWork.IsoFolderRepository.Delete(initialFolder); _unitOfWork.Save(); ShowLogMessage("Changes successfully saved"); } catch (Exception ex) { _unitOfWork.IsoVolumeRepository.Delete(iso); _unitOfWork.IsoFolderRepository.Delete(initialFolder); _unitOfWork.Save(); ShowLogMessage("An error has ocurred saving changes to database"); fileScanResult.Log.Append(ex.Message); fileScanResult.Log.Append(ex.StackTrace); } try { ShowLogMessage("Saving log file..."); SaveScanLog(iso.FileName, fileScanResult.Log.ToString()); ShowLogMessage(string.Format("Log file successfully saved for {0} ", iso.FileName)); } catch { ShowLogMessage("Failed to save log file"); ShowLogMessage(fileScanResult.Log.ToString()); } } else { ShowLogMessage(string.Format("Iso Volume '{0}' already exists in the database.", volumeName)); } unmountResult = await UnMount(); if (!unmountResult) { ShowLogMessage(string.Format("Error trying to unmount '{0}'. Process aborted.", isoFilePath)); break; } } currentIsoFilePathIndex += 1; } ShowLogMessage("Scan finished"); var totalTime = string.Format("Total time: {0}", DateTime.Now.Subtract(TotalScanStartTime).ToString("hh\\:mm\\:ss")); ShowLogMessage(totalTime); ShowInformationMessage(string.Format("Scan finished. {0}", totalTime)); if (WorkFinished != null) { WorkFinished(); } }