PDF-Druck über Windows-Dienst mit C #

8

Ich verwende diesen Code, um eine PDF-Datei auf einem lokalen Drucker mit C # innerhalb eines Windows-Dienstes zu drucken.

Process process = new Process();
PrinterSettings printerSettings = new PrinterSettings();

if (!string.IsNullOrWhiteSpace(basePrint))
   printerSettings.PrinterName = basePrint;

process.StartInfo.FileName = fileName;
process.StartInfo.Verb = "printto";
process.StartInfo.Arguments = "\"" + printerSettings.PrinterName + "\"";
process.Start();
process.WaitForInputIdle();

Alles funktioniert einwandfrei, wenn ich einen Benutzer so einstelle, dass er den Windows-Dienst ausführt.

Immer wenn ich diesen Code unter dem LocalSystem-Berechtigungsnachweis ausführe, erhalte ich die Fehlermeldung "Es ist keine Anwendung mit diesem Vorgang verknüpft", die normalerweise anzeigt, dass ich kein Programm habe, das für einen Druckvorgang einer Datei mit der Erweiterung .pdf bereit ist .

Mein Problem ist, dass ich das Programm (Foxit Reader) habe, um diesen Vorgang zu verarbeiten. Dies wird durch die Tatsache bestätigt, dass dieser Code mit einem bestimmten Benutzer funktioniert, der für den Dienst festgelegt wurde, und dass ich Dateien an den Drucker senden kann, indem ich mit der rechten Maustaste darauf klicke und Wählen Sie die Druckoption.

Kann ich etwas ändern, um innerhalb eines Dienstes ohne einen bestimmten Benutzer auf einem lokalen Drucker drucken zu können?

Caio Sant'Anna
quelle
Was ist das für ein Drucker? Lokal installiert oder ein gemeinsam genutzter Netzwerkdrucker über \\ Computername \ Druckername?
Sa.he
Es handelt sich um einen lokal installierten Epson EcoTank L375 ( epson.com.jm/Support/Printers/All-In-Ones/L-Series/… ).
Caio Sant'Anna

Antworten:

4

Am Ende habe ich pdfium verwendet, um die Arbeit zu erledigen. Mit diesem Code wird die PDF-Datei auch dann korrekt an den Drucker gesendet, wenn der Windows-Dienst unter dem LocalService-Benutzer ausgeführt wird.

PrinterSettings printerSettings = new PrinterSettings()
{
    PrinterName = printerName,
    Copies = 1
};

PageSettings pageSettings = new PageSettings(printerSettings)
{
    Margins = new Margins(0, 0, 0, 0)
};

foreach (PaperSize paperSize in printerSettings.PaperSizes)
{
    if (paperSize.PaperName == "A4")
    {
        pageSettings.PaperSize = paperSize;
        break;
    }
}

using (PdfDocument pdfDocument = PdfDocument.Load(filePath))
{
    using (PrintDocument printDocument = pdfDocument.CreatePrintDocument())
    {
        printDocument.PrinterSettings = printerSettings;
        printDocument.DefaultPageSettings = pageSettings;
        printDocument.PrintController = (PrintController) new     StandardPrintController();
        printDocument.Print();
    }
}

Danke für die Antworten Jungs.

Caio Sant'Anna
quelle
0

Das Problem kann sein, dass das SYSTEM-Konto (LocalSystem) nur über eingeschränkte Benutzeroberflächenfunktionen verfügt und möglicherweise Shell oder Shell-Erweiterungen entfernt oder deaktiviert werden. Und Verben sind eine Fähigkeit des Shell-Subsystems, insbesondere des Explorers.

Sie können das Programm manuell aufrufen, um festzustellen, ob dies der Fall ist oder ob es sich um ein Sicherheitsproblem oder einen Mangel an Benutzerprofildetails handelt.

Dazu müssen Sie in der Registrierung graben und Sie werden feststellen, dass viele Shell-Ausführungserweiterungsverben eine Befehlszeile haben.

Suchen Sie beispielsweise den Befehl HKEY_CLASSES_ROOT.pdf \ shell \ printto \ und verwenden Sie diesen Befehl.

Sie können auch überprüfen, ob das SYSTEM-Konto Zugriff auf diesen und den übergeordneten Schlüssel hat. (Selten der Fall, aber eine Überprüfung wert)

Bahram Ardalan
quelle
0

Sie können möglicherweise Ihren Arbeitscode ausführen, aber ein aktuell aktives Sitzungsbenutzertoken verwenden (aber ohne aktive Sitzung sollte dies nicht funktionieren).

