Implemented VirtualPrinterDriver project

This commit is contained in:
Marco Batzinger 2020-10-19 17:44:50 +02:00
parent f29c84821b
commit 5c87967c3f
125 changed files with 8191 additions and 0 deletions

363
.gitignore vendored Normal file
View file

@ -0,0 +1,363 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
.idea/

View file

@ -0,0 +1,41 @@
using System;
using Autofac;
using VirtualPrinter.Agent.Autofac;
using VirtualPrinter.Agent.Core;
using VirtualPrinter.Logging;
using VirtualPrinter.ProgressInfo.Autofac;
namespace VirtualPrinter.Agent.Console
{
internal static class Program
{
// ReSharper disable once UnusedParameter.Local
// Start the console application to debug through the solution
private static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterModule(new ProgressInfoModule());
builder.RegisterModule(new VirtualPrinterModule());
builder.RegisterModule(new LoggerModule());
var container = builder.Build();
var service = container.Resolve<IVirtualPrinterService>();
service.Start();
System.Console.WriteLine(@"Press Ctrl + C to shutdown");
ConsoleKeyInfo key;
do
{
key = System.Console.ReadKey();
}
while (key.Key != ConsoleKey.C && key.Modifiers != ConsoleModifiers.Control);
service.Stop();
}
}
}

View file

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("VirtualPrinter.AgentConsole")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("3d3a379b-f9b8-466d-a04d-fd5ef948ff1c")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{3D3A379B-F9B8-466D-A04D-FD5EF948FF1C}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>VirtualPrinter.Agent.Console</RootNamespace>
<AssemblyName>VirtualPrinter.AgentConsole</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\VirtualPrinter.Agent.Autofac\VirtualPrinter.Agent.Autofac.csproj">
<Project>{1b2f0781-82d7-4576-b936-c6a26053d6ed}</Project>
<Name>VirtualPrinter.Agent.Autofac</Name>
</ProjectReference>
<ProjectReference Include="..\..\Common\VirtualPrinter.Agent.Core\VirtualPrinter.Agent.Core.csproj">
<Project>{135c85eb-2116-4cc4-8ccb-b6804b9d6467}</Project>
<Name>VirtualPrinter.Agent.Core</Name>
</ProjectReference>
<ProjectReference Include="..\..\Common\VirtualPrinter.Agent.Lib\VirtualPrinter.Agent.Lib.csproj">
<Project>{94e8105f-5001-403b-b9f1-b0b0b236ad65}</Project>
<Name>VirtualPrinter.Agent.Lib</Name>
</ProjectReference>
<ProjectReference Include="..\..\Common\VirtualPrinter.Logging\VirtualPrinter.Logging.csproj">
<Project>{aa25364d-22d5-44b0-86a5-6fb14c686308}</Project>
<Name>VirtualPrinter.Logging</Name>
</ProjectReference>
<ProjectReference Include="..\..\UI\VirtualPrinter.ProgressInfo.Autofac\VirtualPrinter.ProgressInfo.Autofac.csproj">
<Project>{17e2cf8a-462c-4130-9faf-f1ca5fc4e06d}</Project>
<Name>VirtualPrinter.ProgressInfo.Autofac</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.0.0" />
<PackageReference Include="Cassia" Version="3.0.0-alpha.9" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,35 @@
using System.ServiceProcess;
using Autofac;
using VirtualPrinter.Agent.Autofac;
using VirtualPrinter.Logging;
using VirtualPrinter.ProgressInfo.Autofac;
namespace VirtualPrinter.Agent.Service
{
/// <summary>
/// The Windows service that is registered during an installation
/// </summary>
public static class Program
{
public static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new VirtualPrinterModule());
builder.RegisterModule(new ProgressInfoModule());
builder.RegisterModule(new LoggerModule());
builder.RegisterType<VirtualPrinterService>().As<ServiceBase>();
var container = builder.Build();
var servicesToRun = new[]
{
container.Resolve<ServiceBase>()
};
ServiceBase.Run(servicesToRun);
}
}
}

View file

@ -0,0 +1,60 @@
namespace VirtualPrinter.Agent.Service
{
partial class ProjectInstaller
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
//
// serviceProcessInstaller1
//
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
//
// serviceInstaller1
//
this.serviceInstaller1.ServiceName = VirtualPrinterService.PrinterServiceName;
this.serviceInstaller1.Description = VirtualPrinterService.PrinterDescription;
this.serviceInstaller1.ServicesDependedOn = new[] {"Spooler"};
this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
//
// ProjectInstaller
//
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
this.serviceProcessInstaller1,
this.serviceInstaller1});
}
#endregion
private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
private System.ServiceProcess.ServiceInstaller serviceInstaller1;
}
}

View file

@ -0,0 +1,14 @@
using System.ComponentModel;
using System.Configuration.Install;
namespace VirtualPrinter.Agent.Service
{
[RunInstaller(true)]
public partial class ProjectInstaller : Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
}
}

View file

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="serviceProcessInstaller1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 56</value>
</metadata>
<metadata name="serviceInstaller1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>196, 17</value>
</metadata>
<metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>

View file

@ -0,0 +1,22 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("VirtualPrinter.Agent.Service")]
[assembly: AssemblyDescription("The agent for the virtual printer")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("8c4f0640-4628-4cea-8e31-143d68a3a70f")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
// For testing
[assembly: InternalsVisibleTo("VirtualPrinter.Test")]

View file

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8C4F0640-4628-4CEA-8E31-143D68A3A70F}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>VirtualPrinter.Agent.Service</RootNamespace>
<AssemblyName>VPDAgent</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Files\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject>VirtualPrinter.Agent.Service.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="PresentationCore" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.ServiceProcess" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="ProjectInstaller.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ProjectInstaller.Designer.cs">
<DependentUpon>ProjectInstaller.cs</DependentUpon>
</Compile>
<Compile Include="VirtualPrinterService.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="VirtualPrinterService.Designer.cs">
<DependentUpon>VirtualPrinterService.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="..\Common\VirtualPrinter.Logging\NLog.config">
<Link>NLog.config</Link>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ProjectInstaller.resx">
<DependentUpon>ProjectInstaller.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="VirtualPrinterService.resx">
<DependentUpon>VirtualPrinterService.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\VirtualPrinter.Agent.Autofac\VirtualPrinter.Agent.Autofac.csproj">
<Project>{1b2f0781-82d7-4576-b936-c6a26053d6ed}</Project>
<Name>VirtualPrinter.Agent.Autofac</Name>
</ProjectReference>
<ProjectReference Include="..\..\Common\VirtualPrinter.Agent.Core\VirtualPrinter.Agent.Core.csproj">
<Project>{135c85eb-2116-4cc4-8ccb-b6804b9d6467}</Project>
<Name>VirtualPrinter.Agent.Core</Name>
</ProjectReference>
<ProjectReference Include="..\..\Common\VirtualPrinter.Agent.Lib\VirtualPrinter.Agent.Lib.csproj">
<Project>{94e8105f-5001-403b-b9f1-b0b0b236ad65}</Project>
<Name>VirtualPrinter.Agent.Lib</Name>
</ProjectReference>
<ProjectReference Include="..\..\Common\VirtualPrinter.Logging\VirtualPrinter.Logging.csproj">
<Project>{aa25364d-22d5-44b0-86a5-6fb14c686308}</Project>
<Name>VirtualPrinter.Logging</Name>
</ProjectReference>
<ProjectReference Include="..\..\UI\VirtualPrinter.ProgressInfo.Autofac\VirtualPrinter.ProgressInfo.Autofac.csproj">
<Project>{17e2cf8a-462c-4130-9faf-f1ca5fc4e06d}</Project>
<Name>VirtualPrinter.ProgressInfo.Autofac</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.6.1">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.6.1 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.0.0" />
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,40 @@
namespace VirtualPrinter.Agent.Service
{
partial class VirtualPrinterService
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// VirtualPrinterService
//
this.ServiceName = VirtualPrinterService.PrinterServiceName;
}
#endregion
}
}

View file

@ -0,0 +1,52 @@
using System;
using System.ServiceProcess;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core;
namespace VirtualPrinter.Agent.Service
{
public partial class VirtualPrinterService : ServiceBase
{
public const string PrinterServiceName = "VirtualPrinterService";
public const string PrinterDescription = "Handles virtual printers";
[NotNull]
private readonly IVirtualPrinterService _virtualPrinterService;
public VirtualPrinterService([NotNull]IVirtualPrinterService virtualPrinterService)
{
if (virtualPrinterService == null)
{
throw new ArgumentNullException(nameof(virtualPrinterService));
}
InitializeComponent();
_virtualPrinterService = virtualPrinterService;
}
protected override void OnStart(string[] args)
{
OnServiceStart();
}
public void OnServiceStart()
{
// Insert additional code here to define processing.
_virtualPrinterService.Start();
}
protected override void OnStop()
{
OnServiceStop();
}
public void OnServiceStop()
{
// Insert additional code here to define processing.
_virtualPrinterService.Stop();
}
}
}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>

View file

@ -0,0 +1,98 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Printing;
using JetBrains.Annotations;
using VirtualPrinter.Logging;
using VirtualPrinter.Utils;
namespace VirtualPrinter.Delivery
{
/// <summary>
/// Redirects instructions to Ghostscript
/// </summary>
public class GhostScriptRedirector
{
private const string GsWin64 = "gswin64c.exe";
private const string GsWin32 = "gswin32c.exe";
[NotNull]
private readonly PrintQueue _queue;
public GhostScriptRedirector([NotNull]PrintQueue queue)
{
if (queue == null)
{
throw new ArgumentNullException(nameof(queue));
}
_queue = queue;
}
/// <summary>
/// Starts the Ghostscript process for the specified <paramref name="file"/>
/// </summary>
/// <param name="file">The .redirect file located in the PrinterOutput directory</param>
public void Redirect([NotNull]string file)
{
if (file == null)
{
throw new ArgumentNullException(nameof(file));
}
var logger = new VirtualPrinterLogger<GhostScriptRedirector>();
var ghostScriptExe = GetGhostScriptPath();
if (ghostScriptExe == null)
{
throw new FileNotFoundException("Can not find Ghostscript.");
}
// More details about the arguments can be find at https://www.ghostscript.com/doc/current/Use.htm
var ghostScriptArguments = $"-dPrinted -dBATCH -dNOPAUSE -dNoCancel -dNOSAFER -q -dNumCopies=1 -sDEVICE=mswinpr2 -sOutputFile=\"%printer%{ _queue.FullName}\" \"{file}\"";
logger.Info("Try to start the process {ghostScriptExe} with the following arguments: {ghostScriptArguments}.", ghostScriptExe, ghostScriptArguments);
var processStartInfo = new ProcessStartInfo
{
FileName = ghostScriptExe,
Arguments = ghostScriptArguments,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = false,
WindowStyle = ProcessWindowStyle.Hidden
};
try
{
using(var process = Process.Start(processStartInfo))
{
process.WaitForExit();
}
}
catch (Exception exception)
{
logger.Error(exception, "Failed to start {ghostScriptExe} with the following arguments: {ghostScriptArguments}", ghostScriptExe, ghostScriptArguments);
}
}
[CanBeNull]
private string GetGhostScriptPath()
{
return GetGhostScriptPath(GsWin64) ?? GetGhostScriptPath(GsWin32);
}
[CanBeNull]
private string GetGhostScriptPath(string execName)
{
if (!new RegistryRepository().TryGetGhostscriptPath(out var path))
{
return null;
}
var ghostScriptBinPath = Path.Combine(path, "bin");
var fullPath = Path.Combine(ghostScriptBinPath, execName);
return File.Exists(fullPath) ? fullPath : null;
}
}
}

View file

@ -0,0 +1,38 @@
using System;
using JetBrains.Annotations;
using static VirtualPrinter.Delivery.Redirector;
namespace VirtualPrinter.Delivery
{
internal class Program
{
private const string RedirectCmd = "redirect";
[STAThread]
private static void Main([CanBeNull]string[] args)
{
if (args == null || args.Length < 1) {
NotUseful();
return;
}
switch (args[0]) {
case RedirectCmd: {
RedirectToPrinter(args[1], args[2]);
break;
}
default: {
NotUseful();
break;
}
}
}
private static void NotUseful()
{
throw new ArgumentNullException($"Use '{RedirectCmd}'!");
}
}
}

View file

