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

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());
}
}