예제 #1
0
        private async Task GetImagesFromPage(PdfPage page, ImportParams importParams, ScannedImageSource.Concrete source)
        {
            if (page.CustomValues.Elements.ContainsKey("/NAPS2ImportedPage"))
            {
                source.Put(await ExportRawPdfPage(page, importParams));
                return;
            }

            // Get resources dictionary
            var resources = page.Elements.GetDictionary("/Resources");
            // Get external objects dictionary
            var xObjects = resources?.Elements.GetDictionary("/XObject");

            if (xObjects == null)
            {
                return;
            }
            // Iterate references to external objects
            foreach (var item in xObjects.Elements.Values)
            {
                // Is external object an image?
                if (!((item as PdfReference)?.Value is PdfDictionary xObject) ||
                    xObject.Elements.GetString("/Subtype") != "/Image")
                {
                    continue;
                }
                // Support multiple filter schemes
                var element        = xObject.Elements.Single(x => x.Key == "/Filter");
                var elementAsArray = element.Value as PdfArray;
                var elementAsName  = element.Value as PdfName;
                if (elementAsArray != null)
                {
                    var arrayElements = elementAsArray.Elements.Select(x => x.ToString()).ToArray();
                    if (arrayElements.Length == 2)
                    {
                        source.Put(DecodeImage(arrayElements[1], page, xObject, Filtering.Decode(xObject.Stream.Value, arrayElements[0]), importParams));
                    }
                }
                else if (elementAsName != null)
                {
                    source.Put(DecodeImage(elementAsName.Value, page, xObject, xObject.Stream.Value, importParams));
                }
                else
                {
                    throw new NotImplementedException("Unsupported filter");
                }
            }
        }
예제 #2
0
 protected override async Task ScanInternal(ScannedImageSource.Concrete source)
 {
     await Task.Factory.StartNew(async() =>
     {
         if (UseWorker)
         {
             using (var worker = workerServiceFactory.Create())
             {
                 worker.Callback.ImageCallback += (img, tempPath) =>
                 {
                     if (tempPath != null)
                     {
                         scannedImageHelper.RunBackgroundOcr(img, ScanParams, tempPath);
                     }
                     source.Put(img);
                 };
                 CancelToken.Register(worker.Service.CancelTwainScan);
                 await worker.Service.TwainScan(ScanDevice, ScanProfile, ScanParams, DialogParent?.SafeHandle() ?? IntPtr.Zero);
             }
         }
         else
         {
             twainWrapper.Scan(DialogParent, ScanDevice, ScanProfile, ScanParams, CancelToken, source, scannedImageHelper.RunBackgroundOcr);
         }
     }, TaskCreationOptions.LongRunning).Unwrap();
 }
예제 #3
0
        protected override async Task ScanInternal(ScannedImageSource.Concrete source)
        {
            using (var eventLoop = new WiaBackgroundEventLoop(ScanProfile, ScanDevice))
            {
                bool supportsFeeder = eventLoop.GetSync(wia => WiaApi.DeviceSupportsFeeder(wia.Device));
                if (ScanProfile.PaperSource != ScanSource.Glass && !supportsFeeder)
                {
                    throw new NoFeederSupportException();
                }
                bool supportsDuplex = eventLoop.GetSync(wia => WiaApi.DeviceSupportsDuplex(wia.Device));
                if (ScanProfile.PaperSource == ScanSource.Duplex && !supportsDuplex)
                {
                    throw new NoDuplexSupportException();
                }
                int  pageNumber = 1;
                int  retryCount = 0;
                bool retry      = false;
                bool done       = false;
                do
                {
                    ScannedImage image;
                    try
                    {
                        if (pageNumber > 1 && ScanProfile.WiaDelayBetweenScans)
                        {
                            int delay = (int)(ScanProfile.WiaDelayBetweenScansSeconds.Clamp(0, 30) * 1000);
                            Thread.Sleep(delay);
                        }
                        (image, done) = await TransferImage(eventLoop, pageNumber);

                        pageNumber++;
                        retryCount = 0;
                        retry      = false;
                    }
                    catch (ScanDriverException e)
                    {
                        if (ScanProfile.WiaRetryOnFailure && e.InnerException is COMException comError &&
                            (uint)comError.ErrorCode == 0x80004005 && retryCount < MAX_RETRIES)
                        {
                            Thread.Sleep(1000);
                            retryCount += 1;
                            retry       = true;
                            continue;
                        }
                        throw;
                    }
                    if (image != null)
                    {
                        source.Put(image);
                    }
                } while (!CancelToken.IsCancellationRequested && (retry || !done && ScanProfile.PaperSource != ScanSource.Glass));
            }
        }