@ -0,0 +1,18 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("VirtualPrinter.Delivery")]
[assembly: AssemblyDescription("The delivery man for the virtual printer")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("74fa80b3-7cf1-4b68-8aa3-4c3d37bbe855")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,51 @@
using System;
using System.IO;
using System.Printing;
using JetBrains.Annotations;
namespace VirtualPrinter.Delivery
{
public static class Redirector
{
public static void RedirectToPrinter([NotNull]string filePath, [NotNull]string printerName)
{
if (filePath == null)
{
throw new ArgumentNullException(nameof(filePath));
}
if (printerName == null)
{
throw new ArgumentNullException(nameof(printerName));
}
var file = Path.GetFullPath(filePath);
// e.g. "Dell C2665dnf Color MFP"
using (var localSystem = new LocalPrintServer())
{
using (var queue = localSystem.GetPrintQueueSafe(printerName))
{
if (queue != null)
{
var ghostScriptRedirector = new GhostScriptRedirector(queue);
ghostScriptRedirector.Redirect(file);
}
}
}
}
[CanBeNull]
private static PrintQueue GetPrintQueueSafe(this PrintServer server, string name)
{
try
{
return server.GetPrintQueue(name);
}
catch
{
return null;
}
}
}
}

View file

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{74FA80B3-7CF1-4B68-8AA3-4C3D37BBE855}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>VirtualPrinter.Delivery</RootNamespace>
<AssemblyName>delivery</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Files</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject>VirtualPrinter.Delivery.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="ReachFramework" />
<Reference Include="System" />
<Reference Include="System.Printing" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="GhostScriptRedirector.cs" />
<Compile Include="Redirector.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\VirtualPrinter.Logging\VirtualPrinter.Logging.csproj">
<Project>{aa25364d-22d5-44b0-86a5-6fb14c686308}</Project>
<Name>VirtualPrinter.Logging</Name>
</ProjectReference>
<ProjectReference Include="..\..\Common\VirtualPrinter.Utils\VirtualPrinter.Utils.csproj">
<Project>{cd1c8e9d-5335-41ac-b0c0-88fd7c7c55f3}</Project>
<Name>VirtualPrinter.Utils</Name>
</ProjectReference>
<ProjectReference Include="..\..\Common\VirtualPrinter.Logging\VirtualPrinter.Logging.csproj">
<Project>{AA25364D-22D5-44B0-86A5-6FB14C686308}</Project>
<Name>VirtualPrinter.Logging</Name>
</ProjectReference>
<ProjectReference Include="..\..\Common\VirtualPrinter.Utils\VirtualPrinter.Utils.csproj">
<Project>{cd1c8e9d-5335-41ac-b0c0-88fd7c7c55f3}</Project>
<Name>VirtualPrinter.Utils</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>2020.1.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,32 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("VirtualPrinter.Agent.Autofac")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("fc76a2af-b0db-4ec8-a6b7-dbd25d461ab5")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1B2F0781-82D7-4576-B936-C6A26053D6ED}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>VirtualPrinter.Agent.Autofac</RootNamespace>
<AssemblyName>VirtualPrinter.Agent.Autofac</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Files\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VirtualPrinterModule.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Printing" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VirtualPrinter.Agent.Core\VirtualPrinter.Agent.Core.csproj">
<Project>{135c85eb-2116-4cc4-8ccb-b6804b9d6467}</Project>
<Name>VirtualPrinter.Agent.Core</Name>
</ProjectReference>
<ProjectReference Include="..\VirtualPrinter.Agent.Lib\VirtualPrinter.Agent.Lib.csproj">
<Project>{94e8105f-5001-403b-b9f1-b0b0b236ad65}</Project>
<Name>VirtualPrinter.Agent.Lib</Name>
</ProjectReference>
<ProjectReference Include="..\VirtualPrinter.Logging\VirtualPrinter.Logging.csproj">
<Project>{AA25364D-22D5-44B0-86A5-6FB14C686308}</Project>
<Name>VirtualPrinter.Logging</Name>
</ProjectReference>
<ProjectReference Include="..\VirtualPrinter.Utils\VirtualPrinter.Utils.csproj">
<Project>{CD1C8E9D-5335-41AC-B0C0-88FD7C7C55F3}</Project>
<Name>VirtualPrinter.Utils</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.0.0" />
<PackageReference Include="Autofac.Extras.NLog" Version="4.0.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="NLog" Version="4.6.1" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,33 @@
using Autofac;
using VirtualPrinter.Agent.Core;
using VirtualPrinter.Agent.Lib;
using VirtualPrinter.Agent.Lib.Misc;
using VirtualPrinter.Utils;
namespace VirtualPrinter.Agent.Autofac
{
/// <summary>
/// All classes to be resolved with IoC are registered here
/// </summary>
public class VirtualPrinterModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<GhostScriptConverter>().As<IPostScriptConverter>();
builder.RegisterType<UserRegistryConfig>().As<IUserConfig>();
builder.RegisterType<JobService>().As<IJobService>();
builder.RegisterType<JobFactory>().As<IJobFactory>();
builder.RegisterType<JobProcessor>().As<IJobProcessor>();
builder.RegisterType<Job>().As<IJob>();
builder.RegisterType<Shell>().As<IShell>();
builder.RegisterType<VirtualTcpInputPrinter>().As<IVirtualPrinter>();
builder.RegisterType<RegistryConfig>().As<IExConfig>();
builder.RegisterType<VirtualPrinterService>().As<IVirtualPrinterService>();
builder.RegisterType<JobRedirector>().As<IJobRedirector>();
builder.RegisterType<RegistryRepository>().As<IRegistryRepository>();
builder.RegisterType<Shell>().As<IShell>();
builder.RegisterType<DirectoryHelper>().As<IDirectoryHelper>();
}
}
}

View file

@ -0,0 +1,8 @@
namespace VirtualPrinter.Agent.Core.Enums
{
public enum IntermediateFormat
{
Ps,
Xps
}
}

View file

@ -0,0 +1,20 @@
using JetBrains.Annotations;
namespace VirtualPrinter.Agent.Core
{
public interface IConfig
{
/// <summary>
/// The mask for the filename.
/// </summary>
/// <remarks>The mask can be look like {yyyy}{MM}{DD}{hh}{mm}{ss}{job05}{page03}</remarks>
[NotNull]
string FileNameMask { get; }
/// <summary>
/// The port of the printer.
/// </summary>
/// <remarks>E.g. 9101</remarks>
short PrinterPort { get; }
}
}

View file

@ -0,0 +1,7 @@
namespace VirtualPrinter.Agent.Core
{
public interface IDirectoryHelper
{
string GetOutputDirectory(IExConfig config);
}
}

View file

@ -0,0 +1,36 @@
using System;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core.Enums;
namespace VirtualPrinter.Agent.Core
{
public interface IExConfig : IConfig
{
/// <summary>
/// Splits a preconverter into two strings.
/// </summary>
/// <remarks>e.g. "C:\Program Files (x86)\MySoftware\MySoftware.exe PRINT" now becomes: string 1 = "C:\Program Files (x86)\MySoftware\MySoftware.exe" and string 2 = "PRINT"</remarks>
[NotNull]
Tuple<string, string> ResolvedPreconverter { get; }
/// <summary>
/// Splits a postconverter into two strings.
/// </summary>
/// <remarks>e.g. "C:\Program Files (x86)\MySoftware\MySoftware.exe PRINTCOMPLETE" now becomes: string 1 = "C:\Program Files (x86)\MySoftware\MySoftware.exe" and string 2 = "PRINTCOMPLETE"</remarks>
[NotNull]
Tuple<string, string> ResolvedPostconverter { get; }
/// <summary>
/// The full path of the output directory.
/// </summary>
[NotNull]
string ResolvedOutputDirectory { get; }
/// <summary>
/// An intermediate format which is read by the printer or similar.
/// </summary>
IntermediateFormat IntermediateFormat { get; }
}
}

View file

@ -0,0 +1,34 @@
using JetBrains.Annotations;
namespace VirtualPrinter.Agent.Core
{
/// <summary>
/// The information of the job.
/// </summary>
public interface IJob
{
/// <summary>
/// The path to the file containing the data.
/// </summary>
[NotNull]
string RawDataPath { get; }
/// <summary>
/// The path to the ini file.
/// </summary>
[NotNull]
string IniDataPath { get; }
/// <summary>
/// Several job infos.
/// </summary>
[NotNull]
IJobInfo JobInfo { get; }
/// <summary>
/// Information about the session.
/// </summary>
[NotNull]
ISessionInfo SessionInfo { get; }
}
}

View file

@ -0,0 +1,15 @@
using System.IO;
using JetBrains.Annotations;
namespace VirtualPrinter.Agent.Core
{
public interface IJobFactory
{
[CanBeNull]
IJob Create([NotNull]string printerName, [NotNull]Stream stream);
[NotNull]
IJob Create([NotNull]string iniPath, [NotNull]string rawPath, IJobInfo jobInfo, ISessionInfo sessionInfo);
}
}

View file

@ -0,0 +1,15 @@
using System.Printing;
namespace VirtualPrinter.Agent.Core
{
public interface IJobInfo
{
int JobId { get; set; }
string Name { get; set; }
string DomainName { get; set; }
string MachineName { get; set; }
string UserName { get; set; }
PrintJobStatus Status { get; set; }
string DeviceName { get; set; }
}
}

View file

@ -0,0 +1,18 @@
using System;
using JetBrains.Annotations;
namespace VirtualPrinter.Agent.Core
{
public interface IJobProcessor
{
/// <summary>
/// Processes an <see cref="IJob"/> passed to it with the information from the <see cref="IUserConfig"/>.
/// </summary>
/// <param name="job"></param>
/// <param name="userConfig"></param>
/// <exception cref="ArgumentNullException">Throws when the <param name="job">job</param> or the <param name="userConfig"> is null.</param></exception>
/// <exception cref="PostScriptConversionException">The job cannot be converted. There is no redirect to a printer. Will not be thrown.</exception>
void Process([NotNull]IJob job, [NotNull]IUserConfig userConfig);
}
}

View file

@ -0,0 +1,17 @@
using System;
using JetBrains.Annotations;
namespace VirtualPrinter.Agent.Core
{
public interface IJobRedirector
{
/// <summary>
/// Redirects the information from the <param name="job">job</param> and the <param name="userConfig">config</param> to an Process to be executed.
/// </summary>
/// <param name="job"></param>
/// <param name="userConfig"></param>
/// <exception cref="ArgumentNullException">Throws when the <see cref="IJob"/> or the <see cref="IUserConfig"/> is null.</exception>
void Redirect([NotNull]IJob job, [NotNull]IUserConfig userConfig);
}
}

View file

@ -0,0 +1,47 @@
using System;
using JetBrains.Annotations;
namespace VirtualPrinter.Agent.Core
{
public interface IJobService
{
/// <summary>
/// Starts the <see cref="IJob"/> in a new Process.
/// </summary>
/// <param name="job"></param>
/// <exception cref="ArgumentNullException">Throws when the <see cref="IJob"/> is null.</exception>
void Start([NotNull]IJob job);
/// <summary>
/// Creates an new <see cref="IJob"/> from the <param name="iniPath">ini file</param> and the <param name="rawPath">raw path</param>.
/// </summary>
/// <param name="iniPath"></param>
/// <param name="rawPath"></param>
/// <returns>An new <see cref="IJob"/> object.</returns>
/// <exception cref="ArgumentException">Throws when the <paramref name="iniPath"/> or the <paramref name="rawPath"/> is null or empty.</exception>
[NotNull]
IJob CreateJob([NotNull]string iniPath, [NotNull]string rawPath);
/// <summary>
/// Reads the <see cref="PrintStatus"/> from the <paramref name="iniPath"/>
/// </summary>
/// <param name="iniPath"></param>
/// <returns>A <see cref="PrintStatus"/></returns>
/// <exception cref="ArgumentException">Throws when the <paramref name="iniPath"/> is null or empty.</exception>
PrintStatus ReadStatus([NotNull]string iniPath);
/// <summary>
/// Starts a new process to finish the <see cref="IJob"/>.
/// </summary>
/// <param name="job"></param>
void Finish([NotNull]IJob job);
/// <summary>
/// Gets the <see cref="JobStatus"/> from the ini file.
/// </summary>
/// <param name="iniPath">The path to the ini file</param>
/// <returns><see cref="JobStatus"/></returns>
JobStatus ReadJobStatus(string iniPath);
}
}

View file

@ -0,0 +1,15 @@
using System;
using VirtualPrinter.Agent.Lib.Model;
namespace VirtualPrinter.Agent.Core
{
public interface IPostScriptConverter
{
event EventHandler<IJob> ProgressInitialized;
event EventHandler<IJob> ProgressFinished;
event EventHandler<ProgressUpdateArgs> ProgressUpdate;
void Convert(IJob job, string target, PostScriptRenderOptions options);
}
}

View file

@ -0,0 +1,29 @@
using JetBrains.Annotations;
namespace VirtualPrinter.Agent.Core
{
public interface IRegistryRepository
{
/// <summary>
/// Try to get the ghostscript path from.
/// </summary>
/// <returns>True if the path exists.</returns>
[ContractAnnotation("=>true,path:notnull; =>false,path:null")]
bool TryGetGhostscriptPath(out string path);
/// <summary>
/// Get the <see cref="IExConfig"/> from the registry.
/// </summary>
/// <returns>The configuration that was read from the registry in HKEY_LOCAL_MACHINE\SOFTWARE.</returns>
[NotNull]
IExConfig GetRegistryConfig();
/// <summary>
/// Get the <see cref="IUserConfig"/> from the registry.
/// </summary>
/// <param name="sid">The security identifier with which each Windows user can be clearly identified in the network.</param>
/// <returns>The configuration that was read from the registry in HKEY_USERS</returns>
[NotNull]
IUserConfig GetUserRegistryConfig([NotNull]string sid);
}
}

View file

@ -0,0 +1,11 @@
namespace VirtualPrinter.Agent.Core
{
public interface ISessionInfo
{
int Id { get; set; }
string Desktop { get; set; }
string Sid { get; set; }
}
}

View file

@ -0,0 +1,16 @@
using JetBrains.Annotations;
namespace VirtualPrinter.Agent.Core
{
public interface IShell
{
void WriteIniEntry(string section, string key, string value, string iniFilePath);
[NotNull]
T ReadIniEntry<T>(string section, string key, string iniFilePath);
void Execute(IJobInfo job, ISessionInfo session, string exe, string args);
bool FileExists([NotNull]string path);
}
}

View file

@ -0,0 +1,26 @@
using JetBrains.Annotations;
namespace VirtualPrinter.Agent.Core
{
public interface IUserConfig
{
/// <summary>
/// The printer stored in the registry.
/// </summary>
[CanBeNull]
string RedirectPrinter { get; }
/// <summary>
/// The DPI value stored in the registry.
/// </summary>
/// <remarks>Initial value is null.</remarks>
double? UserRenderDpi { get; }
/// <summary>
/// The format that you choose on your client side stored in the registry.
/// </summary>
/// <remarks>Intital value is PDF</remarks>
[NotNull]
string Format { get; }
}
}