Der Kürze halber wird dieser Code nicht kompiliert: Sie müssen zuerst P / Invoke anpassen und hinzufügen .

Sie müssen die aktive Sitzungs-ID finden. Verwenden Sie für eine lokal geöffnete Sitzung Folgendes:

    [DllImport("wtsapi32.dll", SetLastError = true)]
    public static extern int WTSEnumerateSessions(
        IntPtr hServer,
        int Reserved,
        int Version,
        ref IntPtr ppSessionInfo,
        ref int pCount);

Suchen Sie dann nach einer geöffneten Sitzungs-ID:

        var typeSessionInfo = typeof(WTSApi32.WTSSessionInfo);
        var sizeSessionInfo = Marshal.SizeOf(typeSessionInfo);
        var current = handleSessionInfo;
        for (var i = 0; i < sessionCount; i++)
        {
            var sessionInfo = (WTSApi32.WTSSessionInfo)Marshal.PtrToStructure(current, typeSessionInfo);
            current += sizeSessionInfo;
            if (sessionInfo.State == WTSApi32.WTSConnectStateClass.WTSActive)
                return sessionInfo.SessionID;
        }

Wenn nicht gefunden, suchen Sie eine RDP-Sitzung mit:

    [DllImport("kernel32.dll")]
    public static extern uint WTSGetActiveConsoleSessionId();

Damit bekommst du einen Token

    private static IntPtr GetUserImpersonatedToken(uint activeSessionId)
    {
        if (!WTSApi32.WTSQueryUserToken(activeSessionId, out var handleImpersonationToken))
            Win32Helper.RaiseInvalidOperation("WTSQueryUserToken");

        try
        {
            return DuplicateToken(handleImpersonationToken, AdvApi32.TokenType.TokenPrimary);
        }
        finally
        {
            Kernel32.CloseHandle(handleImpersonationToken);
        }
    }

Damit können Sie in einer geöffneten Benutzersitzung eine Exe vom lokalen Systemdienst ausführen.

    public static void ExecuteAsUserFromService(string appExeFullPath, uint activeSessionId, bool isVisible = false, string cmdLine = null, string workDir = null)
    {
        var tokenUser = GetUserImpersonatedToken(activeSessionId);
        try
        {
            if (!AdvApi32.SetTokenInformation(tokenUser, AdvApi32.TokenInformationClass.TokenSessionId, ref activeSessionId, sizeof(UInt32)))
                Win32Helper.RaiseInvalidOperation("SetTokenInformation");

            ExecuteAsUser(tokenUser, appExeFullPath, isVisible, cmdLine, workDir);
        }
        finally
        {
            Kernel32.CloseHandle(tokenUser);
        }
    }

Überprüfen Sie nun, ob Sie Ihren Code anpassen können CreateProcessAsUSer(...)

    private static void ExecuteAsUser(IntPtr token, string appExeFullPath, bool isVisible, string cmdLine, string workDir)
    {
        PrepareExecute(appExeFullPath, isVisible, ref workDir, out var creationFlags, out var startInfo, out var procInfo);
        try
        {
            startInfo.lpDesktop = "WinSta0\\Default";
            var processAttributes = new AdvApi32.SecurityAttributes
            {
                lpSecurityDescriptor = IntPtr.Zero
            };
            var threadAttributes = new AdvApi32.SecurityAttributes
            {
                lpSecurityDescriptor = IntPtr.Zero
            };
            if (!AdvApi32.CreateProcessAsUser(token,
                appExeFullPath, // Application Name
                cmdLine, // Command Line
                ref processAttributes,
                ref threadAttributes,
                true,
                creationFlags,
                IntPtr.Zero,
                workDir, // Working directory
                ref startInfo,
                out procInfo))
            {
                throw Win32Helper.RaiseInvalidOperation("CreateProcessAsUser");
            }
        }
        finally
        {
            Kernel32.CloseHandle(procInfo.hThread);
            Kernel32.CloseHandle(procInfo.hProcess);
        }
    }

In der Hoffnung, dass dieser Code Ihnen oder jemand anderem dient!

Dmo
quelle
0

Könnte es sein, dass sich die PDF-Anwendung nicht in der systemweiten PATH-Variablen befindet, sondern nur unter Ihrem spezifischen Benutzer?

Ich denke, Ihr Problem tritt auf, weil der Benutzer des "lokalen Systems" keine richtige Anwendung findet, sodass Sie sie für ihn registrieren müssten. Da Sie bereits eine andere Antwort akzeptiert haben, werde ich nicht mehr Zeit darauf verwenden, aber wenn es weitere Fragen dazu gibt, fragen Sie bitte.

Tillhoff
quelle