예제 #4
0
        public ScannedImageSource Scan()
        {
            var source = new ScannedImageSource.Concrete();

            Task.Factory.StartNew(() =>
            {
                for (int i = 0; i < ImageCount; i++)
                {
                    Thread.Sleep(500);
                    source.Put(MakeImage());
                }
            }, TaskCreationOptions.LongRunning);
            return(source);
        }
예제 #5
0
        protected override async Task ScanInternal(ScannedImageSource.Concrete source)
        {
            // TODO: Test ADF
            var options    = new Lazy <KeyValueScanOptions>(GetOptions);
            var pageNumber = 1;

            var(img, done) = await Transfer(options, pageNumber);

            if (img != null)
            {
                source.Put(img);
            }

            if (!done && ScanProfile.PaperSource != ScanSource.Glass)
            {
                try
                {
                    while (true)
                    {
                        (img, done) = await Transfer(options, ++pageNumber);

                        if (done)
                        {
                            break;
                        }
                        if (img != null)
                        {
                            source.Put(img);
                        }
                    }
                }
                catch (Exception e)
                {
                    Log.ErrorException("Error in SANE. This may be a normal ADF termination.", e);
                }
            }
        }
예제 #6
0
 protected override async Task ScanInternal(ScannedImageSource.Concrete source)
 {
     await Task.Factory.StartNew(async() =>
     {
         if (UseWorker)
         {
             var parentHandle = DialogParent?.SafeHandle() ?? IntPtr.Zero;
             try
             {
                 using (var worker = workerServiceFactory.Create())
                 {
                     worker.Callback.ImageCallback += (img, tempPath) =>
                     {
                         if (tempPath != null)
                         {
                             scannedImageHelper.RunBackgroundOcr(img, ScanParams, tempPath);
                         }
                         source.Put(img);
                     };
                     CancelToken.Register(worker.Service.CancelTwainScan);
                     await worker.Service.TwainScan(ScanDevice, ScanProfile, ScanParams, parentHandle);
                 }
             }
             finally
             {
                 if (parentHandle != IntPtr.Zero)
                 {
                     // If the worker process hard crashes while a modal window is open, it may leave the parent
                     // window in a state where it can't be interacted with. This fixes that interaction.
                     //
                     // At the Windows API level, a modal window is implemented by doing two things:
                     // 1. Setting the parent on the child window
                     // 2. Disabling the parent window
                     // The first is implicitly undone when the worker process dies. The second is undone here.
                     Win32.EnableWindow(parentHandle, true);
                 }
             }
         }
         else
         {
             twainWrapper.Scan(DialogParent, ScanDevice, ScanProfile, ScanParams, CancelToken, source, scannedImageHelper.RunBackgroundOcr);
         }
     }, TaskCreationOptions.LongRunning).Unwrap();
 }
예제 #7
0
        private void ProduceImage(ScannedImageSource.Concrete source, Image output, ref int pageNumber)
        {
            var results = new[] { scannedImageHelper.PostProcessStep1(output, ScanProfile) };

            if (ScanProfile.DivideScanIntoTwoPages)
            {
                var result = results[0];

                // Should probably detect portrait vs landscape and split appropriately but this is the
                // main use case.
                var halfHeight = result.Height / 2;
                var firstRect  = new Rectangle(0, 0, result.Width, halfHeight);
                var secondRect = new Rectangle(0, halfHeight, result.Width, halfHeight);

                var firstPage = result.Clone(secondRect, result.PixelFormat);
                firstPage.RotateFlip(RotateFlipType.Rotate90FlipNone);
                var secondPage = result.Clone(firstRect, result.PixelFormat);
                secondPage.RotateFlip(RotateFlipType.Rotate90FlipNone);

                results = new[] { firstPage, secondPage };

                result.Dispose();
            }

            foreach (var result in results)
            {
                if (blankDetector.ExcludePage(result, ScanProfile))
                {
                    continue;
                }

                ScanBitDepth bitDepth = ScanProfile.UseNativeUI ? ScanBitDepth.C24Bit : ScanProfile.BitDepth;
                var          image    = new ScannedImage(result, bitDepth, ScanProfile.MaxQuality, ScanProfile.Quality);
                scannedImageHelper.PostProcessStep2(image, result, ScanProfile, ScanParams, pageNumber);
                string tempPath = scannedImageHelper.SaveForBackgroundOcr(result, ScanParams);
                result.Dispose();
                scannedImageHelper.RunBackgroundOcr(image, ScanParams, tempPath);
                source.Put(image);

                pageNumber++;
                InitNextPageProgress(pageNumber);
            }
        }