View file

@ -0,0 +1,12 @@
using System;
namespace VirtualPrinter.Agent.Core
{
public interface IVirtualPrinter : IDisposable
{
/// <summary>
/// Initialize the virtual printer.
/// </summary>
void Init();
}
}

View file

@ -0,0 +1,8 @@
namespace VirtualPrinter.Agent.Core
{
public interface IVirtualPrinterService
{
void Start();
void Stop();
}
}

View file

@ -0,0 +1,9 @@
namespace VirtualPrinter.Agent.Core
{
public enum JobStatus
{
Completed,
Failed,
InProgress
}
}

View file

@ -0,0 +1,15 @@
using System.Printing;
namespace VirtualPrinter.Agent.Core
{
public struct JobInfo : IJobInfo
{
public int JobId { get; set; }
public string Name { get; set; }
public string DomainName { get; set; }
public string MachineName { get; set; }
public string UserName { get; set; }
public PrintJobStatus Status { get; set; }
public string DeviceName { get; set; }
}
}

View file

@ -0,0 +1,19 @@
using System;
namespace VirtualPrinter.Agent.Core
{
public class PostScriptConversionException : Exception
{
public PostScriptConversionException()
{
}
public PostScriptConversionException(string message) : base(message)
{
}
public PostScriptConversionException(string message, Exception inner) : base(message, inner)
{
}
}
}

View file

@ -0,0 +1,9 @@
namespace VirtualPrinter.Agent.Core
{
public struct PostScriptRenderOptions
{
public double? UserRenderDpi { get; set; }
public PostScriptRenderPdfOptions PdfOptions { get; set; }
public PostScriptRenderTiffOptions TiffOptions { get; set; }
}
}

View file

@ -0,0 +1,8 @@
namespace VirtualPrinter.Agent.Core
{
public struct PostScriptRenderPdfOptions
{
public bool Enabled { set; get; }
public bool Archivable { get; set; }
}
}

View file

@ -0,0 +1,7 @@
namespace VirtualPrinter.Agent.Core
{
public struct PostScriptRenderTiffOptions
{
public bool Enabled { set; get; }
}
}

View file

@ -0,0 +1,13 @@
using JetBrains.Annotations;
namespace VirtualPrinter.Agent.Core
{
public static class PrintExts
{
[NotNull]
public static string ToIni(this PrintStatus status)
{
return status.ToString().ToLowerInvariant();
}
}
}

View file

@ -0,0 +1,15 @@
namespace VirtualPrinter.Agent.Core
{
public enum PrintStatus
{
Undefined = 0,
Paused,
Resumed,
Complete,
Canceled
}
}

View file

@ -0,0 +1,22 @@
using System;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core;
namespace VirtualPrinter.Agent.Lib.Model
{
public class ProgressUpdateArgs : EventArgs
{
public ProgressUpdateArgs([NotNull] IJob job, uint val)
{
Job = job;
Value = val;
}
[NotNull]
public IJob Job { get; }
public uint Value { get; }
}
}

View file

@ -0,0 +1,49 @@
using System;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core.Enums;
namespace VirtualPrinter.Agent.Core
{
public class RegistryConfig : IExConfig
{
public string Postconverter { get; set; }
public string Preconverter { get; set; }
public string OutputDirectory { get; set; }
public string FileNameMask { get; set; }
public short PrinterPort { get; set; }
public Tuple<string, string> ResolvedPreconverter
{
get { return GetResolvedArgs(Preconverter); }
}
public Tuple<string, string> ResolvedPostconverter
{
get { return GetResolvedArgs(Postconverter); }
}
public string ResolvedOutputDirectory
{
get { return string.IsNullOrWhiteSpace(OutputDirectory) ? "" : Path.GetFullPath(OutputDirectory); }
}
public IntermediateFormat IntermediateFormat { get; set; }
[NotNull]
private static Tuple<string, string> GetResolvedArgs([NotNull]string text)
{
const string ending = ".exe";
var parts = text.Split(new[] { ending }, StringSplitOptions.RemoveEmptyEntries);
return Tuple.Create(Path.GetFullPath(parts.First() + ending), parts.Last().Trim());
}
}
}

View file

@ -0,0 +1,11 @@
namespace VirtualPrinter.Agent.Core
{
public struct SessionInfo : ISessionInfo
{
public int Id { get; set; }
public string Desktop { get; set; }
public string Sid { get; set; }
}
}

View file

@ -0,0 +1,13 @@
namespace VirtualPrinter.Agent.Core
{
public class UserRegistryConfig : IUserConfig
{
public bool RedirectEnabled { get; set; }
public string RedirectPrinter { get; set; }
public double? UserRenderDpi { get; set; }
public string Format { get; set; }
}
}

View file

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("VirtualPrinter.Agent.Core")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("135c85eb-2116-4cc4-8ccb-b6804b9d6467")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
// indem Sie "*" wie unten gezeigt eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{135C85EB-2116-4CC4-8CCB-B6804B9D6467}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VirtualPrinter.Agent.Core</RootNamespace>
<AssemblyName>VirtualPrinter.Agent.Core</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Files\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="ReachFramework" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Printing" />
</ItemGroup>
<ItemGroup>
<Compile Include="Enums\IntermediateFormat.cs" />
<Compile Include="Interfaces\IDirectoryHelper.cs" />
<Compile Include="Interfaces\IConfig.cs" />
<Compile Include="Interfaces\IExConfig.cs" />
<Compile Include="Interfaces\IJob.cs" />
<Compile Include="Interfaces\IJobFactory.cs" />
<Compile Include="Interfaces\IJobInfo.cs" />
<Compile Include="Interfaces\IJobProcessor.cs" />
<Compile Include="Interfaces\IJobRedirector.cs" />
<Compile Include="Interfaces\IJobService.cs" />
<Compile Include="Interfaces\IPostScriptConverter.cs" />
<Compile Include="Interfaces\IRegistryRepository.cs" />
<Compile Include="Interfaces\ISessionInfo.cs" />
<Compile Include="Interfaces\IShell.cs" />
<Compile Include="Interfaces\IUserConfig.cs" />
<Compile Include="Interfaces\IVirtualPrinter.cs" />
<Compile Include="Interfaces\IVirtualPrinterService.cs" />
<Compile Include="Interfaces\JobStatus.cs" />
<Compile Include="Model\JobInfo.cs" />
<Compile Include="Model\PostScriptConversionException.cs" />
<Compile Include="Model\PostScriptRenderOptions.cs" />
<Compile Include="Model\PostScriptRenderPdfOptions.cs" />
<Compile Include="Model\PostScriptRenderTiffOptions.cs" />
<Compile Include="Model\ProgressUpdateArgs.cs" />
<Compile Include="Model\RegistryConfig.cs" />
<Compile Include="Model\UserRegistryConfig.cs" />
<Compile Include="Model\PrintExts.cs" />
<Compile Include="Model\PrintStatus.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Model\SessionInfo.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,218 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core;
using VirtualPrinter.Agent.Lib.Model;
using VirtualPrinter.Logging;
namespace VirtualPrinter.Agent.Lib.Misc
{
public class GhostScriptConverter : IPostScriptConverter
{
private const string GsWin64 = "gswin64c.exe";
private const string GsWin32 = "gswin32c.exe";
[NotNull]
private readonly IVirtualPrinterLogger<GhostScriptConverter> _logger;
[NotNull]
private readonly IRegistryRepository _registryRepository;
[NotNull]
private readonly IShell _shell;
public event EventHandler<IJob> ProgressInitialized;
public event EventHandler<IJob> ProgressFinished;
public event EventHandler<ProgressUpdateArgs> ProgressUpdate;
public GhostScriptConverter
(
[NotNull]IVirtualPrinterLogger<GhostScriptConverter> logger,
[NotNull]IRegistryRepository registryRepository,
[NotNull]IShell shell
)
{
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
if (registryRepository == null)
{
throw new ArgumentNullException(nameof(registryRepository));
}
if (shell == null)
{
throw new ArgumentNullException(nameof(shell));
}
_logger = logger;
_registryRepository = registryRepository;
_shell = shell;
}
public void Convert(IJob job, string target, PostScriptRenderOptions options)
{
var ghostScriptExe = GetGhostScriptPath();
if (ghostScriptExe == null)
{
throw new PostScriptConversionException("GhostScript not found. Please place local variable.");
}
ProgressInitialized?.Invoke(this, job);
var pdfTarget = target + ".pdf";
if (options.PdfOptions.Enabled)
{
Convert(job, ghostScriptExe, GetArgumentsForPdfConversion(job.RawDataPath, pdfTarget, options));
if (!_shell.FileExists(pdfTarget))
{
throw new PostScriptConversionException("Postscript conversion failed after output.");
}
}
if (options.TiffOptions.Enabled)
{
var tiffTarget = target + ".tif";
Convert(job, ghostScriptExe, GetArgumentsForTiffConversion(job.RawDataPath, tiffTarget, options));
Convert(job, ghostScriptExe, GetArgumentsForPdfConversion(job.RawDataPath, pdfTarget, options));
if (!_shell.FileExists(tiffTarget))
{
throw new PostScriptConversionException("Postscript conversion failed after output.");
}
}
ProgressFinished?.Invoke(this, job);
}
[CanBeNull]
private string GetGhostScriptPath()
{
return GetGhostScriptPath(GsWin64) ?? GetGhostScriptPath(GsWin32);
}
[CanBeNull]
private string GetGhostScriptPath(string execName)
{
if (!_registryRepository.TryGetGhostscriptPath(out var path))
{
return null;
}
var ghostScriptBinPath = Path.Combine(path, "bin");
var fullPath = Path.Combine(ghostScriptBinPath, execName);
return _shell.FileExists(fullPath) ? fullPath : null;
}
[NotNull]
private static string GetArgumentsForPdfConversion(string source, string target, PostScriptRenderOptions options)
{
const string initialArguments = "-q -P- -dSAFER -dNOPAUSE -dBATCH -dNoCancel -sDEVICE=pdfwrite";
var finalArguments = $"-sOutputFile=\"{target}\" \"{source}\"";
var optionalArguments = "";
if (options.PdfOptions.Archivable)
{
optionalArguments += "-sColorConversionStrategy=/RGB -dUseCIEColor -dPDFACompatibilityPolicy=2";
}
if (options.UserRenderDpi != null && options.UserRenderDpi > 0)
{
optionalArguments += "-r" + options.UserRenderDpi.Value;
}
return $"{initialArguments} {optionalArguments} {finalArguments}";
}
[NotNull]
private static string GetArgumentsForTiffConversion(string source, string target, PostScriptRenderOptions options)
{
const string initialArguments = "-q -P- -dSAFER -dNOPAUSE -dBATCH -sDEVICE=tiff12nc -sCompression=lzw";
var finalArguments = $"-sOutputFile=\"{target}\" \"{source}\"";
var optionalArguments = "-dTextAlphaBits=4 ";
if (options.UserRenderDpi != null && options.UserRenderDpi > 0)
{
optionalArguments += "-r" + options.UserRenderDpi.Value;
}
else
{
optionalArguments += "-r300";
}
return $"{initialArguments} {optionalArguments} {finalArguments}";
}
private void Convert(IJob job, string gsExe, string gsArguments)
{
LogDebug("Starting ps conversion ...\n"+gsExe + " " + gsArguments);
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = gsExe,
Arguments = gsArguments,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
process.Start();
while (!process.StandardOutput.EndOfStream)
{
var readLine = process.StandardOutput.ReadLine();
var progress = ParseProgress(readLine);
if (progress == null)
{
continue;
}
ProgressUpdate?.Invoke(this, new ProgressUpdateArgs(job, progress.Value));
}
}
private static uint? ParseProgress([CanBeNull]string line)
{
// Example output:
// %%[Page: 49]%%
if (line == null)
{
return null;
}
var groups = Regex.Match(line, @"%%\[Page:(.*)\]%%").Groups;
if (groups.Count < 2)
{
return null;
}
if (!uint.TryParse(groups[1].Value, out var result))
{
return null;
}
return result;
}
private void LogDebug(string message, params object[] args)
{
_logger.Debug(message, args);
}
}
}

View file

@ -0,0 +1,15 @@
using VirtualPrinter.Agent.Core;
namespace VirtualPrinter.Agent.Lib.Misc
{
public class Job : IJob
{
public string RawDataPath { get; set; }
public string IniDataPath { get; set; }
public IJobInfo JobInfo { get; set; }
public ISessionInfo SessionInfo { get; set; }
}
}

View file

