mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-11 15:26:55 +03:00
Grpc wip with tests started
This commit is contained in:
parent
7b6273aeb2
commit
83fbf68dd1
@ -10,6 +10,7 @@ using NAPS2.Ocr;
|
|||||||
using NAPS2.Operation;
|
using NAPS2.Operation;
|
||||||
using NAPS2.Scan;
|
using NAPS2.Scan;
|
||||||
using NAPS2.Images;
|
using NAPS2.Images;
|
||||||
|
using NAPS2.ImportExport.Email.Mapi;
|
||||||
using NAPS2.Scan.Sane;
|
using NAPS2.Scan.Sane;
|
||||||
using NAPS2.Scan.Twain;
|
using NAPS2.Scan.Twain;
|
||||||
using NAPS2.Scan.Wia;
|
using NAPS2.Scan.Wia;
|
||||||
@ -36,6 +37,7 @@ namespace NAPS2.DI.Modules
|
|||||||
Bind<PdfExporter>().To<PdfSharpExporter>();
|
Bind<PdfExporter>().To<PdfSharpExporter>();
|
||||||
Bind<IScannedImagePrinter>().To<PrintDocumentPrinter>();
|
Bind<IScannedImagePrinter>().To<PrintDocumentPrinter>();
|
||||||
Bind<IEmailProviderFactory>().To<NinjectEmailProviderFactory>();
|
Bind<IEmailProviderFactory>().To<NinjectEmailProviderFactory>();
|
||||||
|
Bind<IMapiWrapper>().To<MapiWrapper>();
|
||||||
Bind<OcrEngineManager>().ToMethod(ctx => OcrEngineManager.Default);
|
Bind<OcrEngineManager>().ToMethod(ctx => OcrEngineManager.Default);
|
||||||
Bind<OcrRequestQueue>().ToSelf().InSingletonScope();
|
Bind<OcrRequestQueue>().ToSelf().InSingletonScope();
|
||||||
|
|
||||||
@ -50,6 +52,7 @@ namespace NAPS2.DI.Modules
|
|||||||
Bind<IScanDriver>().To<TwainScanDriver>().InSingletonScope().Named(TwainScanDriver.DRIVER_NAME);
|
Bind<IScanDriver>().To<TwainScanDriver>().InSingletonScope().Named(TwainScanDriver.DRIVER_NAME);
|
||||||
Bind<IScanDriver>().To<SaneScanDriver>().InSingletonScope().Named(SaneScanDriver.DRIVER_NAME);
|
Bind<IScanDriver>().To<SaneScanDriver>().InSingletonScope().Named(SaneScanDriver.DRIVER_NAME);
|
||||||
Bind<IScanDriver>().To<ProxiedScanDriver>().InSingletonScope().Named(ProxiedScanDriver.DRIVER_NAME);
|
Bind<IScanDriver>().To<ProxiedScanDriver>().InSingletonScope().Named(ProxiedScanDriver.DRIVER_NAME);
|
||||||
|
Bind<ITwainWrapper>().To<TwainWrapper>();
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
Bind<PdfSettingsContainer>().ToSelf().InSingletonScope();
|
Bind<PdfSettingsContainer>().ToSelf().InSingletonScope();
|
||||||
|
@ -41,6 +41,12 @@
|
|||||||
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Castle.Core.4.3.1\lib\net45\Castle.Core.dll</HintPath>
|
<HintPath>..\packages\Castle.Core.4.3.1\lib\net45\Castle.Core.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Grpc.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Grpc.Core.1.19.0\lib\net45\Grpc.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Grpc.Core.Api, Version=0.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Grpc.Core.Api.1.19.0\lib\net45\Grpc.Core.Api.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Moq, Version=4.10.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
<Reference Include="Moq, Version=4.10.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Moq.4.10.1\lib\net45\Moq.dll</HintPath>
|
<HintPath>..\packages\Moq.4.10.1\lib\net45\Moq.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
@ -52,6 +58,9 @@
|
|||||||
<Reference Include="System.Configuration" />
|
<Reference Include="System.Configuration" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Interactive.Async, Version=3.2.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.Interactive.Async.3.2.0\lib\net46\System.Interactive.Async.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Net" />
|
<Reference Include="System.Net" />
|
||||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||||
@ -98,6 +107,7 @@
|
|||||||
<Compile Include="Util\SliceTests.cs" />
|
<Compile Include="Util\SliceTests.cs" />
|
||||||
<Compile Include="Util\SslHelperTests.cs" />
|
<Compile Include="Util\SslHelperTests.cs" />
|
||||||
<Compile Include="Util\TestingContextTests.cs" />
|
<Compile Include="Util\TestingContextTests.cs" />
|
||||||
|
<Compile Include="Worker\WorkerChannelTests.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\NAPS2.Sdk\NAPS2.Sdk.csproj">
|
<ProjectReference Include="..\NAPS2.Sdk\NAPS2.Sdk.csproj">
|
||||||
@ -125,6 +135,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.props'))" />
|
<Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.props'))" />
|
||||||
<Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.targets'))" />
|
<Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\packages\Grpc.Core.1.19.0\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.1.19.0\build\net45\Grpc.Core.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
<Import Project="..\packages\xunit.core.2.4.1\build\xunit.core.targets" Condition="Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" />
|
<Import Project="..\packages\xunit.core.2.4.1\build\xunit.core.targets" Condition="Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" />
|
||||||
|
<Import Project="..\packages\Grpc.Core.1.19.0\build\net45\Grpc.Core.targets" Condition="Exists('..\packages\Grpc.Core.1.19.0\build\net45\Grpc.Core.targets')" />
|
||||||
</Project>
|
</Project>
|
86
NAPS2.Sdk.Tests/Worker/WorkerChannelTests.cs
Normal file
86
NAPS2.Sdk.Tests/Worker/WorkerChannelTests.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Grpc.Core;
|
||||||
|
using Moq;
|
||||||
|
using NAPS2.Images;
|
||||||
|
using NAPS2.ImportExport.Email.Mapi;
|
||||||
|
using NAPS2.Scan;
|
||||||
|
using NAPS2.Scan.Twain;
|
||||||
|
using NAPS2.Worker;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NAPS2.Sdk.Tests.Worker
|
||||||
|
{
|
||||||
|
public class WorkerChannelTests
|
||||||
|
{
|
||||||
|
private Channel Start()
|
||||||
|
{
|
||||||
|
var twainWrapper = new Mock<ITwainWrapper>();
|
||||||
|
var thumbnailRenderer = new ThumbnailRenderer();
|
||||||
|
var mapiWrapper = new Mock<IMapiWrapper>();
|
||||||
|
Server server = new Server
|
||||||
|
{
|
||||||
|
Services = { GrpcWorkerService.BindService(new GrpcWorkerServiceImpl(twainWrapper.Object, thumbnailRenderer, mapiWrapper.Object)) },
|
||||||
|
Ports = { new ServerPort("localhost", 0, ServerCredentials.Insecure) }
|
||||||
|
};
|
||||||
|
server.Start();
|
||||||
|
var client = new GrpcWorkerServiceAdapter(server.Ports.First().BoundPort);
|
||||||
|
return new Channel
|
||||||
|
{
|
||||||
|
Server = server,
|
||||||
|
Client = client,
|
||||||
|
TwainWrapper = twainWrapper,
|
||||||
|
ThumbnailRenderer = thumbnailRenderer,
|
||||||
|
MapiWrapper = mapiWrapper
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
using (var channel = Start())
|
||||||
|
{
|
||||||
|
// TODO: When init actually does something, do a better test
|
||||||
|
channel.Client.Init("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TwainGetDeviceList()
|
||||||
|
{
|
||||||
|
using (var channel = Start())
|
||||||
|
{
|
||||||
|
channel.TwainWrapper
|
||||||
|
.Setup(tw => tw.GetDeviceList(TwainImpl.OldDsm))
|
||||||
|
.Returns(new List<ScanDevice> { new ScanDevice("test_id", "test_name") });
|
||||||
|
|
||||||
|
var deviceList = channel.Client.TwainGetDeviceList(TwainImpl.OldDsm);
|
||||||
|
|
||||||
|
Assert.Single(deviceList);
|
||||||
|
Assert.Equal("test_id", deviceList[0].ID);
|
||||||
|
Assert.Equal("test_name", deviceList[0].Name);
|
||||||
|
channel.TwainWrapper.Verify(tw => tw.GetDeviceList(TwainImpl.OldDsm));
|
||||||
|
channel.TwainWrapper.VerifyNoOtherCalls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Channel : IDisposable
|
||||||
|
{
|
||||||
|
public Server Server { get; set; }
|
||||||
|
|
||||||
|
public GrpcWorkerServiceAdapter Client { get; set; }
|
||||||
|
|
||||||
|
public Mock<ITwainWrapper> TwainWrapper { get; set; }
|
||||||
|
|
||||||
|
public ThumbnailRenderer ThumbnailRenderer { get; set; }
|
||||||
|
|
||||||
|
public Mock<IMapiWrapper> MapiWrapper { get; set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Server.KillAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,10 @@
|
|||||||
<packages>
|
<packages>
|
||||||
<package id="BouncyCastle" version="1.8.4" targetFramework="net461" />
|
<package id="BouncyCastle" version="1.8.4" targetFramework="net461" />
|
||||||
<package id="Castle.Core" version="4.3.1" targetFramework="net461" />
|
<package id="Castle.Core" version="4.3.1" targetFramework="net461" />
|
||||||
|
<package id="Grpc.Core" version="1.19.0" targetFramework="net461" />
|
||||||
|
<package id="Grpc.Core.Api" version="1.19.0" targetFramework="net461" />
|
||||||
<package id="Moq" version="4.10.1" targetFramework="net461" />
|
<package id="Moq" version="4.10.1" targetFramework="net461" />
|
||||||
|
<package id="System.Interactive.Async" version="3.2.0" targetFramework="net461" />
|
||||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.0" targetFramework="net461" />
|
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.0" targetFramework="net461" />
|
||||||
<package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net461" />
|
<package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net461" />
|
||||||
<package id="xunit" version="2.4.1" targetFramework="net461" />
|
<package id="xunit" version="2.4.1" targetFramework="net461" />
|
||||||
|
@ -38,6 +38,7 @@ namespace NAPS2.Images.Storage
|
|||||||
// TODO: Better format choice?
|
// TODO: Better format choice?
|
||||||
var format = convertParams.Lossless ? ImageFormat.Png : ImageFormat.Jpeg;
|
var format = convertParams.Lossless ? ImageFormat.Png : ImageFormat.Jpeg;
|
||||||
input.Bitmap.Save(stream, format);
|
input.Bitmap.Save(stream, format);
|
||||||
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
return new MemoryStreamStorage(stream);
|
return new MemoryStreamStorage(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
NAPS2.Sdk/ImportExport/Email/Mapi/IMapiWrapper.cs
Normal file
7
NAPS2.Sdk/ImportExport/Email/Mapi/IMapiWrapper.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace NAPS2.ImportExport.Email.Mapi
|
||||||
|
{
|
||||||
|
public interface IMapiWrapper
|
||||||
|
{
|
||||||
|
MapiSendMailReturnCode SendEmail(EmailMessage message);
|
||||||
|
}
|
||||||
|
}
|
@ -14,10 +14,10 @@ namespace NAPS2.ImportExport.Email.Mapi
|
|||||||
public class MapiEmailProvider : IEmailProvider
|
public class MapiEmailProvider : IEmailProvider
|
||||||
{
|
{
|
||||||
private readonly IWorkerServiceFactory workerServiceFactory;
|
private readonly IWorkerServiceFactory workerServiceFactory;
|
||||||
private readonly MapiWrapper mapiWrapper;
|
private readonly IMapiWrapper mapiWrapper;
|
||||||
private readonly ErrorOutput errorOutput;
|
private readonly ErrorOutput errorOutput;
|
||||||
|
|
||||||
public MapiEmailProvider(IWorkerServiceFactory workerServiceFactory, MapiWrapper mapiWrapper, ErrorOutput errorOutput)
|
public MapiEmailProvider(IWorkerServiceFactory workerServiceFactory, IMapiWrapper mapiWrapper, ErrorOutput errorOutput)
|
||||||
{
|
{
|
||||||
this.workerServiceFactory = workerServiceFactory;
|
this.workerServiceFactory = workerServiceFactory;
|
||||||
this.mapiWrapper = mapiWrapper;
|
this.mapiWrapper = mapiWrapper;
|
||||||
|
@ -6,7 +6,7 @@ using NAPS2.Util;
|
|||||||
|
|
||||||
namespace NAPS2.ImportExport.Email.Mapi
|
namespace NAPS2.ImportExport.Email.Mapi
|
||||||
{
|
{
|
||||||
public class MapiWrapper
|
public class MapiWrapper : IMapiWrapper
|
||||||
{
|
{
|
||||||
private readonly SystemEmailClients systemEmailClients;
|
private readonly SystemEmailClients systemEmailClients;
|
||||||
|
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
<Compile Include="Images\ScannedImageSink.cs" />
|
<Compile Include="Images\ScannedImageSink.cs" />
|
||||||
<Compile Include="Images\Storage\PdfConverters.cs" />
|
<Compile Include="Images\Storage\PdfConverters.cs" />
|
||||||
<Compile Include="Images\Storage\PdfStorage.cs" />
|
<Compile Include="Images\Storage\PdfStorage.cs" />
|
||||||
|
<Compile Include="ImportExport\Email\Mapi\IMapiWrapper.cs" />
|
||||||
<Compile Include="ImportExport\Email\Mapi\MapiWrapper.cs" />
|
<Compile Include="ImportExport\Email\Mapi\MapiWrapper.cs" />
|
||||||
<Compile Include="ImportExport\Images\ImageSettingsProvider.cs" />
|
<Compile Include="ImportExport\Images\ImageSettingsProvider.cs" />
|
||||||
<Compile Include="ImportExport\Pdf\GhostscriptManager.cs" />
|
<Compile Include="ImportExport\Pdf\GhostscriptManager.cs" />
|
||||||
@ -255,6 +256,7 @@
|
|||||||
<Compile Include="Scan\Sane\SaneOptionParser.cs" />
|
<Compile Include="Scan\Sane\SaneOptionParser.cs" />
|
||||||
<Compile Include="Scan\Sane\SaneScanDriver.cs" />
|
<Compile Include="Scan\Sane\SaneScanDriver.cs" />
|
||||||
<Compile Include="Scan\Sane\SaneWrapper.cs" />
|
<Compile Include="Scan\Sane\SaneWrapper.cs" />
|
||||||
|
<Compile Include="Scan\Twain\ITwainWrapper.cs" />
|
||||||
<Compile Include="Scan\Wia\Native\Hresult.cs" />
|
<Compile Include="Scan\Wia\Native\Hresult.cs" />
|
||||||
<Compile Include="Scan\Wia\Native\IWiaDeviceProps.cs" />
|
<Compile Include="Scan\Wia\Native\IWiaDeviceProps.cs" />
|
||||||
<Compile Include="Scan\Wia\Native\NativeStreamWrapper.cs" />
|
<Compile Include="Scan\Wia\Native\NativeStreamWrapper.cs" />
|
||||||
@ -290,6 +292,7 @@
|
|||||||
<Compile Include="Platform\IRuntimeCompat.cs" />
|
<Compile Include="Platform\IRuntimeCompat.cs" />
|
||||||
<Compile Include="Platform\MonoRuntimeCompat.cs" />
|
<Compile Include="Platform\MonoRuntimeCompat.cs" />
|
||||||
<Compile Include="Util\ExpFallback.cs" />
|
<Compile Include="Util\ExpFallback.cs" />
|
||||||
|
<Compile Include="Worker\GrpcHelper.cs" />
|
||||||
<Compile Include="Util\IInvoker.cs" />
|
<Compile Include="Util\IInvoker.cs" />
|
||||||
<Compile Include="Util\ImageExtensions.cs" />
|
<Compile Include="Util\ImageExtensions.cs" />
|
||||||
<Compile Include="Util\Invoker.cs" />
|
<Compile Include="Util\Invoker.cs" />
|
||||||
@ -342,11 +345,7 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Worker\GrpcWorkerServiceImpl.cs" />
|
<Compile Include="Worker\GrpcWorkerServiceImpl.cs" />
|
||||||
<Compile Include="Worker\GrpcWorkerServiceAdapter.cs" />
|
<Compile Include="Worker\GrpcWorkerServiceAdapter.cs" />
|
||||||
<Compile Include="Worker\IWorkerCallback.cs" />
|
|
||||||
<Compile Include="Worker\ScanDriverExceptionDetail.cs" />
|
|
||||||
<Compile Include="Worker\WorkerCallback.cs" />
|
|
||||||
<Compile Include="Worker\WorkerContext.cs" />
|
<Compile Include="Worker\WorkerContext.cs" />
|
||||||
<Compile Include="Worker\WorkerService.cs" />
|
|
||||||
<Compile Include="Icons.Designer.cs">
|
<Compile Include="Icons.Designer.cs">
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
@ -453,7 +452,6 @@
|
|||||||
<Compile Include="Operation\IOperation.cs" />
|
<Compile Include="Operation\IOperation.cs" />
|
||||||
<Compile Include="Worker\IWorkerServiceFactory.cs" />
|
<Compile Include="Worker\IWorkerServiceFactory.cs" />
|
||||||
<Compile Include="Util\OverwritePrompt.cs" />
|
<Compile Include="Util\OverwritePrompt.cs" />
|
||||||
<Compile Include="Worker\IWorkerService.cs" />
|
|
||||||
<Compile Include="Util\ISaveNotify.cs" />
|
<Compile Include="Util\ISaveNotify.cs" />
|
||||||
<Compile Include="Util\Lifecycle.cs" />
|
<Compile Include="Util\Lifecycle.cs" />
|
||||||
<Compile Include="Logging\Log.cs" />
|
<Compile Include="Logging\Log.cs" />
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.ServiceModel;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NAPS2.Config;
|
using NAPS2.Config;
|
||||||
@ -12,7 +11,6 @@ using NAPS2.Lang.Resources;
|
|||||||
using NAPS2.Logging;
|
using NAPS2.Logging;
|
||||||
using NAPS2.Util;
|
using NAPS2.Util;
|
||||||
using NAPS2.WinForms;
|
using NAPS2.WinForms;
|
||||||
using NAPS2.Worker;
|
|
||||||
|
|
||||||
namespace NAPS2.Scan
|
namespace NAPS2.Scan
|
||||||
{
|
{
|
||||||
@ -138,10 +136,11 @@ namespace NAPS2.Scan
|
|||||||
{
|
{
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
catch (FaultException<ScanDriverExceptionDetail> e)
|
// TODO
|
||||||
{
|
//catch (FaultException<ScanDriverExceptionDetail> e)
|
||||||
error = e.Detail.Exception;
|
//{
|
||||||
}
|
// error = e.Detail.Exception;
|
||||||
|
//}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
error = new ScanDriverUnknownException(e);
|
error = new ScanDriverUnknownException(e);
|
||||||
|
16
NAPS2.Sdk/Scan/Twain/ITwainWrapper.cs
Normal file
16
NAPS2.Sdk/Scan/Twain/ITwainWrapper.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using NAPS2.Images;
|
||||||
|
|
||||||
|
namespace NAPS2.Scan.Twain
|
||||||
|
{
|
||||||
|
public interface ITwainWrapper
|
||||||
|
{
|
||||||
|
List<ScanDevice> GetDeviceList(TwainImpl twainImpl);
|
||||||
|
|
||||||
|
void Scan(IntPtr dialogParent, ScanDevice scanDevice, ScanProfile scanProfile, ScanParams scanParams,
|
||||||
|
CancellationToken cancelToken, ScannedImageSink sink, Action<ScannedImage, ScanParams, string> runBackgroundOcr);
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@ namespace NAPS2.Scan.Twain
|
|||||||
{
|
{
|
||||||
public const string DRIVER_NAME = "twain";
|
public const string DRIVER_NAME = "twain";
|
||||||
|
|
||||||
private readonly TwainWrapper twainWrapper;
|
private readonly ITwainWrapper twainWrapper;
|
||||||
private readonly ScannedImageHelper scannedImageHelper;
|
private readonly ScannedImageHelper scannedImageHelper;
|
||||||
|
|
||||||
public TwainScanDriver() : base(ErrorOutput.Default, new AutoSaver())
|
public TwainScanDriver() : base(ErrorOutput.Default, new AutoSaver())
|
||||||
@ -24,7 +24,7 @@ namespace NAPS2.Scan.Twain
|
|||||||
scannedImageHelper = new ScannedImageHelper();
|
scannedImageHelper = new ScannedImageHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TwainScanDriver(TwainWrapper twainWrapper, ScannedImageHelper scannedImageHelper, ErrorOutput errorOutput, AutoSaver autoSaver) : base(errorOutput, autoSaver)
|
public TwainScanDriver(ITwainWrapper twainWrapper, ScannedImageHelper scannedImageHelper, ErrorOutput errorOutput, AutoSaver autoSaver) : base(errorOutput, autoSaver)
|
||||||
{
|
{
|
||||||
this.twainWrapper = twainWrapper;
|
this.twainWrapper = twainWrapper;
|
||||||
this.scannedImageHelper = scannedImageHelper;
|
this.scannedImageHelper = scannedImageHelper;
|
||||||
@ -63,13 +63,11 @@ namespace NAPS2.Scan.Twain
|
|||||||
{
|
{
|
||||||
using (var worker = WorkerManager.Factory.Create())
|
using (var worker = WorkerManager.Factory.Create())
|
||||||
{
|
{
|
||||||
worker.Callback.ImageCallback += (img, tempPath) =>
|
await worker.Service.TwainScan(scanDevice, scanProfile, scanParams, dialogParent, cancelToken, (img, tempPath) =>
|
||||||
{
|
{
|
||||||
if (tempPath != null) scannedImageHelper.RunBackgroundOcr(img, scanParams, tempPath);
|
if (tempPath != null) scannedImageHelper.RunBackgroundOcr(img, scanParams, tempPath);
|
||||||
sink.PutImage(img);
|
sink.PutImage(img);
|
||||||
};
|
});
|
||||||
cancelToken.Register(worker.Service.CancelTwainScan);
|
|
||||||
await worker.Service.TwainScan(scanDevice, scanProfile, scanParams, dialogParent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -18,7 +18,7 @@ using NAPS2.Util;
|
|||||||
|
|
||||||
namespace NAPS2.Scan.Twain
|
namespace NAPS2.Scan.Twain
|
||||||
{
|
{
|
||||||
public class TwainWrapper
|
public class TwainWrapper : ITwainWrapper
|
||||||
{
|
{
|
||||||
private static readonly TWIdentity TwainAppId = TWIdentity.CreateFromAssembly(DataGroups.Image | DataGroups.Control, Assembly.GetEntryAssembly());
|
private static readonly TWIdentity TwainAppId = TWIdentity.CreateFromAssembly(DataGroups.Image | DataGroups.Control, Assembly.GetEntryAssembly());
|
||||||
|
|
||||||
|
61
NAPS2.Sdk/Worker/GrpcHelper.cs
Normal file
61
NAPS2.Sdk/Worker/GrpcHelper.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NAPS2.Util;
|
||||||
|
|
||||||
|
namespace NAPS2.Worker
|
||||||
|
{
|
||||||
|
public static class GrpcHelper
|
||||||
|
{
|
||||||
|
public static void HandleErrors(Error error)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(error?.Xml))
|
||||||
|
{
|
||||||
|
var knownExceptionTypes = new Type[] { }; // TODO
|
||||||
|
var exception = error.Xml.FromXml<Exception>();
|
||||||
|
exception.PreserveStackTrace();
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<TResponse> WrapFunc<TResponse>(Func<TResponse> action, Func<Error, TResponse> error) =>
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return action();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return error(new Error { Xml = e.ToXml() });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
public static Task<TResponse> WrapFunc<TResponse>(Func<Task<TResponse>> action, Func<Error, TResponse> error) =>
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await action();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return error(new Error { Xml = e.ToXml() });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
public static Task WrapAction(Action action, Action<Error> error) =>
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
error(new Error { Xml = e.ToXml() });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using NAPS2.Images;
|
using NAPS2.Images;
|
||||||
@ -12,38 +13,83 @@ using NAPS2.Util;
|
|||||||
|
|
||||||
namespace NAPS2.Worker
|
namespace NAPS2.Worker
|
||||||
{
|
{
|
||||||
class GrpcWorkerServiceAdapter : IWorkerService
|
public class GrpcWorkerServiceAdapter
|
||||||
{
|
{
|
||||||
private int port;
|
private GrpcWorkerService.GrpcWorkerServiceClient client;
|
||||||
|
|
||||||
public GrpcWorkerServiceAdapter(int port)
|
public GrpcWorkerServiceAdapter(int port)
|
||||||
{
|
{
|
||||||
this.port = port;
|
this.client = new GrpcWorkerService.GrpcWorkerServiceClient(new Channel("localhost", port, ChannelCredentials.Insecure));
|
||||||
|
}
|
||||||
|
public void Init(string recoveryFolderPath)
|
||||||
|
{
|
||||||
|
var req = new InitRequest { RecoveryFolderPath = recoveryFolderPath };
|
||||||
|
var resp = client.Init(req);
|
||||||
|
GrpcHelper.HandleErrors(resp.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WiaConfiguration Wia10NativeUI(string scanDevice, IntPtr hwnd) => throw new NotImplementedException();
|
public WiaConfiguration Wia10NativeUI(string scanDevice, IntPtr hwnd)
|
||||||
|
{
|
||||||
|
var req = new Wia10NativeUiRequest
|
||||||
|
{
|
||||||
|
DeviceId = scanDevice,
|
||||||
|
Hwnd = (ulong)hwnd
|
||||||
|
};
|
||||||
|
var resp = client.Wia10NativeUi(req);
|
||||||
|
GrpcHelper.HandleErrors(resp.Error);
|
||||||
|
return resp.WiaConfigurationXml.FromXml<WiaConfiguration>();
|
||||||
|
}
|
||||||
|
|
||||||
public List<ScanDevice> TwainGetDeviceList(TwainImpl twainImpl)
|
public List<ScanDevice> TwainGetDeviceList(TwainImpl twainImpl)
|
||||||
{
|
{
|
||||||
var client = new GrpcWorkerService.GrpcWorkerServiceClient(new Channel("localhost", port, ChannelCredentials.Insecure));
|
var req = new TwainGetDeviceListRequest { TwainImpl = twainImpl.ToXml() };
|
||||||
var response = client.TwainGetDeviceList(new TwainGetDeviceListRequest { TwainImpl = twainImpl.ToXml() });
|
var resp = client.TwainGetDeviceList(req);
|
||||||
return response.DeviceListXml.FromXml<List<ScanDevice>>();
|
GrpcHelper.HandleErrors(resp.Error);
|
||||||
|
return resp.DeviceListXml.FromXml<List<ScanDevice>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task TwainScan(ScanDevice scanDevice, ScanProfile scanProfile, ScanParams scanParams, IntPtr hwnd) => throw new NotImplementedException();
|
public async Task TwainScan(ScanDevice scanDevice, ScanProfile scanProfile, ScanParams scanParams, IntPtr hwnd, CancellationToken cancelToken, Action<ScannedImage, string> imageCallback)
|
||||||
|
|
||||||
public void CancelTwainScan()
|
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var req = new TwainScanRequest
|
||||||
|
{
|
||||||
|
ScanDeviceXml = scanDevice.ToXml(),
|
||||||
|
ScanProfileXml = scanProfile.ToXml(),
|
||||||
|
ScanParamsXml = scanParams.ToXml(),
|
||||||
|
Hwnd = (ulong)hwnd
|
||||||
|
};
|
||||||
|
var streamingCall = client.TwainScan(req, cancellationToken: cancelToken);
|
||||||
|
while (await streamingCall.ResponseStream.MoveNext())
|
||||||
|
{
|
||||||
|
var resp = streamingCall.ResponseStream.Current;
|
||||||
|
GrpcHelper.HandleErrors(resp.Error);
|
||||||
|
// TODO
|
||||||
|
//var scannedImage = new ScannedImage(image);
|
||||||
|
//if (thumbnail != null)
|
||||||
|
//{
|
||||||
|
// scannedImage.SetThumbnail(StorageManager.MemoryStorageFactory.Decode(new MemoryStream(thumbnail), ".bmp"));
|
||||||
|
//}
|
||||||
|
//ImageCallback?.Invoke(scannedImage, tempImageFilePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MapiSendMailReturnCode SendMapiEmail(EmailMessage message) => throw new NotImplementedException();
|
public MapiSendMailReturnCode SendMapiEmail(EmailMessage message)
|
||||||
|
|
||||||
public byte[] RenderThumbnail(ScannedImage.Snapshot snapshot, int size) => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public void Init(string recoveryFolderPath)
|
|
||||||
{
|
{
|
||||||
//throw new NotImplementedException();
|
var req = new SendMapiEmailRequest { EmailMessageXml = message.ToXml() };
|
||||||
|
var resp = client.SendMapiEmail(req);
|
||||||
|
GrpcHelper.HandleErrors(resp.Error);
|
||||||
|
return resp.ReturnCodeXml.FromXml<MapiSendMailReturnCode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] RenderThumbnail(ScannedImage.Snapshot snapshot, int size)
|
||||||
|
{
|
||||||
|
var req = new RenderThumbnailRequest
|
||||||
|
{
|
||||||
|
SnapshotXml = snapshot.ToXml(),
|
||||||
|
Size = size
|
||||||
|
};
|
||||||
|
var resp = client.RenderThumbnail(req);
|
||||||
|
GrpcHelper.HandleErrors(resp.Error);
|
||||||
|
return resp.Thumbnail.ToByteArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,40 +1,144 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Google.Protobuf;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using NAPS2.Images;
|
using NAPS2.Images;
|
||||||
|
using NAPS2.Images.Storage;
|
||||||
using NAPS2.ImportExport.Email;
|
using NAPS2.ImportExport.Email;
|
||||||
using NAPS2.ImportExport.Email.Mapi;
|
using NAPS2.ImportExport.Email.Mapi;
|
||||||
using NAPS2.Scan;
|
using NAPS2.Scan;
|
||||||
using NAPS2.Scan.Twain;
|
using NAPS2.Scan.Twain;
|
||||||
using NAPS2.Scan.Wia;
|
using NAPS2.Scan.Wia;
|
||||||
|
using NAPS2.Scan.Wia.Native;
|
||||||
using NAPS2.Util;
|
using NAPS2.Util;
|
||||||
|
|
||||||
namespace NAPS2.Worker
|
namespace NAPS2.Worker
|
||||||
{
|
{
|
||||||
public class GrpcWorkerServiceImpl : GrpcWorkerService.GrpcWorkerServiceBase
|
public class GrpcWorkerServiceImpl : GrpcWorkerService.GrpcWorkerServiceBase
|
||||||
{
|
{
|
||||||
private readonly TwainWrapper twainWrapper;
|
private readonly ITwainWrapper twainWrapper;
|
||||||
private readonly ThumbnailRenderer thumbnailRenderer;
|
private readonly ThumbnailRenderer thumbnailRenderer;
|
||||||
private readonly MapiWrapper mapiWrapper;
|
private readonly IMapiWrapper mapiWrapper;
|
||||||
|
|
||||||
private CancellationTokenSource twainScanCts = new CancellationTokenSource();
|
private CancellationTokenSource twainScanCts = new CancellationTokenSource();
|
||||||
|
|
||||||
public GrpcWorkerServiceImpl(TwainWrapper twainWrapper, ThumbnailRenderer thumbnailRenderer, MapiWrapper mapiWrapper)
|
public GrpcWorkerServiceImpl(ITwainWrapper twainWrapper, ThumbnailRenderer thumbnailRenderer, IMapiWrapper mapiWrapper)
|
||||||
{
|
{
|
||||||
this.twainWrapper = twainWrapper;
|
this.twainWrapper = twainWrapper;
|
||||||
this.thumbnailRenderer = thumbnailRenderer;
|
this.thumbnailRenderer = thumbnailRenderer;
|
||||||
this.mapiWrapper = mapiWrapper;
|
this.mapiWrapper = mapiWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<TwainGetDeviceListResponse> TwainGetDeviceList(TwainGetDeviceListRequest request, ServerCallContext context)
|
public override Task<InitResponse> Init(InitRequest request, ServerCallContext context) =>
|
||||||
|
GrpcHelper.WrapFunc(
|
||||||
|
() => new InitResponse(),
|
||||||
|
err => new InitResponse { Error = err });
|
||||||
|
|
||||||
|
public override Task<Wia10NativeUiResponse> Wia10NativeUi(Wia10NativeUiRequest request, ServerCallContext context) =>
|
||||||
|
GrpcHelper.WrapFunc(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var deviceManager = new WiaDeviceManager(WiaVersion.Wia10))
|
||||||
|
using (var device = deviceManager.FindDevice(request.DeviceId))
|
||||||
|
{
|
||||||
|
var item = device.PromptToConfigure((IntPtr)request.Hwnd);
|
||||||
|
return new Wia10NativeUiResponse
|
||||||
|
{
|
||||||
|
WiaConfigurationXml = new WiaConfiguration
|
||||||
|
{
|
||||||
|
DeviceProps = device.Properties.SerializeEditable(),
|
||||||
|
ItemProps = item.Properties.SerializeEditable(),
|
||||||
|
ItemName = item.Name()
|
||||||
|
}.ToXml()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (WiaException e)
|
||||||
|
{
|
||||||
|
WiaScanErrors.ThrowDeviceError(e);
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
err => new Wia10NativeUiResponse { Error = err });
|
||||||
|
|
||||||
|
public override Task<TwainGetDeviceListResponse> TwainGetDeviceList(TwainGetDeviceListRequest request, ServerCallContext context) =>
|
||||||
|
GrpcHelper.WrapFunc(
|
||||||
|
() => new TwainGetDeviceListResponse
|
||||||
|
{
|
||||||
|
DeviceListXml = twainWrapper.GetDeviceList(request.TwainImpl.FromXml<TwainImpl>()).ToXml()
|
||||||
|
},
|
||||||
|
err => new TwainGetDeviceListResponse { Error = err });
|
||||||
|
|
||||||
|
public override Task TwainScan(TwainScanRequest request, IServerStreamWriter<TwainScanResponse> responseStream, ServerCallContext context) =>
|
||||||
|
GrpcHelper.WrapAction(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
var imagePathDict = new Dictionary<ScannedImage, string>();
|
||||||
|
twainWrapper.Scan(
|
||||||
|
(IntPtr)request.Hwnd,
|
||||||
|
request.ScanDeviceXml.FromXml<ScanDevice>(),
|
||||||
|
request.ScanProfileXml.FromXml<ScanProfile>(),
|
||||||
|
request.ScanParamsXml.FromXml<ScanParams>(),
|
||||||
|
context.CancellationToken,
|
||||||
|
new WorkerImageSink(responseStream, imagePathDict),
|
||||||
|
(img, _, path) => imagePathDict.Add(img, path));
|
||||||
|
}, err => responseStream.WriteAsync(new TwainScanResponse { Error = err }));
|
||||||
|
|
||||||
|
public override Task<SendMapiEmailResponse> SendMapiEmail(SendMapiEmailRequest request, ServerCallContext context) =>
|
||||||
|
GrpcHelper.WrapFunc(
|
||||||
|
() => new SendMapiEmailResponse
|
||||||
|
{
|
||||||
|
ReturnCodeXml = mapiWrapper.SendEmail(request.EmailMessageXml.FromXml<EmailMessage>()).ToXml()
|
||||||
|
},
|
||||||
|
err => new SendMapiEmailResponse { Error = err });
|
||||||
|
|
||||||
|
public override Task<RenderThumbnailResponse> RenderThumbnail(RenderThumbnailRequest request, ServerCallContext context) =>
|
||||||
|
GrpcHelper.WrapFunc(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
var thumbnail = await thumbnailRenderer.Render(request.SnapshotXml.FromXml<ScannedImage.Snapshot>(), request.Size);
|
||||||
|
var stream = StorageManager.Convert<MemoryStreamStorage>(thumbnail, new StorageConvertParams { Lossless = true }).Stream;
|
||||||
|
return new RenderThumbnailResponse
|
||||||
|
{
|
||||||
|
Thumbnail = ByteString.FromStream(stream)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
err => new RenderThumbnailResponse { Error = err });
|
||||||
|
|
||||||
|
private class WorkerImageSink : ScannedImageSink
|
||||||
{
|
{
|
||||||
return await Task.Run(() => new TwainGetDeviceListResponse
|
private readonly IServerStreamWriter<TwainScanResponse> callback;
|
||||||
|
private readonly Dictionary<ScannedImage, string> imagePathDict;
|
||||||
|
|
||||||
|
public WorkerImageSink(IServerStreamWriter<TwainScanResponse> callback, Dictionary<ScannedImage, string> imagePathDict)
|
||||||
{
|
{
|
||||||
DeviceListXml = twainWrapper.GetDeviceList(request.TwainImpl.FromXml<TwainImpl>()).ToXml()
|
this.callback = callback;
|
||||||
});
|
this.imagePathDict = imagePathDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PutImage(ScannedImage image)
|
||||||
|
{
|
||||||
|
// TODO: Ideally this shouldn't be inheriting ScannedImageSink, some other cleaner mechanism
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
MemoryStream stream = null;
|
||||||
|
var thumb = image.GetThumbnail();
|
||||||
|
if (thumb != null)
|
||||||
|
{
|
||||||
|
stream = StorageManager.Convert<MemoryStreamStorage>(thumb, new StorageConvertParams { Lossless = true }).Stream;
|
||||||
|
}
|
||||||
|
//callback.WriteAsync(new TwainScanResponse
|
||||||
|
//{
|
||||||
|
// RecoveryIndexImageXml = image.
|
||||||
|
//});
|
||||||
|
//callback.TwainImageReceived(image.RecoveryIndexImage, stream?.ToArray(), imagePathDict.Get(image));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,18 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.ServiceModel;
|
|
||||||
using NAPS2.Recovery;
|
|
||||||
using NAPS2.Images;
|
|
||||||
|
|
||||||
namespace NAPS2.Worker
|
|
||||||
{
|
|
||||||
[ServiceContract]
|
|
||||||
public interface IWorkerCallback
|
|
||||||
{
|
|
||||||
[OperationContract(IsOneWay = true)]
|
|
||||||
void TwainImageReceived(RecoveryIndexImage image, byte[] thumbnail, string tempImageFilePath);
|
|
||||||
|
|
||||||
event Action<ScannedImage, string> ImageCallback;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.ServiceModel;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using NAPS2.ImportExport.Email;
|
|
||||||
using NAPS2.ImportExport.Email.Mapi;
|
|
||||||
using NAPS2.Scan;
|
|
||||||
using NAPS2.Images;
|
|
||||||
using NAPS2.Scan.Wia;
|
|
||||||
|
|
||||||
namespace NAPS2.Worker
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The WCF service interface for NAPS2.Worker.exe.
|
|
||||||
/// </summary>
|
|
||||||
[ServiceContract(CallbackContract = typeof(IWorkerCallback))]
|
|
||||||
public interface IWorkerService
|
|
||||||
{
|
|
||||||
[OperationContract]
|
|
||||||
void Init(string recoveryFolderPath);
|
|
||||||
|
|
||||||
[FaultContract(typeof(ScanDriverExceptionDetail))]
|
|
||||||
[OperationContract]
|
|
||||||
WiaConfiguration Wia10NativeUI(string scanDevice, IntPtr hwnd);
|
|
||||||
|
|
||||||
[OperationContract]
|
|
||||||
List<ScanDevice> TwainGetDeviceList(TwainImpl twainImpl);
|
|
||||||
|
|
||||||
[FaultContract(typeof(ScanDriverExceptionDetail))]
|
|
||||||
[OperationContract]
|
|
||||||
Task TwainScan(ScanDevice scanDevice, ScanProfile scanProfile, ScanParams scanParams, IntPtr hwnd);
|
|
||||||
|
|
||||||
[OperationContract]
|
|
||||||
void CancelTwainScan();
|
|
||||||
|
|
||||||
[OperationContract]
|
|
||||||
MapiSendMailReturnCode SendMapiEmail(EmailMessage message);
|
|
||||||
|
|
||||||
[OperationContract]
|
|
||||||
byte[] RenderThumbnail(ScannedImage.Snapshot snapshot, int size);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using NAPS2.Scan.Exceptions;
|
|
||||||
using NAPS2.Util;
|
|
||||||
|
|
||||||
namespace NAPS2.Worker
|
|
||||||
{
|
|
||||||
public class ScanDriverExceptionDetail
|
|
||||||
{
|
|
||||||
public ScanDriverExceptionDetail()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScanDriverExceptionDetail(ScanDriverException e)
|
|
||||||
{
|
|
||||||
var stream = new MemoryStream();
|
|
||||||
new NetDataContractSerializer().Serialize(stream, e);
|
|
||||||
SerializedException = stream.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] SerializedException { get; set; }
|
|
||||||
|
|
||||||
public ScanDriverException Exception => (ScanDriverException)new NetDataContractSerializer().Deserialize(new MemoryStream(SerializedException));
|
|
||||||
|
|
||||||
public void Throw()
|
|
||||||
{
|
|
||||||
var exception = Exception;
|
|
||||||
exception.PreserveStackTrace();
|
|
||||||
throw exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NAPS2.Recovery;
|
|
||||||
using NAPS2.Images;
|
|
||||||
|
|
||||||
namespace NAPS2.Worker
|
|
||||||
{
|
|
||||||
public class WorkerCallback : IWorkerCallback
|
|
||||||
{
|
|
||||||
public event Action<ScannedImage, string> ImageCallback;
|
|
||||||
|
|
||||||
public void TwainImageReceived(RecoveryIndexImage image, byte[] thumbnail, string tempImageFilePath)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
//var scannedImage = new ScannedImage(image);
|
|
||||||
//if (thumbnail != null)
|
|
||||||
//{
|
|
||||||
// scannedImage.SetThumbnail(StorageManager.MemoryStorageFactory.Decode(new MemoryStream(thumbnail), ".bmp"));
|
|
||||||
//}
|
|
||||||
//ImageCallback?.Invoke(scannedImage, tempImageFilePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,25 +12,12 @@ namespace NAPS2.Worker
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class WorkerContext : IDisposable
|
public class WorkerContext : IDisposable
|
||||||
{
|
{
|
||||||
public IWorkerService Service { get; set; }
|
public GrpcWorkerServiceAdapter Service { get; set; }
|
||||||
|
|
||||||
public IWorkerCallback Callback { get; set; }
|
|
||||||
|
|
||||||
public Process Process { get; set; }
|
public Process Process { get; set; }
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
((IDisposable) Service)?.Dispose();
|
|
||||||
}
|
|
||||||
catch (CommunicationObjectFaultedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.ErrorException("Error cleaning up worker", e);
|
|
||||||
}
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Process.Kill();
|
Process.Kill();
|
||||||
|
@ -5,7 +5,6 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.ServiceModel;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NAPS2.Platform;
|
using NAPS2.Platform;
|
||||||
|
|
||||||
@ -16,7 +15,6 @@ namespace NAPS2.Worker
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class WorkerManager
|
public static class WorkerManager
|
||||||
{
|
{
|
||||||
public const string PIPE_NAME_FORMAT = "net.pipe://localhost/NAPS2.Worker/{0}";
|
|
||||||
public const string WORKER_EXE_NAME = "NAPS2.Worker.exe";
|
public const string WORKER_EXE_NAME = "NAPS2.Worker.exe";
|
||||||
public static readonly string[] SearchDirs =
|
public static readonly string[] SearchDirs =
|
||||||
{
|
{
|
||||||
@ -86,18 +84,7 @@ namespace NAPS2.Worker
|
|||||||
Task.Factory.StartNew(() =>
|
Task.Factory.StartNew(() =>
|
||||||
{
|
{
|
||||||
var (proc, port) = StartWorkerProcess();
|
var (proc, port) = StartWorkerProcess();
|
||||||
//var pipeName = string.Format(PIPE_NAME_FORMAT, proc.Id);
|
_workerQueue.Add(new WorkerContext { Service = new GrpcWorkerServiceAdapter(port), Process = proc });
|
||||||
//var callback = new WorkerCallback();
|
|
||||||
//var instanceContext = new InstanceContext(callback);
|
|
||||||
//var channelFactory = new DuplexChannelFactory<IWorkerService>(instanceContext,
|
|
||||||
// new NetNamedPipeBinding
|
|
||||||
// {
|
|
||||||
// SendTimeout = TimeSpan.FromHours(24),
|
|
||||||
// MaxReceivedMessageSize = int.MaxValue
|
|
||||||
// },
|
|
||||||
// new EndpointAddress(pipeName));
|
|
||||||
//var channel = channelFactory.CreateChannel();
|
|
||||||
_workerQueue.Add(new WorkerContext { Service = new GrpcWorkerServiceAdapter(port), Callback = null, Process = proc });
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,153 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.ServiceModel;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using NAPS2.ImportExport.Email;
|
|
||||||
using NAPS2.ImportExport.Email.Mapi;
|
|
||||||
using NAPS2.Scan;
|
|
||||||
using NAPS2.Scan.Exceptions;
|
|
||||||
using NAPS2.Images;
|
|
||||||
using NAPS2.Scan.Twain;
|
|
||||||
using NAPS2.Scan.Wia;
|
|
||||||
using NAPS2.Scan.Wia.Native;
|
|
||||||
|
|
||||||
namespace NAPS2.Worker
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The WCF service implementation for NAPS2.Worker.exe.
|
|
||||||
/// </summary>
|
|
||||||
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
|
|
||||||
IncludeExceptionDetailInFaults = true,
|
|
||||||
ConcurrencyMode = ConcurrencyMode.Multiple)]
|
|
||||||
public class WorkerService : IWorkerService
|
|
||||||
{
|
|
||||||
private readonly TwainWrapper twainWrapper;
|
|
||||||
private readonly ThumbnailRenderer thumbnailRenderer;
|
|
||||||
private readonly MapiWrapper mapiWrapper;
|
|
||||||
|
|
||||||
private CancellationTokenSource twainScanCts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
public WorkerService(TwainWrapper twainWrapper, ThumbnailRenderer thumbnailRenderer, MapiWrapper mapiWrapper)
|
|
||||||
{
|
|
||||||
this.twainWrapper = twainWrapper;
|
|
||||||
this.thumbnailRenderer = thumbnailRenderer;
|
|
||||||
this.mapiWrapper = mapiWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Init(string recoveryFolderPath)
|
|
||||||
{
|
|
||||||
Callback = OperationContext.Current.GetCallbackChannel<IWorkerCallback>();
|
|
||||||
// TODO
|
|
||||||
//RecoveryImage.RecoveryFolder = new DirectoryInfo(recoveryFolderPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WiaConfiguration Wia10NativeUI(string deviceId, IntPtr hwnd)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var deviceManager = new WiaDeviceManager(WiaVersion.Wia10))
|
|
||||||
using (var device = deviceManager.FindDevice(deviceId))
|
|
||||||
{
|
|
||||||
var item = device.PromptToConfigure(hwnd);
|
|
||||||
return new WiaConfiguration
|
|
||||||
{
|
|
||||||
DeviceProps = device.Properties.SerializeEditable(),
|
|
||||||
ItemProps = item.Properties.SerializeEditable(),
|
|
||||||
ItemName = item.Name()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (WiaException e)
|
|
||||||
{
|
|
||||||
WiaScanErrors.ThrowDeviceError(e);
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ScanDriverException e)
|
|
||||||
{
|
|
||||||
throw new FaultException<ScanDriverExceptionDetail>(new ScanDriverExceptionDetail(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ScanDevice> TwainGetDeviceList(TwainImpl twainImpl)
|
|
||||||
{
|
|
||||||
return twainWrapper.GetDeviceList(twainImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task TwainScan(ScanDevice scanDevice, ScanProfile scanProfile, ScanParams scanParams, IntPtr hwnd)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Task.Factory.StartNew(() =>
|
|
||||||
{
|
|
||||||
var imagePathDict = new Dictionary<ScannedImage, string>();
|
|
||||||
twainWrapper.Scan(hwnd, scanDevice, scanProfile, scanParams, twainScanCts.Token,
|
|
||||||
new WorkerImageSink(Callback, imagePathDict), (img, _, path) => imagePathDict.Add(img, path));
|
|
||||||
}, TaskCreationOptions.LongRunning);
|
|
||||||
}
|
|
||||||
catch (ScanDriverException e)
|
|
||||||
{
|
|
||||||
throw new FaultException<ScanDriverExceptionDetail>(new ScanDriverExceptionDetail(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CancelTwainScan()
|
|
||||||
{
|
|
||||||
twainScanCts.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapiSendMailReturnCode SendMapiEmail(EmailMessage message)
|
|
||||||
{
|
|
||||||
return mapiWrapper.SendEmail(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] RenderThumbnail(ScannedImage.Snapshot snapshot, int size)
|
|
||||||
{
|
|
||||||
var stream = new MemoryStream();
|
|
||||||
// TODO
|
|
||||||
//using (var bitmap = Task.Factory.StartNew(() => thumbnailRenderer.RenderThumbnail(snapshot, size)).Unwrap().Result)
|
|
||||||
//{
|
|
||||||
// bitmap.Save(stream, ImageFormat.Png);
|
|
||||||
//}
|
|
||||||
return stream.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public IWorkerCallback Callback { get; set; }
|
|
||||||
|
|
||||||
private class WorkerImageSink : ScannedImageSink
|
|
||||||
{
|
|
||||||
private readonly IWorkerCallback callback;
|
|
||||||
private readonly Dictionary<ScannedImage, string> imagePathDict;
|
|
||||||
|
|
||||||
public WorkerImageSink(IWorkerCallback callback, Dictionary<ScannedImage, string> imagePathDict)
|
|
||||||
{
|
|
||||||
this.callback = callback;
|
|
||||||
this.imagePathDict = imagePathDict;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void PutImage(ScannedImage image)
|
|
||||||
{
|
|
||||||
// TODO: Ideally this shouldn't be inheriting ScannedImageSink, some other cleaner mechanism
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
//MemoryStream stream = null;
|
|
||||||
//var thumb = image.GetThumbnail();
|
|
||||||
//if (thumb != null)
|
|
||||||
//{
|
|
||||||
// stream = new MemoryStream();
|
|
||||||
// thumb.Save(stream, ImageFormat.Png);
|
|
||||||
//}
|
|
||||||
//callback.TwainImageReceived(image.RecoveryIndexImage, stream?.ToArray(), imagePathDict.Get(image));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,6 +6,9 @@ service GrpcWorkerService {
|
|||||||
rpc Init (InitRequest) returns (InitResponse) {}
|
rpc Init (InitRequest) returns (InitResponse) {}
|
||||||
rpc Wia10NativeUi (Wia10NativeUiRequest) returns (Wia10NativeUiResponse) {}
|
rpc Wia10NativeUi (Wia10NativeUiRequest) returns (Wia10NativeUiResponse) {}
|
||||||
rpc TwainGetDeviceList (TwainGetDeviceListRequest) returns (TwainGetDeviceListResponse) {}
|
rpc TwainGetDeviceList (TwainGetDeviceListRequest) returns (TwainGetDeviceListResponse) {}
|
||||||
|
rpc TwainScan (TwainScanRequest) returns (stream TwainScanResponse) {}
|
||||||
|
rpc SendMapiEmail (SendMapiEmailRequest) returns (SendMapiEmailResponse) {}
|
||||||
|
rpc RenderThumbnail (RenderThumbnailRequest) returns (RenderThumbnailResponse) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
message Error {
|
message Error {
|
||||||
@ -39,4 +42,35 @@ message TwainGetDeviceListResponse {
|
|||||||
string deviceListXml = 2;
|
string deviceListXml = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO more. Use streaming for scan callback/cancel.
|
message TwainScanRequest {
|
||||||
|
string scanDeviceXml = 1;
|
||||||
|
string scanProfileXml = 2;
|
||||||
|
string scanParamsXml = 3;
|
||||||
|
fixed64 hwnd = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TwainScanResponse {
|
||||||
|
Error error = 1;
|
||||||
|
string recoveryIndexImageXml = 2;
|
||||||
|
bytes thumbnail = 3;
|
||||||
|
string tempImageFilePath = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SendMapiEmailRequest {
|
||||||
|
string emailMessageXml = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SendMapiEmailResponse {
|
||||||
|
Error error = 1;
|
||||||
|
string returnCodeXml = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RenderThumbnailRequest {
|
||||||
|
string snapshotXml = 1;
|
||||||
|
int32 size = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RenderThumbnailResponse {
|
||||||
|
Error error = 1;
|
||||||
|
bytes thumbnail = 2;
|
||||||
|
}
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||||
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue"><data><IncludeFilters /><ExcludeFilters /></data></s:String>
|
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue"><data><IncludeFilters /><ExcludeFilters /></data></s:String>
|
||||||
<s:String x:Key="/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue"><data /></s:String>
|
<s:String x:Key="/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue"><data /></s:String>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Grpc/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=hwnd/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jpegs/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jpegs/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lineart/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lineart/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=mapi/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pdfs/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pdfs/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
Loading…
Reference in New Issue
Block a user