예제 #8
0
        private void ProduceImage(ScannedImageSource.Concrete source, Image output, ref int pageNumber)
        {
            using (var result = scannedImageHelper.PostProcessStep1(output, ScanProfile))
            {
                if (blankDetector.ExcludePage(result, ScanProfile))
                {
                    return;
                }

                ScanBitDepth bitDepth = ScanProfile.UseNativeUI ? ScanBitDepth.C24Bit : ScanProfile.BitDepth;
                var          image    = new ScannedImage(result, bitDepth, ScanProfile.MaxQuality, ScanProfile.Quality);
                scannedImageHelper.PostProcessStep2(image, result, ScanProfile, ScanParams, pageNumber);
                string tempPath = scannedImageHelper.SaveForBackgroundOcr(result, ScanParams);
                scannedImageHelper.RunBackgroundOcr(image, ScanParams, tempPath);
                source.Put(image);

                pageNumber++;
                InitNextPageProgress(pageNumber);
            }
        }
예제 #9
0
        public static void Scan(ScanProfile settings, ScanDevice device, IWin32Window pForm, IFormFactory formFactory, ScannedImageSource.Concrete source)
        {
            var tw = new Twain();

            if (!tw.Init(pForm.Handle))
            {
                throw new DeviceNotFoundException();
            }
            if (!tw.SelectByName(device.ID))
            {
                throw new DeviceNotFoundException();
            }
            var form = formFactory.Create <FTwainGui>();
            var mf   = new TwainMessageFilter(settings, tw, form);

            form.ShowDialog(pForm);
            foreach (var b in mf.Bitmaps)
            {
                source.Put(b);
            }
        }
예제 #10
0
        public static void Scan(ScanProfile settings, ScanDevice device, IWin32Window pForm, IFormFactory formFactory, ScannedImageSource.Concrete source)
        {
            var tw           = new Twain();
            var windowHandle = (Invoker.Current as Form)?.Handle ?? pForm.Handle;

            if (!tw.Init(windowHandle))
            {
                throw new DeviceNotFoundException();
            }
            if (!tw.SelectByName(device.Id))
            {
                throw new DeviceNotFoundException();
            }
            var form = Invoker.Current.InvokeGet(formFactory.Create <FTwainGui>);
            var mf   = new TwainMessageFilter(settings, tw, form);

            Invoker.Current.Invoke(() => form.ShowDialog(pForm));
            foreach (var b in mf.Bitmaps)
            {
                source.Put(b);
            }
        }
예제 #11
0
        public ScannedImageSource Import(string filePath, ImportParams importParams, ProgressHandler progressCallback, CancellationToken cancelToken)
        {
            var source = new ScannedImageSource.Concrete();

            Task.Factory.StartNew(() =>
            {
                try
                {
                    if (cancelToken.IsCancellationRequested)
                    {
                        source.Done();
                        return;
                    }

                    Bitmap toImport;
                    try
                    {
                        toImport = new Bitmap(filePath);
                    }
                    catch (Exception e)
                    {
                        Log.ErrorException("Error importing image: " + filePath, e);
                        // Handle and notify the user outside the method so that errors importing multiple files can be aggregated
                        throw;
                    }

                    using (toImport)
                    {
                        int frameCount = toImport.GetFrameCount(FrameDimension.Page);
                        int i          = 0;
                        foreach (var frameIndex in importParams.Slice.Indices(frameCount))
                        {
                            progressCallback(i++, frameCount);
                            if (cancelToken.IsCancellationRequested)
                            {
                                source.Done();
                                return;
                            }

                            toImport.SelectActiveFrame(FrameDimension.Page, frameIndex);
                            var image = new ScannedImage(toImport, ScanBitDepth.C24Bit, IsLossless(toImport.RawFormat), -1);
                            if (!importParams.NoThumbnails)
                            {
                                image.SetThumbnail(thumbnailRenderer.RenderThumbnail(toImport));
                            }
                            if (importParams.DetectPatchCodes)
                            {
                                image.PatchCode = PatchCodeDetector.Detect(toImport);
                            }

                            source.Put(image);
                        }

                        progressCallback(frameCount, frameCount);
                    }
                    source.Done();
                }
                catch (Exception e)
                {
                    source.Error(e);
                }
            }, TaskCreationOptions.LongRunning);
            return(source);
        }