@ -0,0 +1,243 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Printing;
using System.Security.Principal;
using Cassia;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core;
using VirtualPrinter.Agent.Core.Enums;
using VirtualPrinter.Logging;
namespace VirtualPrinter.Agent.Lib.Misc
{
public class JobFactory : IJobFactory
{
[NotNull]
private readonly IVirtualPrinterLogger<JobFactory> _logger;
private readonly IDirectoryHelper _directoryHelper;
[NotNull]
private readonly IRegistryRepository _registryRepository;
public JobFactory
(
[NotNull]IRegistryRepository registryRepository,
[NotNull]IVirtualPrinterLogger<JobFactory> logger,
[NotNull]IDirectoryHelper directoryHelper
)
{
if (registryRepository == null)
{
throw new ArgumentNullException(nameof(registryRepository));
}
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
if (directoryHelper == null)
{
throw new ArgumentNullException(nameof(logger));
}
_registryRepository = registryRepository;
_logger = logger;
_directoryHelper = directoryHelper;
}
public IJob Create(string printerName, Stream stream)
{
if (printerName == null)
{
throw new ArgumentNullException(nameof(printerName));
}
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
try
{
var now = DateTime.Now;
var config = _registryRepository.GetRegistryConfig();
var root = _directoryHelper.GetOutputDirectory(config);
var jobInfo = GetCurrentPrintJobs(printerName).FirstOrDefault();
if (jobInfo == null)
{
throw new InvalidOperationException();
}
var session = GetCurrentSessions(jobInfo).FirstOrDefault();
var iniName = GenerateFileName(now, jobInfo.JobId, 0, config.FileNameMask, "ini");
var iniPath = Path.Combine(root, iniName);
var extension = GetRawFileExtension(config.IntermediateFormat);
var rawName = $"{Path.GetFileNameWithoutExtension(iniName)}.{extension}";
var rawPath = Path.Combine(root, rawName);
using (var output = File.Create(rawPath))
{
stream.CopyTo(output);
}
return new Job
{
RawDataPath = rawPath,
IniDataPath = iniPath,
JobInfo = jobInfo,
SessionInfo = session
};
}
catch (Exception exception)
{
LogError(exception, "Failed to create job.");
return null;
}
}
public IJob Create(string iniPath, string rawPath, IJobInfo jobInfo, ISessionInfo sessionInfo)
{
if (string.IsNullOrWhiteSpace(iniPath))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(iniPath));
}
if (string.IsNullOrWhiteSpace(rawPath))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(rawPath));
}
return new Job
{
IniDataPath = iniPath,
RawDataPath = rawPath,
JobInfo = jobInfo,
SessionInfo = sessionInfo
};
}
[NotNull]
private string GenerateFileName(DateTime time, int job, int page, [NotNull]string pattern, [NotNull]string ending)
{
var fileName = pattern;
fileName = fileName.Replace("{yyyy}", $"{time.Year:0000}");
fileName = fileName.Replace("{MM}", $"{time.Month:00}");
fileName = fileName.Replace("{DD}", $"{time.Day:00}");
fileName = fileName.Replace("{hh}", $"{time.Hour:00}");
fileName = fileName.Replace("{mm}", $"{time.Minute:00}");
fileName = fileName.Replace("{ss}", $"{time.Second:00}");
fileName = fileName.Replace("{fff}", $"{time.Millisecond:000}");
fileName = fileName.Replace("{job05}", $"{job:00000}");
fileName = fileName.Replace("{page03}", $"{page:000}");
return $"{fileName}.{ending}";
}
private IEnumerable<SessionInfo> GetCurrentSessions([NotNull]IJobInfo job)
{
var domain = job.DomainName;
var machine = job.MachineName?.TrimStart('\\');
var user = job.UserName;
LogDebug($"Searching for session of {domain}\\{user} on {machine} !");
if (domain == null || machine == null || user == null)
{
yield break;
}
const StringComparison cmp = StringComparison.OrdinalIgnoreCase;
using (var server = new TerminalServicesManager().GetLocalServer())
{
var sessions = server.GetSessions().Where(s => s.UserName != null && s.DomainName != null);
foreach (var session in sessions)
{
if (!session.UserName.Equals(user, cmp))
{
continue;
}
var isSingleUser = session.DomainName.Equals(machine, cmp);
var isDomainUser = session.DomainName.Equals(domain, cmp);
if (!isSingleUser && !isDomainUser)
{
continue;
}
var sessionId = session.SessionId;
var desktopName = session.WindowStationName;
var account = session.UserAccount;
yield return new SessionInfo
{
Id = sessionId,
Desktop = desktopName,
Sid = account.Translate(typeof(SecurityIdentifier)).Value
};
}
}
}
[ItemNotNull]
private IEnumerable<IJobInfo> GetCurrentPrintJobs(string printerName)
{
using (var server = new LocalPrintServer())
{
using (var queue = server.GetPrintQueue(printerName))
{
using (var jobs = queue.GetPrintJobInfoCollection())
{
foreach (var job in jobs)
{
using (job)
{
var id = job.JobIdentifier;
var machine = server.Name;
var domain = Environment.UserDomainName;
var user = job.Submitter;
var name = job.Name;
yield return new JobInfo
{
JobId = id,
Name = name,
DomainName = domain,
MachineName = machine,
UserName = user,
Status = job.JobStatus,
DeviceName = queue.Name
};
}
}
}
}
}
}
[NotNull]
private string GetRawFileExtension(IntermediateFormat format)
{
switch (format)
{
case IntermediateFormat.Xps:
return "xps";
case IntermediateFormat.Ps:
return "ps";
default:
throw new ArgumentOutOfRangeException();
}
}
private void LogDebug(string message)
{
_logger.Debug(message);
}
private void LogError(Exception exception, string message, params object[] args)
{
_logger.Error(exception, message, args);
}
}
}

View file

@ -0,0 +1,150 @@
using System;
using System.IO;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core;
using VirtualPrinter.Agent.Lib.Model;
using VirtualPrinter.Logging;
using VirtualPrinter.ProgressInfo.Core;
namespace VirtualPrinter.Agent.Lib.Misc
{
public class JobProcessor : IJobProcessor
{
[NotNull]
private readonly IRegistryRepository _registryRepository;
[NotNull]
private readonly IJobRedirector _jobRedirector;
[NotNull]
private readonly IVirtualPrinterLogger<JobProcessor> _logger;
[NotNull]
private readonly IPostScriptConverter _postScriptConverter;
[NotNull]
private readonly IProgressInfo _progressInfo;
private readonly IDirectoryHelper _directoryHelper;
public JobProcessor
(
[NotNull]IRegistryRepository registryRepository,
[NotNull]IVirtualPrinterLogger<JobProcessor> logger,
[NotNull]IPostScriptConverter postScriptConverter,
[NotNull]IJobRedirector redirector,
[NotNull]IProgressInfo progressInfo,
[NotNull]IDirectoryHelper directoryHelper
)
{
if (registryRepository == null)
{
throw new ArgumentNullException(nameof(registryRepository));
}
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
if (postScriptConverter == null)
{
throw new ArgumentNullException(nameof(postScriptConverter));
}
if (redirector == null)
{
throw new ArgumentNullException(nameof(redirector));
}
if (progressInfo == null)
{
throw new ArgumentNullException(nameof(progressInfo));
}
if (directoryHelper == null)
{
throw new ArgumentNullException(nameof(directoryHelper));
}
_registryRepository = registryRepository;
_logger = logger;
_postScriptConverter = postScriptConverter;
_jobRedirector = redirector;
_progressInfo = progressInfo;
_directoryHelper = directoryHelper;
_postScriptConverter.ProgressInitialized += OnProgressInitialized;
_postScriptConverter.ProgressFinished += OnProgressFinished;
_postScriptConverter.ProgressUpdate += OnProgressUpdate;
}
public void Process(IJob job, IUserConfig userConfig)
{
if (job == null)
{
throw new ArgumentNullException(nameof(job));
}
if (userConfig == null)
{
throw new ArgumentNullException(nameof(userConfig));
}
var targetFile = $"{Path.GetFileNameWithoutExtension(job.RawDataPath)}";
var config = _registryRepository.GetRegistryConfig();
var dir = _directoryHelper.GetOutputDirectory(config);
targetFile = Path.Combine(dir, targetFile);
var options = new PostScriptRenderOptions
{
UserRenderDpi = userConfig.UserRenderDpi,
PdfOptions = new PostScriptRenderPdfOptions
{
Enabled = userConfig.Format == "PDF" || string.IsNullOrEmpty(userConfig.Format)
},
TiffOptions = new PostScriptRenderTiffOptions
{
Enabled = userConfig.Format == "TIFF"
}
};
try
{
_postScriptConverter.Convert(job, targetFile, options);
}
catch (PostScriptConversionException exception)
{
LogError(exception, "Error processing PS file.");
return;
}
_jobRedirector.Redirect(job, userConfig);
}
private void OnProgressUpdate(object sender, [NotNull]ProgressUpdateArgs args)
{
if (args == null)
{
throw new ArgumentNullException(nameof(args));
}
_progressInfo.Progress(args.Job, args.Value);
}
private void OnProgressFinished(object sender, IJob job)
{
_progressInfo.Finish(job);
}
private void OnProgressInitialized(object sender, IJob job)
{
_progressInfo.Initialize(job);
}
private void LogError(Exception exception, string message, params object[] args)
{
_logger.Error(exception, message, args);
}
}
}

View file

@ -0,0 +1,106 @@
using System;
using System.IO;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core;
using VirtualPrinter.Delivery;
using VirtualPrinter.Logging;
using VirtualPrinter.Utils;
namespace VirtualPrinter.Agent.Lib.Misc
{
public class JobRedirector : IJobRedirector
{
[NotNull]
private readonly IVirtualPrinterLogger<JobRedirector> _logger;
[NotNull]
private readonly IShell _shell;
public JobRedirector([NotNull]IVirtualPrinterLogger<JobRedirector> logger, [NotNull]IShell shell)
{
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
if (shell == null)
{
throw new ArgumentNullException(nameof(shell));
}
_logger = logger;
_shell = shell;
}
public void Redirect(IJob job, IUserConfig userConfig)
{
if (job == null)
{
throw new ArgumentNullException(nameof(job));
}
if (userConfig == null)
{
throw new ArgumentNullException(nameof(userConfig));
}
var printer = userConfig.RedirectPrinter;
if (string.IsNullOrWhiteSpace(printer))
{
LogWarn("Can not redirect to empty printer.");
return;
}
try
{
var pdfToRedirect = GetPdfPath(job.RawDataPath);
LogDebug($"Redirecting '{pdfToRedirect}' to '{printer}'...");
var redirectExe = Path.GetFullPath(typeof(Redirector).Assembly.Location);
var redirectArgs = $@"redirect ""{pdfToRedirect}"" ""{printer}""";
_shell.Execute(job.JobInfo, job.SessionInfo, redirectExe, redirectArgs);
}
catch (Exception exception)
{
LogError(exception, $"{exception.GetType()}: {exception.Message}");
}
}
[NotNull]
private static string GetPdfPath([NotNull]string rawFilePath)
{
if (string.IsNullOrWhiteSpace(rawFilePath))
{
throw new ArgumentException(nameof(rawFilePath));
}
var directoryName = Path.GetDirectoryName(rawFilePath);
if (string.IsNullOrWhiteSpace(directoryName))
{
throw new ArgumentException(nameof(directoryName));
}
var file = Path.GetFileNameWithoutExtension(rawFilePath);
return Path.Combine(directoryName, file) + ".pdf";
}
private void LogDebug(string message, params object[] args)
{
_logger.Debug(message, args);
}
private void LogWarn(string message, params object[] args)
{
_logger.Warn(message, args);
}
private void LogError(Exception exception, string message, params object[] args)
{
_logger.Error(exception, message, args);
}
}
}

View file

@ -0,0 +1,170 @@
using System;
using System.IO;
using System.Printing;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core;
namespace VirtualPrinter.Agent.Lib.Misc
{
public class JobService : IJobService
{
[NotNull]
private readonly IRegistryRepository _registryRepository;
[NotNull]
private readonly IJobFactory _jobFactory;
[NotNull]
private readonly IShell _shell;
private IDirectoryHelper _directoryHelper;
public JobService
(
[NotNull]IRegistryRepository registryRepository,
[NotNull]IJobFactory jobFactory,
[NotNull]IShell shell,
[NotNull]IDirectoryHelper directoryHelper
)
{
if (registryRepository == null)
{
throw new ArgumentNullException(nameof(registryRepository));
}
if (jobFactory == null)
{
throw new ArgumentNullException(nameof(jobFactory));
}
if (shell == null)
{
throw new ArgumentNullException(nameof(shell));
}
if (directoryHelper == null)
{
throw new ArgumentNullException(nameof(directoryHelper));
}
_registryRepository = registryRepository;
_jobFactory = jobFactory;
_shell = shell;
_directoryHelper = directoryHelper;
}
public void Start(IJob job)
{
if (job == null)
{
throw new ArgumentNullException(nameof(job));
}
const PrintStatus status = PrintStatus.Paused;
var iniFile = Path.GetFullPath(job.IniDataPath);
var config = _registryRepository.GetRegistryConfig();
var pre = config.ResolvedPreconverter;
WriteJobStartIni(job, status);
_shell.Execute(job.JobInfo, job.SessionInfo, pre.Item1, $"{pre.Item2} \"{iniFile}\"");
}
public IJob CreateJob(string iniFile, string rawFile)
{
var jobInfo = GetJobInfo(iniFile);
var sessionInfo = GetSessionInfo(iniFile);
return _jobFactory.Create(iniFile, rawFile, jobInfo, sessionInfo);
}
public PrintStatus ReadStatus(string iniPath)
{
var txt = _shell.ReadIniEntry<string>("Preconverting", "Status", iniPath);
Enum.TryParse(txt, true, out PrintStatus result);
return result;
}
public JobStatus ReadJobStatus(string iniPath)
{
var status = _shell.ReadIniEntry<string>("Job", "Status", iniPath);
Enum.TryParse(status, true, out JobStatus result);
return result;
}
public void Finish(IJob job)
{
var config = _registryRepository.GetRegistryConfig();
WriteJobFinishIni(job.IniDataPath, config);
var iniFile = Path.GetFullPath(job.IniDataPath);
var post = config.ResolvedPostconverter;
_shell.Execute(job.JobInfo, job.SessionInfo, post.Item1, $"{post.Item2} \"{iniFile}\"");
}
private void WriteJobStartIni([NotNull]IJob job, PrintStatus status)
{
_shell.WriteIniEntry("Job", "Status", JobStatus.InProgress.ToString().ToLowerInvariant(), job.IniDataPath);
_shell.WriteIniEntry("Device", "DeviceName", job.JobInfo.DeviceName, job.IniDataPath);
_shell.WriteIniEntry("Document", "Name", job.JobInfo.Name.Normalize(), job.IniDataPath);
_shell.WriteIniEntry("Document", "JobID", $"{job.JobInfo.JobId}", job.IniDataPath);
_shell.WriteIniEntry("Document", "DomainName", job.JobInfo.DomainName, job.IniDataPath);
_shell.WriteIniEntry("Document", "MachineName", job.JobInfo.MachineName, job.IniDataPath);
_shell.WriteIniEntry("Document", "UserName", job.JobInfo.UserName, job.IniDataPath);
_shell.WriteIniEntry("Document", "SessionID", $"{job.SessionInfo.Id}", job.IniDataPath);
_shell.WriteIniEntry("Document", "Desktop", $"{job.SessionInfo.Desktop}", job.IniDataPath);
_shell.WriteIniEntry("Document", "SID", $"{job.SessionInfo.Sid}", job.IniDataPath);
_shell.WriteIniEntry("Document", "Status", job.JobInfo.Status.ToString(), job.IniDataPath);
_shell.WriteIniEntry("Preconverting", "Status", status.ToIni(), job.IniDataPath);
}
private SessionInfo GetSessionInfo(string iniFile)
{
var sessionInfo = new SessionInfo
{
Id = _shell.ReadIniEntry<int>("Document",
"SessionID",
iniFile),
Sid = _shell.ReadIniEntry<string>("Document", "SID", iniFile),
Desktop = _shell.ReadIniEntry<string>("Document", "Desktop", iniFile)
};
return sessionInfo;
}
private JobInfo GetJobInfo(string iniFile)
{
var jobInfo = new JobInfo
{
DomainName = _shell.ReadIniEntry<string>("Document",
"DomainName",
iniFile),
MachineName = _shell.ReadIniEntry<string>("Document",
"MachineName",
iniFile),
UserName = _shell.ReadIniEntry<string>("Document",
"UserName",
iniFile)
};
return jobInfo;
}
private void WriteJobFinishIni(string iniPath, [NotNull]IExConfig config)
{
const PrintStatus status = PrintStatus.Complete;
const PrintJobStatus spoolerState = PrintJobStatus.Printed;
_shell.WriteIniEntry("Preconverting", "Status", status.ToIni(), iniPath);
var pdfFile = Path.GetFileNameWithoutExtension(iniPath) + ".pdf";
var tiffFile = Path.GetFileNameWithoutExtension(iniPath) + ".tif";
var dir = _directoryHelper.GetOutputDirectory(config);
pdfFile = Path.Combine(dir, pdfFile);
tiffFile = Path.Combine(dir, tiffFile);
_shell.WriteIniEntry("PDF", "File0", pdfFile, iniPath);
_shell.WriteIniEntry("TIFF", "File0", tiffFile, iniPath);
_shell.WriteIniEntry("Document", "Status", spoolerState.ToString(), iniPath);
}
}
}

