mirror of
https://github.com/cyanfish/naps2.git
synced 2024-08-16 18:50:45 +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.Scan;
|
||||
using NAPS2.Images;
|
||||
using NAPS2.ImportExport.Email.Mapi;
|
||||
using NAPS2.Scan.Sane;
|
||||
using NAPS2.Scan.Twain;
|
||||
using NAPS2.Scan.Wia;
|
||||
@ -36,6 +37,7 @@ namespace NAPS2.DI.Modules
|
||||
Bind<PdfExporter>().To<PdfSharpExporter>();
|
||||
Bind<IScannedImagePrinter>().To<PrintDocumentPrinter>();
|
||||
Bind<IEmailProviderFactory>().To<NinjectEmailProviderFactory>();
|
||||
Bind<IMapiWrapper>().To<MapiWrapper>();
|
||||
Bind<OcrEngineManager>().ToMethod(ctx => OcrEngineManager.Default);
|
||||
Bind<OcrRequestQueue>().ToSelf().InSingletonScope();
|
||||
|
||||
@ -50,6 +52,7 @@ namespace NAPS2.DI.Modules
|
||||
Bind<IScanDriver>().To<TwainScanDriver>().InSingletonScope().Named(TwainScanDriver.DRIVER_NAME);
|
||||
Bind<IScanDriver>().To<SaneScanDriver>().InSingletonScope().Named(SaneScanDriver.DRIVER_NAME);
|
||||
Bind<IScanDriver>().To<ProxiedScanDriver>().InSingletonScope().Named(ProxiedScanDriver.DRIVER_NAME);
|
||||
Bind<ITwainWrapper>().To<TwainWrapper>();
|
||||
|
||||
// Config
|
||||
Bind<PdfSettingsContainer>().ToSelf().InSingletonScope();
|
||||
|
@ -41,6 +41,12 @@
|
||||
<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>
|
||||
</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">
|
||||
<HintPath>..\packages\Moq.4.10.1\lib\net45\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
@ -52,6 +58,9 @@
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<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.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>
|
||||
@ -98,6 +107,7 @@
|
||||
<Compile Include="Util\SliceTests.cs" />
|
||||
<Compile Include="Util\SslHelperTests.cs" />
|
||||
<Compile Include="Util\TestingContextTests.cs" />
|
||||
<Compile Include="Worker\WorkerChannelTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NAPS2.Sdk\NAPS2.Sdk.csproj">
|
||||
@ -125,6 +135,8 @@
|
||||
</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.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>
|
||||
<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>
|
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>
|
||||
<package id="BouncyCastle" version="1.8.4" 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="System.Interactive.Async" version="3.2.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="xunit" version="2.4.1" targetFramework="net461" />
|
||||
|
@ -38,6 +38,7 @@ namespace NAPS2.Images.Storage
|
||||
// TODO: Better format choice?
|
||||
var format = convertParams.Lossless ? ImageFormat.Png : ImageFormat.Jpeg;
|
||||
input.Bitmap.Save(stream, format);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
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
|
||||
{
|
||||
private readonly IWorkerServiceFactory workerServiceFactory;
|
||||
private readonly MapiWrapper mapiWrapper;
|
||||
private readonly IMapiWrapper mapiWrapper;
|
||||
private readonly ErrorOutput errorOutput;
|
||||
|
||||
public MapiEmailProvider(IWorkerServiceFactory workerServiceFactory, MapiWrapper mapiWrapper, ErrorOutput errorOutput)
|
||||
public MapiEmailProvider(IWorkerServiceFactory workerServiceFactory, IMapiWrapper mapiWrapper, ErrorOutput errorOutput)
|
||||
{
|
||||
this.workerServiceFactory = workerServiceFactory;
|
||||
this.mapiWrapper = mapiWrapper;
|
||||
|
@ -6,7 +6,7 @@ using NAPS2.Util;
|
||||
|
||||
namespace NAPS2.ImportExport.Email.Mapi
|
||||
{
|
||||
public class MapiWrapper
|
||||
public class MapiWrapper : IMapiWrapper
|
||||
{
|
||||
private readonly SystemEmailClients systemEmailClients;
|
||||
|
||||
|
@ -188,6 +188,7 @@
|
||||
<Compile Include="Images\ScannedImageSink.cs" />
|
||||
<Compile Include="Images\Storage\PdfConverters.cs" />
|
||||
<Compile Include="Images\Storage\PdfStorage.cs" />
|
||||
<Compile Include="ImportExport\Email\Mapi\IMapiWrapper.cs" />
|
||||
<Compile Include="ImportExport\Email\Mapi\MapiWrapper.cs" />
|
||||
<Compile Include="ImportExport\Images\ImageSettingsProvider.cs" />
|
||||
<Compile Include="ImportExport\Pdf\GhostscriptManager.cs" />
|
||||
@ -255,6 +256,7 @@
|
||||
<Compile Include="Scan\Sane\SaneOptionParser.cs" />
|
||||
<Compile Include="Scan\Sane\SaneScanDriver.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\IWiaDeviceProps.cs" />
|
||||
<Compile Include="Scan\Wia\Native\NativeStreamWrapper.cs" />
|
||||
@ -290,6 +292,7 @@
|
||||
<Compile Include="Platform\IRuntimeCompat.cs" />
|
||||
<Compile Include="Platform\MonoRuntimeCompat.cs" />
|
||||
<Compile Include="Util\ExpFallback.cs" />
|
||||
<Compile Include="Worker\GrpcHelper.cs" />
|
||||
<Compile Include="Util\IInvoker.cs" />
|
||||
<Compile Include="Util\ImageExtensions.cs" />
|
||||
<Compile Include="Util\Invoker.cs" />
|
||||
@ -342,11 +345,7 @@
|
||||
</Compile>
|
||||
<Compile Include="Worker\GrpcWorkerServiceImpl.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\WorkerService.cs" />
|
||||
<Compile Include="Icons.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
@ -453,7 +452,6 @@
|
||||
<Compile Include="Operation\IOperation.cs" />
|
||||
<Compile Include="Worker\IWorkerServiceFactory.cs" />
|
||||
<Compile Include="Util\OverwritePrompt.cs" />
|
||||
<Compile Include="Worker\IWorkerService.cs" />
|
||||
<Compile Include="Util\ISaveNotify.cs" />
|
||||
<Compile Include="Util\Lifecycle.cs" />
|
||||
<Compile Include="Logging\Log.cs" />
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.ServiceModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NAPS2.Config;
|
||||
@ -12,7 +11,6 @@ using NAPS2.Lang.Resources;
|
||||
using NAPS2.Logging;
|
||||
using NAPS2.Util;
|
||||
using NAPS2.WinForms;
|
||||
using NAPS2.Worker;
|
||||
|
||||
namespace NAPS2.Scan
|
||||
{
|
||||
@ -138,10 +136,11 @@ namespace NAPS2.Scan
|
||||
{
|
||||
error = e;
|
||||
}
|
||||
catch (FaultException<ScanDriverExceptionDetail> e)
|
||||
{
|
||||
error = e.Detail.Exception;
|
||||
}
|
||||
// TODO
|
||||
//catch (FaultException<ScanDriverExceptionDetail> e)
|
||||
//{
|
||||
// error = e.Detail.Exception;
|
||||
//}
|
||||
catch (Exception 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";
|
||||
|
||||
private readonly TwainWrapper twainWrapper;
|
||||
private readonly ITwainWrapper twainWrapper;
|
||||
private readonly ScannedImageHelper scannedImageHelper;
|
||||
|
||||
public TwainScanDriver() : base(ErrorOutput.Default, new AutoSaver())
|
||||
@ -24,7 +24,7 @@ namespace NAPS2.Scan.Twain
|
||||
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.scannedImageHelper = scannedImageHelper;
|
||||
@ -63,13 +63,11 @@ namespace NAPS2.Scan.Twain
|
||||
{
|
||||
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);
|
||||
sink.PutImage(img);
|
||||
};
|
||||
cancelToken.Register(worker.Service.CancelTwainScan);
|
||||
await worker.Service.TwainScan(scanDevice, scanProfile, scanParams, dialogParent);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -18,7 +18,7 @@ using NAPS2.Util;
|
||||
|
||||
namespace NAPS2.Scan.Twain
|
||||
{
|
||||
public class TwainWrapper
|
||||
public class TwainWrapper : ITwainWrapper
|
||||
{
|
||||
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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Grpc.Core;
|
||||
using NAPS2.Images;
|
||||
@ -12,38 +13,83 @@ using NAPS2.Util;
|
||||
|
||||
namespace NAPS2.Worker
|
||||
{
|
||||
class GrpcWorkerServiceAdapter : IWorkerService
|
||||
public class GrpcWorkerServiceAdapter
|
||||
{
|
||||
private int port;
|
||||
private GrpcWorkerService.GrpcWorkerServiceClient client;
|
||||
|
||||
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)
|
||||
{
|
||||
var client = new GrpcWorkerService.GrpcWorkerServiceClient(new Channel("localhost", port, ChannelCredentials.Insecure));
|
||||
var response = client.TwainGetDeviceList(new TwainGetDeviceListRequest { TwainImpl = twainImpl.ToXml() });
|
||||
return response.DeviceListXml.FromXml<List<ScanDevice>>();
|
||||
var req = new TwainGetDeviceListRequest { TwainImpl = twainImpl.ToXml() };
|
||||
var resp = client.TwainGetDeviceList(req);
|
||||
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 void CancelTwainScan()
|
||||
public async Task TwainScan(ScanDevice scanDevice, ScanProfile scanProfile, ScanParams scanParams, IntPtr hwnd, CancellationToken cancelToken, Action<ScannedImage, string> imageCallback)
|
||||
{
|
||||
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 byte[] RenderThumbnail(ScannedImage.Snapshot snapshot, int size) => throw new NotImplementedException();
|
||||
|
||||
public void Init(string recoveryFolderPath)
|
||||
public MapiSendMailReturnCode SendMapiEmail(EmailMessage message)
|
||||
{
|
||||
//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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Core;
|
||||
using NAPS2.Images;
|
||||
using NAPS2.Images.Storage;
|
||||
using NAPS2.ImportExport.Email;
|
||||
using NAPS2.ImportExport.Email.Mapi;
|
||||
using NAPS2.Scan;
|
||||
using NAPS2.Scan.Twain;
|
||||
using NAPS2.Scan.Wia;
|
||||
using NAPS2.Scan.Wia.Native;
|
||||
using NAPS2.Util;
|
||||
|
||||
namespace NAPS2.Worker
|
||||
{
|
||||
public class GrpcWorkerServiceImpl : GrpcWorkerService.GrpcWorkerServiceBase
|
||||
{
|
||||
private readonly TwainWrapper twainWrapper;
|
||||
private readonly ITwainWrapper twainWrapper;
|
||||
private readonly ThumbnailRenderer thumbnailRenderer;
|
||||
private readonly MapiWrapper mapiWrapper;
|
||||
private readonly IMapiWrapper mapiWrapper;
|
||||
|
||||
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.thumbnailRenderer = thumbnailRenderer;
|
||||
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>
|
||||
public class WorkerContext : IDisposable
|
||||
{
|
||||
public IWorkerService Service { get; set; }
|
||||
|
||||
public IWorkerCallback Callback { get; set; }
|
||||
public GrpcWorkerServiceAdapter Service { get; set; }
|
||||
|
||||
public Process Process { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
((IDisposable) Service)?.Dispose();
|
||||
}
|
||||
catch (CommunicationObjectFaultedException)
|
||||
{
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.ErrorException("Error cleaning up worker", e);
|
||||
}
|
||||
try
|
||||
{
|
||||
Process.Kill();
|
||||
|
@ -5,7 +5,6 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.ServiceModel;
|
||||
using System.Threading.Tasks;
|
||||
using NAPS2.Platform;
|
||||
|
||||
@ -16,7 +15,6 @@ namespace NAPS2.Worker
|
||||
/// </summary>
|
||||
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 static readonly string[] SearchDirs =
|
||||
{
|
||||
@ -86,18 +84,7 @@ namespace NAPS2.Worker
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
var (proc, port) = StartWorkerProcess();
|
||||
//var pipeName = string.Format(PIPE_NAME_FORMAT, proc.Id);
|
||||
//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 });
|
||||
_workerQueue.Add(new WorkerContext { Service = new GrpcWorkerServiceAdapter(port), 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 Wia10NativeUi (Wia10NativeUiRequest) returns (Wia10NativeUiResponse) {}
|
||||
rpc TwainGetDeviceList (TwainGetDeviceListRequest) returns (TwainGetDeviceListResponse) {}
|
||||
rpc TwainScan (TwainScanRequest) returns (stream TwainScanResponse) {}
|
||||
rpc SendMapiEmail (SendMapiEmailRequest) returns (SendMapiEmailResponse) {}
|
||||
rpc RenderThumbnail (RenderThumbnailRequest) returns (RenderThumbnailResponse) {}
|
||||
}
|
||||
|
||||
message Error {
|
||||
@ -39,4 +42,35 @@ message TwainGetDeviceListResponse {
|
||||
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/FilterSettingsManager/CoverageFilterXml/@EntryValue"><data><IncludeFilters /><ExcludeFilters /></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/=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>
|
Loading…
Reference in New Issue
Block a user