예제 #12
0
        public ScannedImageSource Import(string filePath, ImportParams importParams, ProgressHandler progressCallback, CancellationToken cancelToken)
        {
            var source = new ScannedImageSource.Concrete();

            Task.Factory.StartNew(async() =>
            {
                if (cancelToken.IsCancellationRequested)
                {
                    source.Done();
                }

                int passwordAttempts = 0;
                bool aborted         = false;
                int i = 0;
                try
                {
                    PdfDocument document = PdfReader.Open(filePath, PdfDocumentOpenMode.Import, args =>
                    {
                        if (!pdfPasswordProvider.ProvidePassword(Path.GetFileName(filePath), passwordAttempts++, out args.Password))
                        {
                            args.Abort = true;
                            aborted    = true;
                        }
                    });
                    if (passwordAttempts > 0 &&
                        !document.SecuritySettings.HasOwnerPermissions &&
                        !document.SecuritySettings.PermitExtractContent)
                    {
                        errorOutput.DisplayError(string.Format(MiscResources.PdfNoPermissionToExtractContent, Path.GetFileName(filePath)));
                        source.Done();
                    }

                    var pages = importParams.Slice.Indices(document.PageCount)
                                .Select(index => document.Pages[index])
                                .TakeWhile(page =>
                    {
                        progressCallback(i++, document.PageCount);
                        return(!cancelToken.IsCancellationRequested);
                    });
                    if (document.Info.Creator != MiscResources.NAPS2 && document.Info.Author != MiscResources.NAPS2)
                    {
                        pdfRenderer.ThrowIfCantRender();
                        foreach (var page in pages)
                        {
                            source.Put(await ExportRawPdfPage(page, importParams));
                        }
                    }
                    else
                    {
                        foreach (var page in pages)
                        {
                            await GetImagesFromPage(page, importParams, source);
                        }
                    }
                }
                catch (ImageRenderException e)
                {
                    errorOutput.DisplayError(string.Format(MiscResources.ImportErrorNAPS2Pdf, Path.GetFileName(filePath)));
                    Log.ErrorException("Error importing PDF file.", e);
                }
                catch (Exception e)
                {
                    if (!aborted)
                    {
                        errorOutput.DisplayError(string.Format(MiscResources.ImportErrorCouldNot, Path.GetFileName(filePath)));
                        Log.ErrorException("Error importing PDF file.", e);
                    }
                }
                finally
                {
                    source.Done();
                }
            }, TaskCreationOptions.LongRunning);
            return(source);
        }