View file

@ -0,0 +1,276 @@
using JetBrains.Annotations;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using VirtualPrinter.Agent.Core;
using VirtualPrinter.Logging;
using VirtualPrinter.SetupDriver;
namespace VirtualPrinter.Agent.Lib.Misc
{
public class VirtualTcpInputPrinter : IVirtualPrinter
{
[NotNull]
private readonly IRegistryRepository _registryRepository;
[NotNull]
private readonly IJobFactory _jobFactory;
[NotNull]
private readonly IJobService _jobService;
[NotNull]
private readonly IVirtualPrinterLogger<VirtualTcpInputPrinter> _logger;
[NotNull]
private readonly IJobProcessor _jobProcessor;
private IDirectoryHelper _directoryHelper;
private TcpListener _socket;
private FileSystemWatcher _watcher;
public VirtualTcpInputPrinter
(
[NotNull]IRegistryRepository registryRepository,
[NotNull]IVirtualPrinterLogger<VirtualTcpInputPrinter> logger,
[NotNull]IJobFactory jobFactory,
[NotNull]IJobService jobService,
[NotNull]IJobProcessor jobProcessor,
[NotNull]IDirectoryHelper directoryHelper
)
{
_registryRepository = registryRepository;
_logger = logger;
_jobFactory = jobFactory;
_jobService = jobService;
_jobProcessor = jobProcessor;
_directoryHelper = directoryHelper;
}
public void Dispose()
{
_watcher.Dispose();
_socket.Stop();
}
public void Init()
{
var config = GetRegistryConfig();
var dir = _directoryHelper.GetOutputDirectory(config);
_watcher = new FileSystemWatcher(dir, "*.ini")
{
IncludeSubdirectories = false,
NotifyFilter = NotifyFilters.LastWrite,
EnableRaisingEvents = true
};
_watcher.Changed += IniFileChanged;
_socket = new TcpListener(IPAddress.Loopback, config.PrinterPort);
_socket.Start();
_socket.BeginAcceptTcpClient(HandleClient, _socket);
LogDebug($"Waiting on {_socket.LocalEndpoint}...");
}
private void HandleClient([NotNull]IAsyncResult ar)
{
const string printer = Defaults.PrinterName;
IJob job;
var socket = (TcpListener) ar.AsyncState;
using (var client = socket.EndAcceptTcpClient(ar))
{
var local = client.Client.LocalEndPoint;
var remote = client.Client.RemoteEndPoint;
LogDebug($"{remote} --> {local}");
job = _jobFactory.Create(printer, client.GetStream());
if (job == null)
{
LogError("Job could not be created.");
return;
}
}
LogDebug($"Temporarily printed '{job.RawDataPath}'!");
socket.BeginAcceptTcpClient(HandleClient, ar.AsyncState);
_jobService.Start(job);
}
private void IniFileChanged(object sender, [NotNull]FileSystemEventArgs e)
{
var ini = e.FullPath;
if (!ini.ToLowerInvariant().EndsWith(".ini"))
{
return;
}
var rawName = $"{Path.GetFileNameWithoutExtension(ini)}.ps";
var config = GetRegistryConfig();
var dir = _directoryHelper.GetOutputDirectory(config);
var rawFile = Path.Combine(dir, rawName);
var status = _jobService.ReadStatus(ini);
if (status == PrintStatus.Resumed)
{
var job = _jobService.CreateJob(ini, rawFile);
var isJobValid = IsJobValid(job);
if (!isJobValid)
{
return;
}
ProcessFile(rawFile, ini);
}
if (status == PrintStatus.Canceled)
{
DeleteFiles(ini, dir, rawFile);
}
var jobStatus = _jobService.ReadJobStatus(ini);
if (jobStatus == JobStatus.Completed || jobStatus == JobStatus.Failed)
{
DeleteFiles(ini, dir, rawFile);
}
}
private void DeleteFiles([NotNull]string ini, [NotNull]string outputDir, [NotNull]string rawFile)
{
if (string.IsNullOrWhiteSpace(ini))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(ini));
}
if (string.IsNullOrWhiteSpace(outputDir))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(outputDir));
}
if (string.IsNullOrWhiteSpace(rawFile))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(rawFile));
}
var pdfFile = Path.Combine(outputDir, $"{Path.GetFileNameWithoutExtension(ini)}.pdf");
var tiffFile = Path.Combine(outputDir, $"{Path.GetFileNameWithoutExtension(ini)}.tif");
if (File.Exists(pdfFile) && !IsFileLocked(pdfFile))
{
File.Delete(pdfFile);
}
if (File.Exists(tiffFile) && !IsFileLocked(tiffFile))
{
File.Delete(tiffFile);
}
File.Delete(ini);
File.Delete(rawFile);
}
private bool IsFileLocked(string filePath)
{
try
{
using(var stream = File.Open(filePath, FileMode.Open, FileAccess.Read))
{
stream.Close();
}
return false;
}
catch (IOException)
{
return true;
}
}
[NotNull]
private IExConfig GetRegistryConfig()
{
return _registryRepository.GetRegistryConfig();
}
private bool IsJobValid([CanBeNull]IJob job)
{
if (job == null)
{
return false;
}
if (string.IsNullOrWhiteSpace(job.JobInfo.DomainName))
{
return false;
}
if (string.IsNullOrWhiteSpace(job.JobInfo.MachineName))
{
return false;
}
if (string.IsNullOrWhiteSpace(job.JobInfo.UserName))
{
return false;
}
if (string.IsNullOrWhiteSpace(job.SessionInfo.Sid))
{
return false;
}
return true;
}
private void ProcessFile(string filePath, string iniFile)
{
var thread = new Thread(obj =>
{
var tuple = (Tuple<string, string>) obj;
var rawFile = tuple.Item1;
var ini = tuple.Item2;
var job = _jobService.CreateJob(ini,
rawFile);
try
{
var userConfig = _registryRepository.GetUserRegistryConfig(job.SessionInfo.Sid);
_jobProcessor.Process(job, userConfig);
LogDebug($"Converted '{rawFile}'!");
_jobService.Finish(job);
}
catch (Exception exception)
{
LogError(exception,
"Failed to process file. Job: {@job}",
job);
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start(Tuple.Create(filePath, iniFile));
}
private void LogError(Exception exception, string message, params object[] args)
{
_logger.Error(exception, message, args);
}
private void LogError(string message, params object[] args)
{
_logger.Error(message, args);
}
private void LogDebug(string message, params object[] args)
{
_logger.Debug(message, args);
}
}
}

View file

@ -0,0 +1,22 @@
using System;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core.Interfaces;
namespace VirtualPrinter.Agent.Lib.Model
{
public class ProgressUpdateArgs : EventArgs
{
public ProgressUpdateArgs([NotNull] IJob job, uint val)
{
Job = job;
Value = val;
}
[NotNull]
public IJob Job { get; }
public uint Value { get; }
}
}

View file

@ -0,0 +1,32 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("VirtualPrinter.Agent.Lib")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("3d3f946d-c8dc-4518-b464-75e73cbad734")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{94E8105F-5001-403B-B9F1-B0B0B236AD65}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>VirtualPrinter.Agent.Lib</RootNamespace>
<AssemblyName>VirtualPrinter.Agent.Lib</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Files\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Compile Include="Misc\GhostScriptConverter.cs" />
<Compile Include="Misc\JobProcessor.cs" />
<Compile Include="Misc\JobRedirector.cs" />
<Compile Include="Misc\Job.cs" />
<Compile Include="Misc\JobFactory.cs" />
<Compile Include="Misc\JobService.cs" />
<Compile Include="Misc\VirtualTcpInputPrinter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VirtualPrinterService.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Printing" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Agent\VirtualPrinter.Delivery\VirtualPrinter.Delivery.csproj">
<Project>{74fa80b3-7cf1-4b68-8aa3-4c3d37bbe855}</Project>
<Name>VirtualPrinter.Delivery</Name>
</ProjectReference>
<ProjectReference Include="..\..\Installer\VirtualPrinter.SetupDriver\VirtualPrinter.SetupDriver.csproj">
<Project>{12402f90-a2ae-4549-9142-f90650e2082a}</Project>
<Name>VirtualPrinter.SetupDriver</Name>
</ProjectReference>
<ProjectReference Include="..\..\UI\VirtualPrinter.ProgressInfo.Core\VirtualPrinter.ProgressInfo.Core.csproj">
<Project>{24d28558-c825-43e6-85d2-7c59f4a97698}</Project>
<Name>VirtualPrinter.ProgressInfo.Core</Name>
</ProjectReference>
<ProjectReference Include="..\VirtualPrinter.Agent.Core\VirtualPrinter.Agent.Core.csproj">
<Project>{135c85eb-2116-4cc4-8ccb-b6804b9d6467}</Project>
<Name>VirtualPrinter.Agent.Core</Name>
</ProjectReference>
<ProjectReference Include="..\VirtualPrinter.Logging\VirtualPrinter.Logging.csproj">
<Project>{AA25364D-22D5-44B0-86A5-6FB14C686308}</Project>
<Name>VirtualPrinter.Logging</Name>
</ProjectReference>
<ProjectReference Include="..\VirtualPrinter.Utils\VirtualPrinter.Utils.csproj">
<Project>{cd1c8e9d-5335-41ac-b0c0-88fd7c7c55f3}</Project>
<Name>VirtualPrinter.Utils</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.0.0" />
<PackageReference Include="Cassia" Version="3.0.0-alpha.9" />
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,27 @@
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core;
namespace VirtualPrinter.Agent.Lib
{
public class VirtualPrinterService : IVirtualPrinterService
{
[NotNull]
private readonly IVirtualPrinter _printer;
public VirtualPrinterService([NotNull]IVirtualPrinter printer)
{
_printer = printer;
}
public void Start()
{
_printer.Init();
}
public void Stop()
{
_printer.Dispose();
}
}
}

View file

@ -0,0 +1,81 @@
using System;
using JetBrains.Annotations;
namespace VirtualPrinter.Logging
{
// ReSharper disable once UnusedTypeParameter
public interface IVirtualPrinterLogger<out T> : IVirtualPrinterLogger
{ }
public interface IVirtualPrinterLogger
{
/// <summary>
/// Gets the name of the logger.
/// </summary>
string Name { get; }
/// <summary>
/// A value of true if logging is enabled for the Debug level, otherwise it returns false.
/// </summary>
bool IsDebugEnabled { get; }
/// <summary>
/// A value of true if logging is enabled for the Trace level, otherwise it returns false.
/// </summary>
bool IsTraceEnabled { get; }
/// <summary>
/// Writes the exception at the <c>Error</c> level.
/// </summary>
/// <param name="exception">An exception to be logged.</param>
void Error([CanBeNull]Exception exception);
/// <summary>
/// Writes the diagnostic message and exception at the <c>Error</c> level.
/// </summary>
/// <param name="exception">An exception to be logged.</param>
/// <param name="message">A <see langword="string" /> to be written.</param>
/// <param name="args">Arguments to format.</param>
void Error([CanBeNull]Exception exception, [CanBeNull]string message, [CanBeNull, ItemCanBeNull]params object[] args);
/// <summary>
/// Something failed; application may or may not continue
/// Writes the diagnostic message and exception at the <c>Error</c> level.
/// </summary>
/// <param name="message">A <see langword="string" /> to be written.</param>
/// <param name="args">Arguments to format.</param>
void Error([CanBeNull]string message, [CanBeNull, ItemCanBeNull]params object[] args);
/// <summary>
/// Something unexpected; application will continue
/// Writes the diagnostic message at the <c>Warn</c> level using the specified parameters.
/// </summary>
/// <param name="message">A <see langword="string" /> containing format items.</param>
/// <param name="args">Arguments to format.</param>
void Warn([CanBeNull]string message, [CanBeNull, ItemCanBeNull]params object[] args);
/// <summary>
/// Normal behavior like mail sent, user updated profile etc.
/// Writes the diagnostic message at the <c>Info</c> level using the specified parameters.
/// </summary>
/// <param name="message">A <see langword="string" /> containing format items.</param>
/// <param name="args">Arguments to format.</param>
void Info([CanBeNull]string message, [CanBeNull, ItemCanBeNull]params object[] args);
/// <summary>
/// For debugging; executed query, user authenticated, session expired
/// Writes the diagnostic message at the <c>Debug</c> level using the specified parameters.
/// </summary>
/// <param name="message">A <see langword="string" /> containing format items.</param>
/// <param name="args">Arguments to format.</param>
void Debug([CanBeNull]string message, [CanBeNull, ItemCanBeNull]params object[] args);
/// <summary>
/// Writes the diagnostic message at the <c>Trace</c> level using the specified parameters.
/// </summary>
/// <param name="message">A <see langword="string" /> containing format items.</param>
/// <param name="args">Arguments to format.</param>
void Trace([CanBeNull]string message, [CanBeNull, ItemCanBeNull]params object[] args);
}
}

View file

@ -0,0 +1,17 @@
using Autofac;
using Autofac.Extras.NLog;
namespace VirtualPrinter.Logging
{
public class LoggerModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterModule<NLogModule>();
builder.RegisterInstance(NLog.LogManager.GetCurrentClassLogger()).As<NLog.ILogger>().SingleInstance();
builder.RegisterGeneric(typeof(VirtualPrinterLogger<>))
.As(typeof(IVirtualPrinterLogger<>))
.SingleInstance();
}
}
}

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
<variable name="log_title" value="${processname}"/>
<targets>
<target name="console" xsi:type="ColoredConsole" layout="${longdate} ${level} ${message} ${exception:format=ToString}"/>
<target xsi:type="EventLog"
name="eventlog"
log ="Application"
source="${var:log_title}"
layout="${message}${newline}${exception:format=ToString}">
</target>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="console" />
<logger name="*" minlevel="Info" writeTo="eventlog" />
</rules>
</nlog>