예제 #13
0
        private void InternalScan(TwainImpl twainImpl, IWin32Window dialogParent, ScanDevice scanDevice, ScanProfile scanProfile, ScanParams scanParams,
                                  CancellationToken cancelToken, ScannedImageSource.Concrete source, Action <ScannedImage, ScanParams, string> runBackgroundOcr)
        {
            if (dialogParent == null)
            {
                dialogParent = new BackgroundForm();
            }
            if (twainImpl == TwainImpl.Legacy)
            {
                Legacy.TwainApi.Scan(scanProfile, scanDevice, dialogParent, formFactory, source);
                return;
            }

            PlatformInfo.Current.PreferNewDSM = twainImpl != TwainImpl.OldDsm;
            var        session    = new TwainSession(TwainAppId);
            var        twainForm  = Invoker.Current.InvokeGet(() => scanParams.NoUI ? null : formFactory.Create <FTwainGui>());
            Exception  error      = null;
            bool       cancel     = false;
            DataSource ds         = null;
            var        waitHandle = new AutoResetEvent(false);

            int pageNumber = 0;

            session.TransferReady += (sender, eventArgs) =>
            {
                Debug.WriteLine("NAPS2.TW - TransferReady");
                if (cancel)
                {
                    eventArgs.CancelAll = true;
                }
            };
            session.DataTransferred += (sender, eventArgs) =>
            {
                try
                {
                    Debug.WriteLine("NAPS2.TW - DataTransferred");
                    pageNumber++;
                    using (var output = twainImpl == TwainImpl.MemXfer
                                        ? GetBitmapFromMemXFer(eventArgs.MemoryData, eventArgs.ImageInfo)
                                        : Image.FromStream(eventArgs.GetNativeImageStream()))
                    {
                        using (var result = scannedImageHelper.PostProcessStep1(output, scanProfile))
                        {
                            if (blankDetector.ExcludePage(result, scanProfile))
                            {
                                return;
                            }

                            var bitDepth = output.PixelFormat == PixelFormat.Format1bppIndexed
                                ? ScanBitDepth.BlackWhite
                                : ScanBitDepth.C24Bit;
                            var image = new ScannedImage(result, bitDepth, scanProfile.MaxQuality, scanProfile.Quality);
                            if (scanParams.DetectPatchCodes)
                            {
                                foreach (var patchCodeInfo in eventArgs.GetExtImageInfo(ExtendedImageInfo.PatchCode))
                                {
                                    if (patchCodeInfo.ReturnCode == ReturnCode.Success)
                                    {
                                        image.PatchCode = GetPatchCode(patchCodeInfo);
                                    }
                                }
                            }
                            scannedImageHelper.PostProcessStep2(image, result, scanProfile, scanParams, pageNumber);
                            string tempPath = scannedImageHelper.SaveForBackgroundOcr(result, scanParams);
                            runBackgroundOcr(image, scanParams, tempPath);
                            source.Put(image);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("NAPS2.TW - DataTransferred - Error");
                    error  = ex;
                    cancel = true;
                    StopTwain();
                }
            };
            session.TransferError += (sender, eventArgs) =>
            {
                Debug.WriteLine("NAPS2.TW - TransferError");
                if (eventArgs.Exception != null)
                {
                    error = eventArgs.Exception;
                }
                else if (eventArgs.SourceStatus != null)
                {
                    Log.Error("TWAIN Transfer Error. Return code = {0}; condition code = {1}; data = {2}.",
                              eventArgs.ReturnCode, eventArgs.SourceStatus.ConditionCode, eventArgs.SourceStatus.Data);
                }
                else
                {
                    Log.Error("TWAIN Transfer Error. Return code = {0}.", eventArgs.ReturnCode);
                }
                cancel = true;
                StopTwain();
            };
            session.SourceDisabled += (sender, eventArgs) =>
            {
                Debug.WriteLine("NAPS2.TW - SourceDisabled");
                StopTwain();
            };

            void StopTwain()
            {
                waitHandle.Set();
                if (!scanParams.NoUI)
                {
                    Invoker.Current.Invoke(() => twainForm.Close());
                }
            }

            void InitTwain()
            {
                try
                {
                    var        windowHandle = (Invoker.Current as Form)?.Handle;
                    ReturnCode rc           = windowHandle != null?session.Open(new WindowsFormsMessageLoopHook(windowHandle.Value)) : session.Open();

                    if (rc != ReturnCode.Success)
                    {
                        Debug.WriteLine("NAPS2.TW - Could not open session - {0}", rc);
                        StopTwain();
                        return;
                    }
                    ds = session.FirstOrDefault(x => x.Name == scanDevice.ID);
                    if (ds == null)
                    {
                        Debug.WriteLine("NAPS2.TW - Could not find DS - DS count = {0}", session.Count());
                        throw new DeviceNotFoundException();
                    }
                    rc = ds.Open();
                    if (rc != ReturnCode.Success)
                    {
                        Debug.WriteLine("NAPS2.TW - Could not open DS - {0}", rc);
                        StopTwain();
                        return;
                    }
                    ConfigureDS(ds, scanProfile, scanParams);
                    var ui = scanProfile.UseNativeUI ? SourceEnableMode.ShowUI : SourceEnableMode.NoUI;
                    Debug.WriteLine("NAPS2.TW - Enabling DS");
                    rc = scanParams.NoUI ? ds.Enable(ui, true, windowHandle ?? IntPtr.Zero) : ds.Enable(ui, true, twainForm.Handle);
                    Debug.WriteLine("NAPS2.TW - Enable finished");
                    if (rc != ReturnCode.Success)
                    {
                        Debug.WriteLine("NAPS2.TW - Enable failed - {0}, rc");
                        StopTwain();
                    }
                    else
                    {
                        cancelToken.Register(() =>
                        {
                            Debug.WriteLine("NAPS2.TW - User Cancel");
                            cancel = true;
                            session.ForceStepDown(5);
                        });
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("NAPS2.TW - Error");
                    error = ex;
                    StopTwain();
                }
            }

            if (!scanParams.NoUI)
            {
                twainForm.Shown  += (sender, eventArgs) => { InitTwain(); };
                twainForm.Closed += (sender, args) => waitHandle.Set();
            }

            if (scanParams.NoUI)
            {
                Debug.WriteLine("NAPS2.TW - Init with no form");
                Invoker.Current.Invoke(InitTwain);
            }
            else if (!scanParams.Modal)
            {
                Debug.WriteLine("NAPS2.TW - Init with non-modal form");
                Invoker.Current.Invoke(() => twainForm.Show(dialogParent));
            }
            else
            {
                Debug.WriteLine("NAPS2.TW - Init with modal form");
                Invoker.Current.Invoke(() => twainForm.ShowDialog(dialogParent));
            }
            waitHandle.WaitOne();
            Debug.WriteLine("NAPS2.TW - Operation complete");

            if (ds != null && session.IsSourceOpen)
            {
                Debug.WriteLine("NAPS2.TW - Closing DS");
                ds.Close();
            }
            if (session.IsDsmOpen)
            {
                Debug.WriteLine("NAPS2.TW - Closing session");
                session.Close();
            }

            if (error != null)
            {
                Debug.WriteLine("NAPS2.TW - Throwing error - {0}", error);
                if (error is ScanDriverException)
                {
                    throw error;
                }
                throw new ScanDriverUnknownException(error);
            }
        }
예제 #14
0
        protected override Task ScanInternal(ScannedImageSource.Concrete source)
        {
            if (ScanProfile.ProxyConfig == null)
            {
                throw new InvalidOperationException("ScanProfile.ProxyConfig must be specified to use ProxiedScanDriver.");
            }

            return(Task.Factory.StartNew(async() =>
            {
                try
                {
                    using (var client = clientContextFactory.Create(ScanProfile.ProxyConfig))
                    {
                        var noUi = ScanParams.NoUi;
                        var form = Invoker.Current.InvokeGet(() => noUi ? null : formFactory.Create <FScanProgress>());
                        var pageNumber = 1;
                        var sem = new Semaphore(0, int.MaxValue);

                        client.Callback.ImageCallback += (imageBytes, indexImage) =>
                        {
                            try
                            {
                                indexImage.FileName = RecoveryImage.GetNextFileName() + Path.GetExtension(indexImage.FileName);
                                var recoveryFilePath = Path.Combine(RecoveryImage.RecoveryFolder.FullName, indexImage.FileName);
                                File.WriteAllBytes(recoveryFilePath, imageBytes);
                                var image = new ScannedImage(indexImage);
                                using (var bitmap = new Bitmap(new MemoryStream(imageBytes)))
                                {
                                    scannedImageHelper.PostProcessStep2(image, bitmap, ScanProfile, ScanParams, pageNumber++, false);
                                }

                                source.Put(image);
                                if (form != null)
                                {
                                    form.PageNumber = pageNumber;
                                    Invoker.Current.SafeInvoke(() => form.RefreshStatus());
                                }
                            }
                            finally
                            {
                                sem.Release();
                            }
                        };

                        var scanTask = client.Service.Scan(ScanProfile, ScanParams).ContinueWith(t =>
                        {
                            for (var i = 0; i < t.Result; i++)
                            {
                                sem.WaitOne();
                            }
                        });

                        if (!noUi)
                        {
                            form.PageNumber = pageNumber;
                            form.AsyncTransfer = async() => await scanTask;
                            form.CancelToken.Register(client.Service.CancelScan);
                        }
                        CancelToken.Register(client.Service.CancelScan);

                        if (noUi)
                        {
                            await scanTask;
                        }
                        else if (ScanParams.Modal)
                        {
                            Invoker.Current.SafeInvoke(() => form.ShowDialog());
                        }
                        else
                        {
                            Invoker.Current.SafeInvoke(() => form.Show());
                            await scanTask;
                        }
                    }
                }
                catch (Exception e)
                {
                    Log.ErrorException("Error scanning with proxy", e);
                }
            }, TaskCreationOptions.LongRunning).Unwrap());
        }