View file

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("VirtualPrinter.Logging")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("aa25364d-22d5-44b0-86a5-6fb14c686308")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{AA25364D-22D5-44B0-86A5-6FB14C686308}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VirtualPrinter.Logging</RootNamespace>
<AssemblyName>VirtualPrinter.Logging</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Files</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="IVirtualPrinterLogger.cs" />
<Compile Include="LoggerModule.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VirtualPrinterLogger.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="NLog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.0.0" />
<PackageReference Include="Autofac.Extras.NLog" Version="4.0.0" />
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="NLog" Version="4.6.1" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,103 @@
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using JetBrains.Annotations;
using NLog;
namespace VirtualPrinter.Logging
{
public class VirtualPrinterLogger<T> : VirtualPrinterLogger, IVirtualPrinterLogger<T>
{
public VirtualPrinterLogger() : base(ReadableTypeName.Generate(typeof(T)))
{
}
}
public static class ReadableTypeName
{
[NotNull]
public static string Generate([NotNull]Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var provider = CodeDomProvider.CreateProvider("C#");
var typeReferenceExpression = new CodeTypeReferenceExpression(type);
using (var writer = new StringWriter())
{
provider.GenerateCodeFromExpression(typeReferenceExpression, writer, new CodeGeneratorOptions());
return writer.GetStringBuilder().ToString();
}
}
}
public class VirtualPrinterLogger : IVirtualPrinterLogger
{
private readonly ILogger _logger;
public VirtualPrinterLogger([NotNull]string loggerName)
{
if (string.IsNullOrWhiteSpace(loggerName))
{
throw new ArgumentNullException(nameof(loggerName));
}
_logger = LogManager.GetLogger(loggerName);
}
public string Name
{
get { return _logger.Name; }
}
public bool IsDebugEnabled
{
get { return _logger.IsDebugEnabled; }
}
public bool IsTraceEnabled
{
get { return _logger.IsTraceEnabled; }
}
public void Error(Exception exception)
{
_logger.Error(exception);
}
public void Error(Exception exception, string message, params object[] args)
{
_logger.Error(exception, message, args);
}
public void Error(string message, params object[] args)
{
_logger.Error(message, args);
}
public void Warn(string message, params object[] args)
{
_logger.Warn(message, args);
}
public void Info(string message, params object[] args)
{
_logger.Info(message, args);
}
public void Debug(string message, params object[] args)
{
_logger.Debug(message, args);
}
public void Trace(string message, params object[] args)
{
_logger.Trace(message, args);
}
}
}

View file

@ -0,0 +1,51 @@
namespace VirtualPrinter.Utils
{
public struct Files
{
public const string FILES = @"Files";
public const string PRINTER_SERVICE_EXE = "VPDAgent.exe";
public const string SETUP_DRIVER_EXE = "setupdrv.exe";
public const string AGENT_PROGRESS_EXE = "VPDAgentProgress.exe";
public const string DILIVERY_EXE = "delivery.exe";
public const string LICENCE_FILE = "";
public const string PRE_CONVERTER = @"C:\Program Files (x86)\MyPreConverter.exe ARG";
public const string POST_CONVERTER = @"C:\Program Files (x86)\MyPostConverter.exe ARG";
}
public struct Keys
{
public const string PRINTER_DRIVER_KEY32 = @"SOFTWARE\vpd\PrinterDriver";
public const string PRINTER_DRIVER_KEY64 = @"SOFTWARE\Wow6432Node\vpd\PrinterDriver";
public const string POSTCONVERTER_KEY = @"Application\Postconverter";
public const string PRECONVERTER_KEY = @"Application\Preconverter";
public const string CONVERTER_KEY = @"Converter";
public const string CONVERTER_PDF_KEY = CONVERTER_KEY + @"\PDF";
public const string CONVERTER_TIFF_KEY = CONVERTER_KEY + @"\TIFF";
public const string CONVERTER_REDIRECT_KEY = CONVERTER_KEY + @"\Redirect";
}
public struct KeyNames
{
public const string EXECUTABLE_FILE = "Executable File";
public const string INSTALLATION_DIR = "Installation Directory";
public const string THREADS = "Threads";
public const string SHOW_PROGRESS = "Show Progress";
public const string PAGES_PER_SHEET = "Pages per Sheet";
public const string FILE_NAME_MASK = "File name mask";
public const string OUTPUT_DIR = "Output Directory";
public const string ENABLED = "Enabled";
public const string MULTIPAGE = "Multipage";
public const string PRODUCE_PDFA = "Produce PDFA";
public const string ALLOW_PRINTING = "Allow printing";
public const string ALLOW_COPYING = "Allow printing";
public const string SUBSETTING = "Subsetting";
public const string QUALITY = "Image Quality";
public const string BITS_PIXEL = "Bits per pixel";
public const string COMPRESSION = "Compression";
public const string SERVER_PORT = "Server port";
public const string RENDER_DPI = "RENDER: DPI";
public const string FORMAT = "Intermediate Format";
public const string PRINT_FORMAT = "Format";
public const string PRINTER = "Printer";
}
}

View file

@ -0,0 +1,24 @@
using System.IO;
using VirtualPrinter.Agent.Core;
namespace VirtualPrinter.Utils
{
public class DirectoryHelper : IDirectoryHelper
{
public string GetOutputDirectory(IExConfig config)
{
if (string.IsNullOrWhiteSpace(config.ResolvedOutputDirectory))
{
var outputDir = Path.Combine(Path.GetTempPath(), "PrinterOutput");
if (!Directory.Exists(outputDir))
{
Directory.CreateDirectory(outputDir);
}
return outputDir;
}
return config.ResolvedOutputDirectory;
}
}
}

View file

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("VirtualPrinter.Utils")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("cd1c8e9d-5335-41ac-b0c0-88fd7c7c55f3")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,170 @@
using System;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Win32;
using VirtualPrinter.Agent.Core;
using VirtualPrinter.Agent.Core.Enums;
namespace VirtualPrinter.Utils
{
public class RegistryRepository : IRegistryRepository
{
private const short DefaultServerPort = 9101;
public bool TryGetGhostscriptPath(out string path)
{
path = null;
try
{
var regView = GetRegistryView();
using(var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, regView))
{
const string gsSubKey = @"SOFTWARE\GPL Ghostscript";
using(var ghostscript = baseKey.OpenSubKey(gsSubKey))
{
CheckForNull(ghostscript, gsSubKey);
var subKeys = ghostscript.GetSubKeyNames();
var lastSubKey = subKeys.Last();
using(var subKey = ghostscript.OpenSubKey(lastSubKey))
{
CheckForNull(subKey, lastSubKey);
path = subKey.GetValue("GS_LIB").ToString().Split(';').FirstOrDefault();
if (string.IsNullOrWhiteSpace(path))
{
return false;
}
path = Directory.GetParent(path).FullName;
return true;
}
}
}
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
return false;
}
public IExConfig GetRegistryConfig()
{
var regView = GetRegistryView();
var subKey = GetSubKey();
var registryConfig = new RegistryConfig();
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, regView))
{
using (var driver = baseKey.OpenSubKey(subKey))
{
CheckForNull(driver, subKey);
using(var key = driver.OpenSubKey(Keys.POSTCONVERTER_KEY))
{
CheckForNull(key, Keys.POSTCONVERTER_KEY);
registryConfig.Postconverter = key.GetValue(KeyNames.EXECUTABLE_FILE).ToString();
}
using(var key = driver.OpenSubKey(Keys.PRECONVERTER_KEY))
{
CheckForNull(key, Keys.PRECONVERTER_KEY);
registryConfig.Preconverter = key.GetValue(KeyNames.EXECUTABLE_FILE).ToString();
}
using (var key = driver.OpenSubKey(Keys.CONVERTER_KEY))
{
CheckForNull(key, Keys.CONVERTER_KEY);
registryConfig.OutputDirectory = key.GetValue(KeyNames.OUTPUT_DIR).ToString();
registryConfig.FileNameMask = key.GetValue(KeyNames.FILE_NAME_MASK).ToString();
var portStr = key.GetValue(KeyNames.SERVER_PORT).ToString();
registryConfig.PrinterPort = short.TryParse(portStr, out var portVal) ? portVal : DefaultServerPort;
registryConfig.IntermediateFormat = key.GetValue(KeyNames.FORMAT)?.ToString().ToLower() == "ps" ? IntermediateFormat.Ps : IntermediateFormat.Xps;
}
}
}
return registryConfig;
}
[ContractAnnotation("key:null => void")]
private void CheckForNull(RegistryKey key, string keyName)
{
if (key == null)
{
throw new NullReferenceException(keyName);
}
}
public IUserConfig GetUserRegistryConfig(string sid)
{
var regView = GetRegistryView();
var userConfig = new UserRegistryConfig();
using (var users = RegistryKey.OpenBaseKey(RegistryHive.Users, regView))
{
var subKey = $@"{sid}\{Keys.PRINTER_DRIVER_KEY32}";
using (var driver = users.OpenSubKey(subKey))
{
CheckForNull(driver, subKey);
using (var converter = driver.OpenSubKey(Keys.CONVERTER_KEY))
{
CheckForNull(converter, Keys.CONVERTER_KEY);
subKey = "Redirect";
using (var redirect = converter.OpenSubKey(subKey))
{
CheckForNull(redirect, subKey);
userConfig.RedirectEnabled = (int?) redirect.GetValue("Enabled") == 1;
userConfig.RedirectPrinter = redirect.GetValue("Printer").ToString();
userConfig.Format = converter.GetValue("Format").ToString();
var dpiStr = (string)driver.GetValue("RENDER: DPI");
if (dpiStr == null)
{
userConfig.UserRenderDpi = null;
}
else
{
userConfig.UserRenderDpi = double.TryParse(dpiStr, out var dpiVal) ? dpiVal : (double?) null;
}
}
}
}
}
return userConfig;
}
private RegistryView GetRegistryView()
{
if (Environment.Is64BitOperatingSystem)
{
return RegistryView.Registry64;
}
return RegistryView.Registry32;
}
[NotNull]
private string GetSubKey()
{
if (Environment.Is64BitOperatingSystem)
{
return Keys.PRINTER_DRIVER_KEY64;
}
return Keys.PRINTER_DRIVER_KEY32;
}
}
}

View file

@ -0,0 +1,95 @@
using System;
using System.IO;
using System.Text;
using System.Threading;
using JetBrains.Annotations;
using VirtualPrinter.Agent.Core;
using VirtualPrinter.Logging;
namespace VirtualPrinter.Utils
{
public class Shell : IShell
{
private readonly IVirtualPrinterLogger<Shell> _logger = new VirtualPrinterLogger<Shell>();
public void WriteIniEntry(string section, string key, string value, string iniFilePath)
{
Win32Sys.WritePrivateProfileString(section, key, value, iniFilePath);
}
public T ReadIniEntry<T>(string section, string key, string iniFilePath)
{
var buffer = new StringBuilder(64 * 1024);
Win32Sys.GetPrivateProfileString(section, key, string.Empty, buffer, buffer.Capacity, iniFilePath);
var value = buffer.ToString().Trim();
return (T)Convert.ChangeType(value, typeof(T));
}
public void Execute(IJobInfo job, ISessionInfo session, string exe, string args)
{
var thr = new Thread
(
() =>
{
try
{
if (!File.Exists(exe))
throw new FileNotFoundException(exe);
#if DEBUG
System.Diagnostics.Process.Start(exe, args);
#else
StartProcessAsUser(job, session, exe, args);
#endif
}
catch (Exception exception)
{
LogError(exception, "Failed to create process");
}
}
);
thr.Start();
}
public bool FileExists(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException("The path may not be null or empty.");
}
return File.Exists(path);
}
private void StartProcessAsUser([NotNull]IJobInfo job, [NotNull]ISessionInfo session, string exe, string args)
{
if (job == null)
{
throw new ArgumentNullException(nameof(job));
}
if (session == null)
{
throw new ArgumentNullException(nameof(session));
}
var cmd = $@"""{exe}"" {args}";
var id = session.Id;
var user = job.MachineName.TrimStart('\\') + '\\' + job.UserName;
LogDebug($"Executing '{cmd}' for '{user}' ({id})...");
Win32Sys.CreateProcessAsUser(id, user, cmd);
}
private void LogDebug(string message, params object[] args)
{
_logger.Debug(message, args);
}
private void LogError(Exception exception, string message, params object[] args)
{
_logger.Error(exception, message, args);
}
}
}

View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{CD1C8E9D-5335-41AC-B0C0-88FD7C7C55F3}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VirtualPrinter.Utils</RootNamespace>
<AssemblyName>VirtualPrinter.Utils</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Files\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Consts.cs" />
<Compile Include="DirectoryHelper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RegistryRepository.cs" />
<Compile Include="Shell.cs" />
<Compile Include="Win32Sys.cs" />
<Compile Include="Windows.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VirtualPrinter.Agent.Core\VirtualPrinter.Agent.Core.csproj">
<Project>{135C85EB-2116-4CC4-8CCB-B6804B9D6467}</Project>
<Name>VirtualPrinter.Agent.Core</Name>
</ProjectReference>
<ProjectReference Include="..\VirtualPrinter.Logging\VirtualPrinter.Logging.csproj">
<Project>{AA25364D-22D5-44B0-86A5-6FB14C686308}</Project>
<Name>VirtualPrinter.Logging</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Cassia" Version="3.0.0-alpha.9" />
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
<PackageReference Include="System.Security.Principal.Windows" Version="4.7.0" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,239 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using JetBrains.Annotations;
using Microsoft.Win32.SafeHandles;
namespace VirtualPrinter.Utils
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Global")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
internal static class Win32Sys
{
/// <summary>
/// Creates process with given command line via Win32 API.
/// Use this method to start a process as a service.
/// </summary>
/// <param name="sessId"></param>
/// <param name="user"></param>
/// <param name="commandLine"></param>
internal static void CreateProcessAsUser(int sessId, string user, string commandLine)
{
WTSQueryUserToken((uint) sessId, out var userToken);
if (userToken.IsInvalid)
{
throw new InvalidOperationException($"Could not query user token for session {sessId}! (" + GetLastError() + ")",
Windows.LastError);
}
CreateProcessAsUser(userToken, user, commandLine);
}
private static void CreateProcessAsUser([NotNull]SafeTokenHandle token, string user, string commandLine)
{
var processInformation = new PROCESS_INFORMATION();
try
{
var securityAttributes = new SECURITY_ATTRIBUTES();
securityAttributes.Length = Marshal.SizeOf(securityAttributes);
var startupInfo = new STARTUPINFO();
startupInfo.cb = Marshal.SizeOf(startupInfo);
startupInfo.lpDesktop = "winsta0\\default";
var info = new ProfileInfo();
info.dwSize = Marshal.SizeOf(info);
info.lpUserName = user;
info.dwFlags = 1;
var result = LoadUserProfile(token, ref info);
if (!result)
{
throw Windows.LastError;
}
result = CreateEnvironmentBlock(out var lpEnvironment, token, false);
if (!result)
{
throw Windows.LastError;
}
result = CreateProcessAsUser
(
token,
null,
commandLine,
ref securityAttributes,
ref securityAttributes,
false,
0x00000400,
lpEnvironment,
null,
ref startupInfo,
ref processInformation
);
if (!result)
{
throw Windows.LastError;
}
}
finally
{
if (processInformation.hProcess != IntPtr.Zero)
{
CloseMyHandle(processInformation.hProcess);
}
if (processInformation.hThread != IntPtr.Zero)
{
CloseMyHandle(processInformation.hThread);
}
if (!token.IsInvalid)
{
token.Dispose();
}
}
}
#region Classes
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle() : base(true)
{
}
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
public override string ToString()
{
return $"{handle}";
}
}
#endregion
#region Structures
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessID;
public int dwThreadID;
}
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct ProfileInfo
{
public int dwSize;
public int dwFlags;
public string lpUserName;
public string lpProfilePath;
public string lpDefaultPath;
public string lpServerName;
public string lpPolicyPath;
public IntPtr hProfile;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
#endregion
#region Windows interop
[DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool LoadUserProfile(SafeTokenHandle hToken, ref ProfileInfo lpProfileInfo);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment,
SafeTokenHandle hToken, bool bInherit);
[DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true,
CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern bool CloseMyHandle(IntPtr handle);
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true,
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern bool CreateProcessAsUser(SafeTokenHandle hToken,
string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle,
int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo, ref PROCESS_INFORMATION lpProcessInformation);
[DllImport("wtsapi32.dll", SetLastError = true)]
private static extern bool WTSQueryUserToken(uint sessionId, out SafeTokenHandle Token);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
[DllImport("kernel32")]
public static extern int GetPrivateProfileString
(
string section,
string key,
string def,
StringBuilder retVal,
int size,
string filePath
);
[DllImport("kernel32")]
public static extern long WritePrivateProfileString
(
string section,
string key,
string val,
string filePath
);
#endregion
}
}

View file

@ -0,0 +1,14 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
namespace VirtualPrinter.Utils
{
public class Windows
{
[NotNull]
public static Exception LastError => new Win32Exception(Marshal.GetLastWin32Error());
}
}

View file

@ -0,0 +1,15 @@
namespace VirtualPrinter.SetupDriver
{
public static class Defaults
{
/// <summary>
/// The name that appears in the Windows "Printer & Scanner" menu.
/// </summary>
public const string PrinterName = "AMAGNO";
/// <summary>
/// The printer port.
/// </summary>
public const string PrinterPort = "IP_VIRT_PRINTER";
}
}

View file

@ -0,0 +1,125 @@
using System;
using System.IO;
using JetBrains.Annotations;
using VirtualPrinter.Logging;
using VirtualPrinter.Utils;
using static VirtualPrinter.SetupDriver.Windows;
using static VirtualPrinter.SetupDriver.Defaults;
namespace VirtualPrinter.SetupDriver
{
internal class Program
{
private const string InstallCmd = "install";
private const string TestCmd = "test";
private const string ConfigCmd = "config";
private const string UninstallCmd = "uninstall";
private static readonly IVirtualPrinterLogger<Program> Logger = new VirtualPrinterLogger<Program>();
private static void Main([CanBeNull]string[] args)
{
if (args == null || args.Length < 1)
{
NotUseful();
return;
}
switch (args[0])
{
case InstallCmd:
{
if (args.Length < 2)
{
NotUseful();
return;
}
switch (args[1].ToLower())
{
case "xps":
try
{
AddPrinterPort(PrinterPort, "127.0.0.1", 9101);
var isWin7 = Environment.OSVersion.VersionString.Contains("NT 6.1.");
AddPrinter(PrinterName, "Microsoft XPS Document Writer" + (isWin7 ? string.Empty : " v4"), PrinterPort);
}
catch (Exception exception)
{
LogError(exception, "Failed to add xps printer.");
}
break;
case "ps":
try
{
AddPrinterPort(PrinterPort, "127.0.0.1", 9101);
new RegistryRepository().TryGetGhostscriptPath(out var ghostScriptPath);
if (ghostScriptPath == null)
{
throw new ArgumentNullException(nameof(ghostScriptPath), "Ghostscript path could not be found.");
}
AddPrinter(PrinterName, "Ghostscript PDF", PrinterPort, Path.Combine(ghostScriptPath, @"lib\ghostpdf.inf"));
}
catch (Exception exception)
{
LogError(exception, "Failed to add ps printer.");
}
break;
default:
NotUseful();
return;
}
break;
}
case TestCmd: {
TestPrinter(PrinterName);
break;
}
case ConfigCmd: {
try
{
ConfigPrinter(PrinterName);
}
catch (Exception exception)
{
LogError(exception, "Failed to configurate printer {printerName}.", PrinterName);
}
break;
}
case UninstallCmd: {
try
{
DelPrinter(PrinterName);
DelPrinterPort(PrinterPort);
}
catch (Exception exception)
{
LogError(exception, "Failed to uninstall {printerName} on port {printerPort}.", PrinterName, PrinterPort);
}
break;
}
default: {
NotUseful();
break;
}
}
}
private static void NotUseful()
{
var exception = new ArgumentNullException($"Use '{InstallCmd} [PS|XPS]', '{TestCmd}' or '{UninstallCmd}'!");
LogError(exception, "Failed to handle the arguments.");
throw exception;
}
private static void LogError(Exception exception, string message, params object[] args)
{
Logger.Error(exception, message, args);
}
}
}

View file

@ -0,0 +1,21 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("VirtualPrinter.SetupDriver")]
[assembly: AssemblyDescription("The setup for the virtual printer driver")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("12402f90-a2ae-4549-9142-f90650e2082a")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: InternalsVisibleTo("properties")]

View file

@ -0,0 +1,84 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using VirtualPrinter.Logging;
namespace VirtualPrinter.SetupDriver
{
internal class Shell
{
private static readonly string WinFolder;
private static readonly IVirtualPrinterLogger<Shell> Logger = new VirtualPrinterLogger<Shell>();
static Shell()
{
WinFolder = Environment.GetEnvironmentVariable("windir") ?? @"C:\Windows";
}
[NotNull]
internal static string GetPrintInf()
{
try
{
var winFolder = Environment.GetEnvironmentVariable("windir") ?? @"C:\Windows";
return Path.Combine(winFolder, "inf", "ntprint.inf");
}
catch (Exception exception)
{
LogError(exception, "Cannot get PrintInf");
throw;
}
}
[NotNull]
internal static string GetPrintVbs()
{
try
{
var printScripts = Path.Combine(WinFolder, "System32", "Printing_Admin_Scripts");
return Directory.GetFiles(printScripts, "*port.vbs", SearchOption.AllDirectories).First();
}
catch (Exception exception)
{
LogError(exception, "Cannot get PrintVbs");
throw;
}
}
internal static void Execute([NotNull]string exe, [NotNull]string args)
{
if (exe == null)
{
throw new ArgumentNullException(nameof(exe));
}
if (args == null)
{
throw new ArgumentNullException(nameof(args));
}
try
{
Console.WriteLine(exe + " " + args);
using(var proc = Process.Start(exe, args))
{
proc?.WaitForExit();
}
}
catch (Exception exception)
{
LogError(exception, "Cannot execute {exe} with the following args: {args}", exe, args);
}
}
private static void LogError(Exception exception, string message, params object[] args)
{
Logger.Error(exception, message, args);
}
}
}

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{12402F90-A2AE-4549-9142-F90650E2082A}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>VirtualPrinter.SetupDriver</RootNamespace>
<AssemblyName>setupdrv</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Files\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject>VirtualPrinter.SetupDriver.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
</ItemGroup>
<ItemGroup>
<Compile Include="Defaults.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Shell.cs" />
<Compile Include="Windows.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\VirtualPrinter.Logging\VirtualPrinter.Logging.csproj">
<Project>{aa25364d-22d5-44b0-86a5-6fb14c686308}</Project>
<Name>VirtualPrinter.Logging</Name>
</ProjectReference>
<ProjectReference Include="..\..\Common\VirtualPrinter.Utils\VirtualPrinter.Utils.csproj">
<Project>{cd1c8e9d-5335-41ac-b0c0-88fd7c7c55f3}</Project>
<Name>VirtualPrinter.Utils</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,93 @@
using System;
using JetBrains.Annotations;
namespace VirtualPrinter.SetupDriver
{
internal static class Windows
{
public static void AddPrinter([NotNull]string name, [NotNull]string model, [NotNull]string port, [CanBeNull]string driver = null)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (model == null)
{
throw new ArgumentNullException(nameof(model));
}
if (port == null)
{
throw new ArgumentNullException(nameof(port));
}
driver = driver ?? Shell.GetPrintInf();
var args = $@"printui.dll,PrintUIEntry /if /b ""{name}"" /f ""{driver}"" /r ""{port}"" /m ""{model}"" /u";
Shell.Execute("rundll32", args);
}
public static void TestPrinter([NotNull]string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
var args = $@"printui.dll,PrintUIEntry /k /n ""{name}""";
Shell.Execute("rundll32", args);
}
public static void ConfigPrinter([NotNull]string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
var args = $@"printui.dll,PrintUIEntry /e /n ""{name}""";
Shell.Execute("rundll32", args);
}
public static void DelPrinter([NotNull]string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
var args = $@"printui.dll,PrintUIEntry /dl /n ""{name}""";
Shell.Execute("rundll32", args);
}
public static void AddPrinterPort([NotNull]string name, [NotNull]string ip, int port, [CanBeNull]string vbs = null)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (ip == null)
{
throw new ArgumentNullException(nameof(ip));
}
vbs = vbs ?? Shell.GetPrintVbs();
var args = $@"""{vbs}"" -a -r {name} -h {ip} -o raw -n {port}";
Shell.Execute("cscript", args);
}
public static void DelPrinterPort([NotNull]string name, [CanBeNull]string vbs = null)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
vbs = vbs ?? Shell.GetPrintVbs();
var args = $@"""{vbs}"" -d -r {name}";
Shell.Execute("cscript", args);
}
}
}

View file

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("VirtualPrinter.WixSharpInstaller")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a668846e-54c7-483d-9cf7-48f77ea398ca")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
// ReSharper disable once RedundantUsingDirective
using System.Diagnostics;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using VirtualPrinter.Utils;
using WixSharp;
using Action = WixSharp.Action;
using File = WixSharp.File;
using Files = VirtualPrinter.Utils.Files;
using RegistryHive = WixSharp.RegistryHive;
namespace VirtualPrinter.WixSharpInstaller
{
public class Script
{
private static string _filesDir;
const string SetupDriverId = "setupdrv_exe";
public static void Main([NotNull]string[] args)
{
var workingDir = "";
if (args.Length > 0)
{
foreach(var cmd in args)
{
if (cmd.Contains(@"/p:"))
{
workingDir = cmd.Remove(0, 3);
Console.WriteLine(workingDir);
}
}
}
if (workingDir.IsNullOrEmpty())
{
throw new ArgumentException("Argument for working directory (/p) not set.");
}
_filesDir = Path.Combine(workingDir, Files.FILES);
var feature = new Feature("VPD");
var printerServiceFile = new File(feature, Path.Combine(_filesDir, Files.PRINTER_SERVICE_EXE))
{
ServiceInstaller = new ServiceInstaller
{
Name = "VirtualPrinterService",
StartOn = SvcEvent.Install_Wait,
StopOn = SvcEvent.InstallUninstall_Wait,
RemoveOn = SvcEvent.Uninstall_Wait,
ErrorControl = SvcErrorControl.normal,
ConfigureServiceTrigger = ConfigureServiceTrigger.None
}
};
var project = new ManagedProject("VPDInstaller")
{
Name = "Virtual Printer Driver",
GUID = new Guid("8712D2CD-A9F6-456F-99C8-92C2BB070596"),
UpgradeCode = new Guid("0B37A935-EDEC-4ACA-9307-6D8299496C1D"),
UI = WUI.WixUI_InstallDir,
Version = Version.Parse(FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion),
LicenceFile = Files.LICENCE_FILE,
Dirs = CreateProjectDirs(feature, printerServiceFile),
Actions = CreateActions(),
RegValues = CreateRegValues(feature).ToArray(),
InstallPrivileges = InstallPrivileges.elevated
};
project.BuildMsi();
}
[NotNull, ItemNotNull]
private static Dir[] CreateProjectDirs
(
Feature feature,
File printerServiceFile
)
{
return new[]
{
new Dir
(
@"%ProgramFiles%\MyPrinterDriver\",
new DirFiles(feature, _filesDir + @"\*", s => !s.EndsWith(".exe")),
new File(new Id(SetupDriverId), feature, Path.Combine(_filesDir, Files.SETUP_DRIVER_EXE)),
new File(feature, Path.Combine(_filesDir, Files.DILIVERY_EXE)),
new File(feature, Path.Combine(_filesDir, Files.AGENT_PROGRESS_EXE)),
printerServiceFile
)
};
}
[NotNull, ItemNotNull]
private static Action[] CreateActions()
{
return new Action[]
{
new InstalledFileAction(SetupDriverId, "install ps", Return.check, When.After, Step.InstallFinalize, Condition.NOT_Installed),
new InstalledFileAction(SetupDriverId, "uninstall", Return.check, When.Before, Step.RemoveFiles, Condition.BeingUninstalled)
};
}
[NotNull]
private static IEnumerable<RegValue> CreateRegValues(Feature feature)
{
var converterKey = $@"{Keys.PRINTER_DRIVER_KEY32}\{Keys.CONVERTER_KEY}";
var regValues = new List<RegValue>();
regValues.AddRange(CreateLocalMachineValues(feature, converterKey));
regValues.AddRange(CreateCurrentUserValues(feature, converterKey));
return regValues;
}
[NotNull]
private static IEnumerable<RegValue> CreateLocalMachineValues(Feature feature, string converterKey)
{
var postConverterKey = $@"{Keys.PRINTER_DRIVER_KEY32}\{Keys.POSTCONVERTER_KEY}";
var preConverterKey = $@"{Keys.PRINTER_DRIVER_KEY32}\{Keys.PRECONVERTER_KEY}";
var converterPdfKey = $@"{Keys.PRINTER_DRIVER_KEY32}\{Keys.CONVERTER_PDF_KEY}";
var converterTiffKey = $@"{Keys.PRINTER_DRIVER_KEY32}\{Keys.CONVERTER_TIFF_KEY}";
var registryHive = RegistryHive.LocalMachine;
return new List<RegValue>
{
new RegValue(feature, registryHive, Keys.PRINTER_DRIVER_KEY32, KeyNames.INSTALLATION_DIR, "[INSTALLDIR]"),
new RegValue(feature, registryHive, postConverterKey, KeyNames.EXECUTABLE_FILE, Files.POST_CONVERTER),
new RegValue(feature, registryHive, preConverterKey, KeyNames.EXECUTABLE_FILE, Files.PRE_CONVERTER),
new RegValue(feature, registryHive, converterKey, KeyNames.SERVER_PORT, 9101) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, converterKey, KeyNames.THREADS, 2),
new RegValue(feature, registryHive, converterKey, KeyNames.SHOW_PROGRESS, 1) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, converterKey, KeyNames.PAGES_PER_SHEET, 1),
new RegValue(feature, registryHive, converterKey, KeyNames.FILE_NAME_MASK, "{yyyy}{MM}{DD}{hh}{mm}{ss}{job05}{page03}"),
new RegValue(feature, registryHive, converterKey, KeyNames.OUTPUT_DIR, string.Empty),
new RegValue(feature, registryHive, converterKey, KeyNames.FORMAT, "ps"),
new RegValue(feature, registryHive, converterPdfKey, KeyNames.ENABLED, 1) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, converterPdfKey, KeyNames.MULTIPAGE, 1) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, converterPdfKey, KeyNames.PRODUCE_PDFA, 1) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, converterPdfKey, KeyNames.ALLOW_COPYING, 1) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, converterPdfKey, KeyNames.ALLOW_PRINTING, 1) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, converterPdfKey, KeyNames.SUBSETTING, 1) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, converterPdfKey,KeyNames.QUALITY , 80),
new RegValue(feature, registryHive, converterTiffKey, KeyNames.ENABLED, 1) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, converterTiffKey, KeyNames.BITS_PIXEL, 24),
new RegValue(feature, registryHive, converterTiffKey, KeyNames.MULTIPAGE, 1) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, converterTiffKey, KeyNames.COMPRESSION, 8)
};
}
[NotNull]
private static IEnumerable<RegValue> CreateCurrentUserValues(Feature feature, string converterKey)
{
var redirectKey = $@"{Keys.PRINTER_DRIVER_KEY32}\{Keys.CONVERTER_REDIRECT_KEY}";
var registryHive = RegistryHive.CurrentUser;
return new List<RegValue>
{
new RegValue(feature, registryHive, converterKey, KeyNames.PRINT_FORMAT, "PDF"),
new RegValue(feature, registryHive, converterKey, KeyNames.RENDER_DPI, 300) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, redirectKey, KeyNames.ENABLED, 1) {AttributesDefinition = "Type=integer"},
new RegValue(feature, registryHive, redirectKey, KeyNames.PRINTER, "")
};
}
}
}

View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A668846E-54C7-483D-9CF7-48F77EA398CA}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VirtualPrinter.WixSharpInstaller</RootNamespace>
<AssemblyName>VPDInstaller</AssemblyName>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Files\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject>VirtualPrinter.WixSharpInstaller.Script</StartupObject>
</PropertyGroup>
<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<ItemGroup>
<Compile Include="Script.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<None Include="wix\$(ProjectName).g.wxs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\VirtualPrinter.Utils\VirtualPrinter.Utils.csproj">
<Project>{cd1c8e9d-5335-41ac-b0c0-88fd7c7c55f3}</Project>
<Name>VirtualPrinter.Utils</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
<PackageReference Include="WixSharp.bin" Version="1.14.8" />
<PackageReference Include="WixSharp.wix.bin" Version="3.11.2" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- <Import Project="..\..\packages\WixSharp.bin\1.14.8\build\WixSharp.bin.targets" Condition="Exists('..\..\packages\WixSharp.bin\1.14.8\build\WixSharp.bin.targets')" />-->
<!-- <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">-->
<!-- <PropertyGroup>-->
<!-- <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>-->
<!-- </PropertyGroup>-->
<!-- <Error Condition="!Exists('..\..\packages\WixSharp.bin\1.14.8\build\WixSharp.bin.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixSharp.bin\1.14.8\build\WixSharp.bin.targets'))" />-->
<!-- <Error Condition="!Exists('..\..\packages\WixSharp\1.14.8\build\WixSharp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixSharp\1.14.8\build\WixSharp.targets'))" />-->
<!-- </Target>-->
</Project>

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/></startup></configuration>

7
NuGet.Config Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="repositoryPath" value=".\packages" />
<add key="globalPackagesFolder" value=".\packages" />
</config>
</configuration>

View file

@ -0,0 +1,23 @@
using Autofac;
using VirtualPrinter.ProgressInfo.Core;
using VirtualPrinter.ProgressInfo.Core.Message;
using VirtualPrinter.ProgressInfo.Lib;
using VirtualPrinter.ProgressInfo.Lib.Interfaces;
using VirtualPrinter.ProgressInfo.Lib.Message;
namespace VirtualPrinter.ProgressInfo.Autofac
{
public class ProgressInfoModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ProgressInfoBroker>().As<IProgressInfo>().SingleInstance();
builder.RegisterType<MessageFactory>().As<IMessageFactory>();
builder.RegisterType<Message>();
builder.RegisterType<ProgressInfoServerFactory>().As<IProgressInfoServerFactory>();
builder.RegisterType<ProgressInfoProcessManager>().As<IProgressInfoProcessManager>();
builder.RegisterType<ProgressInfoServer>().As<IProgressInfoServer>();
}
}
}

View file

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("VirtualPrinter.ProgressInfo.Autofac")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("17e2cf8a-462c-4130-9faf-f1ca5fc4e06d")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
// indem Sie "*" wie unten gezeigt eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{17E2CF8A-462C-4130-9FAF-F1CA5FC4E06D}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VirtualPrinter.ProgressInfo.Autofac</RootNamespace>
<AssemblyName>VirtualPrinter.ProgressInfo.Autofac</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\Files\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
</ItemGroup>
<ItemGroup>
<Compile Include="ProgressInfoModule.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VirtualPrinter.ProgressInfo.Core\VirtualPrinter.ProgressInfo.Core.csproj">
<Project>{24D28558-C825-43E6-85D2-7C59F4A97698}</Project>
<Name>VirtualPrinter.ProgressInfo.Core</Name>
</ProjectReference>
<ProjectReference Include="..\VirtualPrinter.ProgressInfo.Lib\VirtualPrinter.ProgressInfo.Lib.csproj">
<Project>{d66f55e5-b3f7-4c61-a4f2-b55c4d412e01}</Project>
<Name>VirtualPrinter.ProgressInfo.Lib</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.0.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="4.7.1" />
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,11 @@
using VirtualPrinter.Agent.Core;
namespace VirtualPrinter.ProgressInfo.Core
{
public interface IProgressInfo
{
void Progress(IJob job, uint val);
void Initialize(IJob job);
void Finish(IJob job);
}
}

View file

@ -0,0 +1,6 @@
namespace VirtualPrinter.ProgressInfo.Core.Message
{
public interface IFinal : IMessage
{
}
}

View file

@ -0,0 +1,6 @@
namespace VirtualPrinter.ProgressInfo.Core.Message
{
public interface IMessage
{
}
}

View file

@ -0,0 +1,16 @@
using JetBrains.Annotations;
namespace VirtualPrinter.ProgressInfo.Core.Message
{
public interface IMessageFactory
{
[NotNull]
Message CreateStart();
[NotNull]
Message CreateStep(uint val);
[NotNull]
Message CreateFinal();
}
}

View file

@ -0,0 +1,6 @@
namespace VirtualPrinter.ProgressInfo.Core.Message
{
public interface IStart : IMessage
{
}
}

View file

@ -0,0 +1,7 @@
namespace VirtualPrinter.ProgressInfo.Core.Message
{
public interface IStep : IMessage
{
uint Value { get; set; }
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace VirtualPrinter.ProgressInfo.Core.Message
{
[Serializable]
public class Message
{
public Message(MessageType type, uint val)
{
Type = type;
Value = val;
}
public MessageType Type { get; }
public uint Value { get; }
}
}

View file

@ -0,0 +1,11 @@
namespace VirtualPrinter.ProgressInfo.Core.Message
{
public enum MessageType : uint
{
None,
Initialize,
Finalize,
Step,
Close
}
}

View file

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("VirtualPrinter.ProgressInfo.Core")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("24d28558-c825-43e6-85d2-7c59f4a97698")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
// indem Sie "*" wie unten gezeigt eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Some files were not shown because too many files have changed in this diff Show more