I have windows service where I send PDF document for printing. The document is formed correctly and normally opens in Adobe PDF. However, if I send it to print, then it is displayed incorrectly (see attachments). At the same time, it is also normal in the print queue (spool). What could be the problem ? My code is below
namespace PrintTicketWcfModule
{
class Print
{
public bool PrintData(byte[] data)
{
return RawPrinterHelper.SendFileToPrinter(data);
}
}
}
namespace PrintPdfFile
{
public class RawPrinterHelper
{
// Structure and API declarions:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private class DOCINFOA
{
[MarshalAs(UnmanagedType.LPStr)]
public string pDocName;
[MarshalAs(UnmanagedType.LPStr)]
public string pOutputFile;
[MarshalAs(UnmanagedType.LPStr)]
public string pDataType;
}
#region dll Wrappers
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
[DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
[DllImport("winspool.Drv", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GetDefaultPrinter(StringBuilder pszBuffer, ref int size);
#endregion dll Wrappers
#region Methods
/// <summary>
/// This function gets the pdf file name.
/// This function opens the pdf file, gets all its bytes & send them to print.
/// </summary>
/// <param name="szPrinterName">Printer Name</param>
/// <param name="szFileName">Pdf File Name</param>
/// <returns>true on success, false on failure</returns>
public static bool SendFileToPrinter(byte [] bytes)
{
try
{
#region Get Connected Printer Name
PrintDocument pd = new PrintDocument();
pd.DefaultPageSettings.PrinterResolution.Kind = PrinterResolutionKind.Medium;
PaperSize paperSize = pd.PrinterSettings.PaperSizes.OfType<PaperSize>().FirstOrDefault(x=>x.Kind == PaperKind.A4);
if (paperSize != null)
pd.DefaultPageSettings.PaperSize = paperSize;
StringBuilder dp = new StringBuilder(256);
int size = dp.Capacity;
if (GetDefaultPrinter(dp, ref size))
{
Logger.Log.Info("GetDefaultPrnt");
pd.PrinterSettings.PrinterName = dp.ToString().Trim();
}
else
{
pd.PrinterSettings.PrinterName = ConfigurationManager.AppSettings["printerName"];
}
#endregion Get Connected Printer Name
Logger.Log.Info("DafaultPrtn = " + pd.PrinterSettings.PrinterName);
bool success = false;
// Unmanaged pointer.
IntPtr ptrUnmanagedBytes = new IntPtr(0);
int nLength = bytes.Length;
Logger.Log.Info("ByteLenght = " + nLength);
ptrUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
// Copy the managed byte array into the unmanaged array.
Marshal.Copy(bytes, 0, ptrUnmanagedBytes, nLength);
// Send the unmanaged bytes to the printer.
Logger.Log.Info("SendTaskToPrnt");
success = SendBytesToPrinter(pd.PrinterSettings.PrinterName, ptrUnmanagedBytes, nLength);
// Free the unmanaged memory that you allocated earlier.
Marshal.FreeCoTaskMem(ptrUnmanagedBytes);
return success;
}
catch (Exception ex)
{
Logger.Log.Error(ex);
return false;
}
}
/// <summary>
/// This function gets the printer name and an unmanaged array of bytes, the function sends those bytes to the print queue.
/// </summary>
/// <param name="szPrinterName">Printer Name</param>
/// <param name="pBytes">No. of bytes in the pdf file</param>
/// <param name="dwCount">Word count</param>
/// <returns>True on success, false on failure</returns>
private static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
{
try
{
Logger.Log.Info("Start Print");
Int32 dwError = 0, dwWritten = 0;
IntPtr hPrinter = new IntPtr(0);
DOCINFOA di = new DOCINFOA();
bool success = false; // Assume failure unless you specifically succeed.
di.pDocName = "PDF Document";
di.pDataType = "RAW";
// Open the printer.
if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
{
Logger.Log.Info("OpenPrinter");
// Start a document.
if (StartDocPrinter(hPrinter, 1, di))
{
Logger.Log.Info("StartDocPrinter");
// Start a page.
if (StartPagePrinter(hPrinter))
{
Logger.Log.Info("StartPagePrinter");
// Write the bytes.
success = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
EndPagePrinter(hPrinter);
}
EndDocPrinter(hPrinter);
}
ClosePrinter(hPrinter);
}
// If print did not succeed, GetLastError may give more information about the failure.
if (success == false)
{
dwError = Marshal.GetLastWin32Error();
}
return success;
}
catch (Exception ex)
{
Logger.Log.Error(ex);
throw new Exception(ex.Message);
}
}
#endregion Methods
}
}
Related
I am using VS2010. .NET Framework 3.5.
Pre-conditions:
Adobe is not installed on the system.
3rd party dlls not to be used
Windows dlls should be used.
So far, I am able to print a pdf file, but not able to change printer setting at run time.
The basic thought for printing pdf file is that, I read bytes of pdf file and directly send them to printer. Here it prints the pdf file without any problem.
But the file is printing side by side on a page, but I want it to print only one side of the page not the both sides. For that I am trying to change printer setting at run time.
I have tested my code on two different printers. On one printer it is printing side by side of the same page and on another printer on one page only. That means, printer settings is not getting changed.
Code is as shown below. Used 4 classes:
//Class 1: This class basically tries to change the printer setting at run time
public class PrinterSettingForPdf
{
#region "Private Variables"
private IntPtr hPrinter = new System.IntPtr();
private PRINTER_DEFAULTS PrinterValues = new PRINTER_DEFAULTS();
private PRINTER_INFO_2 pinfo = new PRINTER_INFO_2();
//private PRINTER_INFO_9 pinfo9 = new PRINTER_INFO_9();
private DEVMODE dm;
private IntPtr ptrDM;
private IntPtr ptrPrinterInfo;
private int sizeOfDevMode = 0;
private int lastError;
private int nBytesNeeded;
private long nRet;
private int intError;
private System.Int32 nJunk;
private IntPtr yDevModeData;
#endregion
#region "Constants"
private const int DM_DUPLEX = 0x1000;
private const int DM_IN_BUFFER = 8;
private const int DM_OUT_BUFFER = 2;
private const int PRINTER_ACCESS_ADMINISTER = 0x4;
private const int PRINTER_ACCESS_USE = 0x8;
private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
private const int PRINTER_ALL_ACCESS =
(STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER
| PRINTER_ACCESS_USE);
#endregion
#region "Win API Def"
[DllImport("kernel32.dll", EntryPoint = "GetLastError", SetLastError = false,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern Int32 GetLastError();
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesA", SetLastError = true,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter,
[MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg,
IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);
[DllImport("winspool.Drv", EntryPoint = "GetPrinterA", SetLastError = true,
CharSet = CharSet.Ansi, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
private static extern bool GetPrinter(IntPtr hPrinter, Int32 dwLevel,
IntPtr pPrinter, Int32 dwBuf, out Int32 dwNeeded);
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA",
SetLastError = true, CharSet = CharSet.Ansi,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool
OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter,
out IntPtr hPrinter, ref PRINTER_DEFAULTS pd);
[DllImport("winspool.drv", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr
pPrinter, int Command);
#endregion
#region "Data structure"
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_DEFAULTS
{
public int pDatatype;
public int pDevMode;
public int DesiredAccess;
}
[StructLayout(LayoutKind.Sequential)]
private struct PRINTER_INFO_2
{
[MarshalAs(UnmanagedType.LPStr)]
public string pServerName;
[MarshalAs(UnmanagedType.LPStr)]
public string pPrinterName;
[MarshalAs(UnmanagedType.LPStr)]
public string pShareName;
[MarshalAs(UnmanagedType.LPStr)]
public string pPortName;
[MarshalAs(UnmanagedType.LPStr)]
public string pDriverName;
[MarshalAs(UnmanagedType.LPStr)]
public string pComment;
[MarshalAs(UnmanagedType.LPStr)]
public string pLocation;
public IntPtr pDevMode;
[MarshalAs(UnmanagedType.LPStr)]
public string pSepFile;
[MarshalAs(UnmanagedType.LPStr)]
public string pPrintProcessor;
[MarshalAs(UnmanagedType.LPStr)]
public string pDatatype;
[MarshalAs(UnmanagedType.LPStr)]
public string pParameters;
public IntPtr pSecurityDescriptor;
public Int32 Attributes;
public Int32 Priority;
public Int32 DefaultPriority;
public Int32 StartTime;
public Int32 UntilTime;
public Int32 Status;
public Int32 cJobs;
public Int32 AveragePPM;
}
/// <summary>
/// The PRINTER_INFO_9 structure specifies the per-user default printer settings.
/// </summary>
/// <seealso href="http://msdn.microsoft.com/en-us/library/windows/desktop/dd162852(v=vs.85).aspx"/>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal class PRINTER_INFO_9
{
/// <summary>
/// A pointer to a DEVMODE structure that defines the per-user
/// default printer data such as the paper orientation and the resolution.
/// The DEVMODE is stored in the user's registry.
/// </summary>
public IntPtr pDevMode;
}
private const short CCDEVICENAME = 32;
private const short CCFORMNAME = 32;
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public short dmOrientation;
public short dmPaperSize;
public short dmPaperLength;
public short dmPaperWidth;
public short dmScale;
public short dmCopies;
public short dmDefaultSource;
public short dmPrintQuality;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
public string dmFormName;
public short dmUnusedPadding;
public short dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
}
#endregion
#region "Function to change printer settings"
public bool ChangePrintersetting(string sPrinterName, PrinterData pd, int numbeOfCopies, bool preserverOldSettings)
{
if (((int)pd.Duplex < 1) || ((int)pd.Duplex > 3))
{
throw new ArgumentOutOfRangeException("nDuplexSetting", "nDup lexSetting is incorrect.");
}
else
{
const int PRINTER_ACCESS_ADMINISTER = 0x4;
const int PRINTER_ACCESS_USE = 0x8;
const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);
//const int READ_CONTROL = 0x20000;
//const int PRINTER_NORMAL_ACCESS = 131080; // (READ_CONTROL | PRINTER_ACCESS_USE);
PrinterValues.pDatatype = 0;
PrinterValues.pDevMode = 0;
PrinterValues.DesiredAccess = PRINTER_ALL_ACCESS;
nRet = Convert.ToInt32(OpenPrinter(sPrinterName, out hPrinter, ref PrinterValues));
if (nRet == 0)
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}
GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out nBytesNeeded);
if (nBytesNeeded <= 0)
{
return false;
}
// Allocate enough space for PRINTER_INFO_2...
//ptrPrinterInfo = Marshal.AllocCoTaskMem(nBytesNeeded);
ptrPrinterInfo = Marshal.AllocHGlobal(nBytesNeeded);
// The second GetPrinter fills in all the current settings, so all you
// need to do is modify what you're interested in...
nRet = Convert.ToInt32(GetPrinter(hPrinter, 2, ptrPrinterInfo, nBytesNeeded, out nJunk));
if (nRet == 0)
{
lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(Marshal.GetLastWin32Error());
}
pinfo = (PRINTER_INFO_2)Marshal.PtrToStructure(ptrPrinterInfo, typeof(PRINTER_INFO_2));
IntPtr Temp = new IntPtr();
int i1 = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, IntPtr.Zero, ref Temp, 0);
//IntPtr yDevModeData = Marshal.AllocCoTaskMem(i1);
IntPtr yDevModeData = Marshal.AllocHGlobal(i1);
i1 = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, yDevModeData, ref Temp, 2);
dm = (DEVMODE)Marshal.PtrToStructure(yDevModeData, typeof(DEVMODE));
dm.dmDefaultSource = (short)pd.Source;
dm.dmOrientation = (short)pd.Orientation;
dm.dmPaperSize = (short)pd.Size;
dm.dmCopies = (short)1;
dm.dmDuplex = (short)pd.Duplex;
Marshal.StructureToPtr(dm, yDevModeData, true);
//nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, yDevModeData
// , ref yDevModeData, (DM_IN_BUFFER | DM_OUT_BUFFER));
if ((nRet == 0) || (hPrinter == IntPtr.Zero))
{
lastError = Marshal.GetLastWin32Error();
//string myErrMsg = GetErrorMessage(lastError);
throw new Win32Exception(Marshal.GetLastWin32Error());
}
if (pinfo.pDevMode == IntPtr.Zero)
{
// If GetPrinter didn't fill in the DEVMODE, try to get it by calling
// DocumentProperties...
IntPtr ptrZero = IntPtr.Zero;
//get the size of the devmode structure
sizeOfDevMode = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, ptrZero, ref ptrZero, 0);
if (nRet <= 0)
{
return false;
}
ptrDM = Marshal.AllocCoTaskMem(sizeOfDevMode);
int i;
i = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, ptrDM,
ref ptrZero, DM_OUT_BUFFER);
if ((i < 0) || (ptrDM == IntPtr.Zero))
{
//Cannot get the DEVMODE structure.
return false;
}
pinfo.pDevMode = ptrDM;
}
if (!Convert.ToBoolean(dm.dmFields & DM_DUPLEX))
{
//You cannot modify the duplex flag for this printer
//because it does not support duplex or the driver does not support setting
//it from the Windows API.
//return false;
}
pinfo.pDevMode = yDevModeData;
pinfo.pSecurityDescriptor = IntPtr.Zero;
/*update driver dependent part of the DEVMODE
i1 = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, yDevModeData
, ref pinfo.pDevMode, (DM_IN_BUFFER | DM_OUT_BUFFER));*/
if (i1 < 0)
{
//Unable to set duplex setting to this printer.
return false;
}
Marshal.StructureToPtr(pinfo, ptrPrinterInfo, true);
lastError = Marshal.GetLastWin32Error();
nRet = Convert.ToInt16(SetPrinter(hPrinter, 2, ptrPrinterInfo, 0));
if (nRet == 0)
{
//Unable to set shared printer settings.
lastError = Marshal.GetLastWin32Error();
//string myErrMsg = GetErrorMessage(lastError);
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return Convert.ToBoolean(nRet);
}
}
#endregion
}
//Class 2: Supporting Printer setting class
public class PrinterData
{
public int Duplex { get; set; }
public int Source { get; set; }
public int Orientation { get; set; }
public int Size { get; set; }
}
//Class 3: This class uses Windows dlls methods to print pdf contents
public class PrintPdf
{
// Structure and API declarions:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class DOCINFOA
{
[MarshalAs(UnmanagedType.LPStr)]
public string pDocName;
[MarshalAs(UnmanagedType.LPStr)]
public string pOutputFile;
[MarshalAs(UnmanagedType.LPStr)]
public string pDataType;
}
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
[DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
// SendBytesToPrinter()
// When the function is given a printer name and an unmanaged array
// of bytes, the function sends those bytes to the print queue.
// Returns true on success, false on failure.
public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
{
Int32 dwError = 0, dwWritten = 0;
IntPtr hPrinter = new IntPtr(0);
DOCINFOA di = new DOCINFOA();
bool bSuccess = false; // Assume failure unless you specifically succeed.
di.pDocName = "My C#.NET RAW Document";
di.pDataType = "RAW";
// Open the printer.
//if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
PrinterSettingForPdf objPS = new PrinterSettingForPdf();
PrinterData objPD = new PrinterData();
objPD.Duplex = 1;
objPD.Orientation = 1;
//objPD.Size = 1;
//objPD.Source = 1;
if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
{
// Start a document.
if (StartDocPrinter(hPrinter, 1, di))
{
// Start a page.
if (StartPagePrinter(hPrinter))
{
objPS.ChangePrintersetting(szPrinterName, objPD, 1, false);
// Write your bytes.
bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
EndPagePrinter(hPrinter);
}
EndDocPrinter(hPrinter);
}
if (hPrinter != IntPtr.Zero)
ClosePrinter(hPrinter);
// ClosePrinter(hPrinter);
}
// If you did not succeed, GetLastError may give more information
// about why not.
if (bSuccess == false)
{
dwError = Marshal.GetLastWin32Error();
}
return bSuccess;
}
public static bool SendFileToPrinter(string szPrinterName, string szFileName)
{
// Open the file.
FileStream fs = new FileStream(szFileName, FileMode.Open);
// Create a BinaryReader on the file.
BinaryReader br = new BinaryReader(fs);
// Dim an array of bytes big enough to hold the file's contents.
Byte[] bytes = new Byte[fs.Length];
bool bSuccess = false;
// Your unmanaged pointer.
IntPtr pUnmanagedBytes = new IntPtr(0);
int nLength;
nLength = Convert.ToInt32(fs.Length);
// Read the contents of the file into the array.
bytes = br.ReadBytes(nLength);
// Allocate some unmanaged memory for those bytes.
pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
// Copy the managed byte array into the unmanaged array.
Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
// Send the unmanaged bytes to the printer.
bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);
// Free the unmanaged memory that you allocated earlier.
Marshal.FreeCoTaskMem(pUnmanagedBytes);
return bSuccess;
}
public static bool SendStringToPrinter(string szPrinterName, string szString)
{
IntPtr pBytes;
Int32 dwCount;
// How many characters are in the string?
dwCount = szString.Length;
// Assume that the printer is expecting ANSI text, and then convert
// the string to ANSI text.
pBytes = Marshal.StringToCoTaskMemAnsi(szString);
// Send the converted ANSI string to the printer.
SendBytesToPrinter(szPrinterName, pBytes, dwCount);
Marshal.FreeCoTaskMem(pBytes);
return true;
}
}
//Class 4: Class that handles printing pdf
public class PrintingPDFData
{
public void SilentPrintPdf(string PdfFileName, string PrinterName)
{
try
{
PrintPdf.SendFileToPrinter(PrinterName, PdfFileName);
}
catch (Exception ex)
{
}
}
}
Please let me know, if any further clarification is needed.
Thanks in advance.
I believe the device mode settings are unavailable when printing a high-level document using the Win32 winspool API (if I am interpreting the documentation correctly). According to MSDN:
The DEVMODE settings defined in the PRINTER_DEFAULTS structure of the pDefault parameter are not used when the value of the pDatatype member of the DOC_INFO_1 structure that was passed in the pDocInfo parameter of the StartDocPrinter call is "RAW". When a high-level document (such as an Adobe PDF or Microsoft Word file) or other printer data (such PCL, PS, or HPGL) is sent directly to a printer with pDatatype set to "RAW", the document must fully describe the DEVMODE-style print job settings in the language understood by the hardware.
https://msdn.microsoft.com/en-us/library/windows/desktop/dd162751%28v=vs.85%29.aspx
You are using "RAW" to send your PDF bytes to the printer, which is the scenario described here.
How to use ESC/POS command with C#? I need format like this
but I cannot achieve this format.
I tried some codes ,but no use.
using (var ms = new MemoryStream())
using (var bw = new BinaryWriter(ms))
{
// Reset the printer bws (NV images are not cleared)
bw.Write(AsciiControlChars.Escape);
bw.Write('#');
bw.Write(AsciiControlChars.Newline);
bw.Write(AsciiControlChars.Escape);
bw.Write("_______________________________________________");
bw.Write(AsciiControlChars.Newline);
bw.Write("Service Price Qty Total");
bw.Write("------------------------------------------------");
bw.Write(AsciiControlChars.GroupSeparator);
bw.Write('V');
bw.Write((byte)66);
bw.Write((byte)3);
bw.Flush();
// Send the converted ANSI string to the printer.
}
You can check this link. I now using that and It's Works!!
http://www.codeproject.com/Tips/704989/Print-Direct-To-Windows-Printer-EPOS-Receipt
Use with careful, you must know ESC/POS command for thermal printer. If I'm not mistaken, the manual command must exit on the CD that came with the printer.
Kind Regards
Bonus:
http://www.developerfusion.com/tools/convert/vb-to-csharp/
I know that this is not exactly an answer to the question of how to use escape codes, but it would be much better to create a PDF file (P for Portable). You'll have a better chance to have it rendered exactly as intended on most printers.
You can generate a PDF with PDFsharp. It's open source and free and it's quite simple to use.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace Apteka.Printer
{
public class RawPrinterHelper
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class DOCINFOA
{
[MarshalAs(UnmanagedType.LPStr)]
public string pDocName;
[MarshalAs(UnmanagedType.LPStr)]
public string pOutputFile;
[MarshalAs(UnmanagedType.LPStr)]
public string pDataType;
}
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, ref IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In()][MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
[DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, ref Int32 dwWritten);
private IntPtr hPrinter = new IntPtr(0);
private DOCINFOA di = new DOCINFOA();
private bool PrinterOpen = false;
public bool PrinterIsOpen
{
get
{
return PrinterOpen;
}
}
public bool OpenPrint(string szPrinterName)
{
if (PrinterOpen == false)
{
di.pDocName = ".NET RAW Document";
di.pDataType = "RAW";
if (OpenPrinter(szPrinterName.Normalize(), ref hPrinter, IntPtr.Zero))
{
if (StartDocPrinter(hPrinter, 1, di))
{
if (StartPagePrinter(hPrinter))
PrinterOpen = true;
}
}
}
return PrinterOpen;
}
public void ClosePrint()
{
if (PrinterOpen)
{
EndPagePrinter(hPrinter);
EndDocPrinter(hPrinter);
ClosePrinter(hPrinter);
PrinterOpen = false;
}
}
public bool SendStringToPrinter(string szPrinterName, string szString)
{
if (PrinterOpen)
{
IntPtr pBytes;
Int32 dwCount;
Int32 dwWritten = 0;
dwCount = szString.Length;
pBytes = Marshal.StringToCoTaskMemAnsi(szString);
var res= WritePrinter(hPrinter, pBytes, dwCount, ref dwWritten);
Marshal.FreeCoTaskMem(pBytes);
return res;
}
else
return false;
}
}
}
I was working on thermal printer and I came across to ESC/POS commands like you do.After Googling I found ThermalDotNet very useful for me.This class also supports image printing as well as letter printing.
I hope this helps ;)
How can I get strings from windows dlls like mssvp.dll and themeui.dll using the index?
In the registry or theme files there are some strings (like DisplayName in themes) that are pointing to a dll and an index number instead of the real texts. For example I have:
DisplayName=#%SystemRoot%\System32\themeui.dll,-2106 in windows theme file. So how can I retrieve the real strings from those dlls using C# and .Net 4.0?
You need to use P/Invoke:
/// <summary>Returns a string resource from a DLL.</summary>
/// <param name="DLLHandle">The handle of the DLL (from LoadLibrary()).</param>
/// <param name="ResID">The resource ID.</param>
/// <returns>The name from the DLL.</returns>
static string GetStringResource(IntPtr handle, uint resourceId) {
StringBuilder buffer = new StringBuilder(8192); //Buffer for output from LoadString()
int length = NativeMethods.LoadString(handle, resourceId, buffer, buffer.Capacity);
return buffer.ToString(0, length); //Return the part of the buffer that was used.
}
static class NativeMethods {
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern int LoadString(IntPtr hInstance, uint wID, StringBuilder lpBuffer, int nBufferMax);
[DllImport("kernel32.dll")]
public static extern int FreeLibrary(IntPtr hLibModule);
}
I am using the methods below to embed any number of files inside an executable
private void EmbedFiles(IEnumerable<string> files)
{
const string exePath = #"C:\SimpleApp.exe";
foreach (var file in files)
WriteFileToResource(exePath, file);
}
[DllImport("kernel32.dll", EntryPoint = "BeginUpdateResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr BeginUpdateResource(string pFileName, bool bDeleteExistingResources);
[DllImport("kernel32.dll", EntryPoint = "UpdateResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
internal static extern bool UpdateResource(IntPtr hUpdate, string lpType, string lpName, short wLanguage, byte[] lpData, int cbData);
[DllImport("kernel32.dll", EntryPoint = "EndUpdateResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
internal static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);
internal static void WriteFileToResource(string path, string file)
{
var resourceName = Path.GetFileName(file);
using (var binaryStream = new FileStream(file, FileMode.Open, FileAccess.Read))
{
byte[] data = null;
var resourceLanguage = MakeLanguageID();
try
{
data = new byte[binaryStream.Length];
binaryStream.Read(data, 0, (int)binaryStream.Length);
}
catch (Exception ex)
{
throw new Exception(string.Format("Error reading {0}: {1}", file, ex.Message), ex);
}
var h = BeginUpdateResource(path, false);
Write(h, "File", resourceName, resourceLanguage, data);
}
}
internal static void Write(
IntPtr h,
string resourceType,
string resourceName,
short resourceLanguage,
byte[] buffer)
{
try
{
if (UpdateResource(h, resourceType, resourceName, resourceLanguage, buffer, buffer.Length))
EndUpdateResource(h, false);
else
throw new Win32Exception(Marshal.GetLastWin32Error());
}
catch (Exception ex)
{
throw new Exception(string.Format("Error writing {0}: {1}", resourceName, ex.Message), ex);
}
}
static short MakeLanguageID()
{
return (short)CultureInfo.CurrentUICulture.LCID;
}
In the code below what I am trying to do is to extract the embedded files from the target exe in order to save them in a selected directory but I am not able to read the files.
var assembly = Assembly.GetExecutingAssembly();
var names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
foreach (string filename in names)
{
var stream = assembly.GetManifestResourceStream(filename);
var rawFile = new byte[stream.Length];
stream.Read(rawFile, 0, (int)stream.Length);
using (var fs = new FileStream(filename, FileMode.Create))
{
fs.Write(rawFile, 0, (int)stream.Length);
}
}
Any advice or help would be much appreciated.
It seems that GetManifestResourceStream() shows only .Net resources that correspond to .resx files.
I got it to work using the other functions in the UpdateResource family.
ResourcesManager.GetResourceFromExecutable(assembly.ManifestModule.FullyQualifiedName, "PACKAGES.TXT", ResourcesManager.RT_RCDATA);
public static class ResourcesManager
{
public const uint RT_RCDATA = 0x10;
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll")]
public static extern IntPtr FindResource(IntPtr hModule, string lpName, uint lpType);
// public static extern IntPtr FindResource(IntPtr hModule, int lpName, uint lpType);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);
[DllImport("kernel32.dll")]
public static extern IntPtr LockResource(IntPtr hResData);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);
public static string GetResourceFromExecutable(string lpFileName, string lpName, uint lpType)
{
IntPtr hModule = LoadLibrary(lpFileName);
if (hModule != IntPtr.Zero)
{
IntPtr hResource = FindResource(hModule, lpName, lpType);
if (hResource != IntPtr.Zero)
{
uint resSize = SizeofResource(hModule, hResource);
IntPtr resData = LoadResource(hModule, hResource);
if (resData != IntPtr.Zero)
{
byte[] uiBytes = new byte[resSize];
IntPtr ipMemorySource = LockResource(resData);
Marshal.Copy(ipMemorySource, uiBytes, 0, (int)resSize);
//....
}
}
}
}
}
}
I have windows service that must periodically print PDF document from server. My function is
private void PrintFormPdfData(byte[] formPdfData)
{
string tempFile;
tempFile = Path.GetTempFileName();
using (FileStream fs = new FileStream(tempFile, FileMode.Create))
{
fs.Write(formPdfData, 0, formPdfData.Length);
fs.Flush();
}
string pdfArguments = string.Format("/p /h\"{0}\"", tempFile);
string pdfPrinterLocation = #"C:\Program Files (x86)\Adobe\Reader 9.0\Reader\AcroRd32.exe";
ProcessStartInfo newProcess = new ProcessStartInfo(pdfPrinterLocation, pdfArguments);
newProcess.CreateNoWindow = true;
newProcess.RedirectStandardOutput = true;
newProcess.UseShellExecute = false;
newProcess.RedirectStandardError = true;
Process pdfProcess = new Process();
pdfProcess.StartInfo = newProcess;
pdfProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
pdfProcess.Start();
pdfProcess.WaitForExit();
}
When I implement this in Windows Application it works, but when I implement in Windows service it does not work.
Can you help me?
I solved problem with Session 0. I use this class:
public class ProcessStarter : IDisposable
{
#region Import Section
private static uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;
private static uint STANDARD_RIGHTS_READ = 0x00020000;
private static uint TOKEN_ASSIGN_PRIMARY = 0x0001;
private static uint TOKEN_DUPLICATE = 0x0002;
private static uint TOKEN_IMPERSONATE = 0x0004;
private static uint TOKEN_QUERY = 0x0008;
private static uint TOKEN_QUERY_SOURCE = 0x0010;
private static uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
private static uint TOKEN_ADJUST_GROUPS = 0x0040;
private static uint TOKEN_ADJUST_DEFAULT = 0x0080;
private static uint TOKEN_ADJUST_SESSIONID = 0x0100;
private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
private static uint TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID);
private const uint NORMAL_PRIORITY_CLASS = 0x0020;
private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const uint MAX_PATH = 260;
private const uint CREATE_NO_WINDOW = 0x08000000;
private const uint INFINITE = 0xFFFFFFFF;
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
public enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
public enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
public Int32 SessionID;
[MarshalAs(UnmanagedType.LPStr)]
public String pWinStationName;
public WTS_CONNECTSTATE_CLASS State;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern uint WTSGetActiveConsoleSessionId();
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool WTSQueryUserToken(int sessionId, out IntPtr tokenHandle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateTokenEx(IntPtr existingToken, uint desiredAccess, IntPtr tokenAttributes, SECURITY_IMPERSONATION_LEVEL impersonationLevel, TOKEN_TYPE tokenType, out IntPtr newToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool CreateProcessAsUser(IntPtr token, string applicationName, string commandLine, ref SECURITY_ATTRIBUTES processAttributes, ref SECURITY_ATTRIBUTES threadAttributes, bool inheritHandles, uint creationFlags, IntPtr environment, string currentDirectory, ref STARTUPINFO startupInfo, out PROCESS_INFORMATION processInformation);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetLastError();
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int WaitForSingleObject(IntPtr token, uint timeInterval);
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int WTSEnumerateSessions(System.IntPtr hServer, int Reserved, int Version, ref System.IntPtr ppSessionInfo, ref int pCount);
[DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("wtsapi32.dll", ExactSpelling = true, SetLastError = false)]
public static extern void WTSFreeMemory(IntPtr memory);
[DllImport("userenv.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="ProcessStarter"/> class.
/// </summary>
public ProcessStarter()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ProcessStarter"/> class.
/// </summary>
/// <param name="processName">Name of the process.</param>
/// <param name="fullExeName">Full name of the exe.</param>
public ProcessStarter(string processName, string fullExeName)
{
processName_ = processName;
processPath_ = fullExeName;
}
/// <summary>
/// Initializes a new instance of the <see cref="ProcessStarter"/> class.
/// </summary>
/// <param name="processName">Name of the process.</param>
/// <param name="fullExeName">Full name of the exe.</param>
/// <param name="arguments">The arguments.</param>
public ProcessStarter(string processName, string fullExeName, string arguments)
{
processName_ = processName;
processPath_ = fullExeName;
arguments_ = arguments;
}
/// <summary>
/// Gets the current user token.
/// </summary>
/// <returns></returns>
public static IntPtr GetCurrentUserToken()
{
IntPtr currentToken = IntPtr.Zero;
IntPtr primaryToken = IntPtr.Zero;
IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
int dwSessionId = 0;
IntPtr hUserToken = IntPtr.Zero;
IntPtr hTokenDup = IntPtr.Zero;
IntPtr pSessionInfo = IntPtr.Zero;
int dwCount = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref dwCount);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int32 current = (int)pSessionInfo;
for (int i = 0; i < dwCount; i++)
{
WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State)
{
dwSessionId = si.SessionID;
break;
}
current += dataSize;
}
WTSFreeMemory(pSessionInfo);
bool bRet = WTSQueryUserToken(dwSessionId, out currentToken);
if (bRet == false)
{
return IntPtr.Zero;
}
bRet = DuplicateTokenEx(currentToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken);
if (bRet == false)
{
return IntPtr.Zero;
}
return primaryToken;
}
/// <summary>
/// Runs this instance.
/// </summary>
public void Run()
{
IntPtr primaryToken = GetCurrentUserToken();
if (primaryToken == IntPtr.Zero)
{
return;
}
STARTUPINFO StartupInfo = new STARTUPINFO();
processInfo_ = new PROCESS_INFORMATION();
StartupInfo.cb = Marshal.SizeOf(StartupInfo);
SECURITY_ATTRIBUTES Security1 = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES Security2 = new SECURITY_ATTRIBUTES();
string command = "\"" + processPath_ + "\"";
if ((arguments_ != null) && (arguments_.Length != 0))
{
command += " " + arguments_;
}
IntPtr lpEnvironment = IntPtr.Zero;
bool resultEnv = CreateEnvironmentBlock(out lpEnvironment, primaryToken, false);
if (resultEnv != true)
{
int nError = GetLastError();
}
CreateProcessAsUser(primaryToken, null, command, ref Security1, ref Security2, false, CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, null, ref StartupInfo, out processInfo_);
DestroyEnvironmentBlock(lpEnvironment);
CloseHandle(primaryToken);
}
/// <summary>
/// Stops this instance.
/// </summary>
public void Stop()
{
Process[] processes = Process.GetProcesses();
foreach (Process current in processes)
{
if (current.ProcessName == processName_)
{
current.Kill();
}
}
}
/// <summary>
/// Waits for exit.
/// </summary>
/// <returns></returns>
public int WaitForExit()
{
WaitForSingleObject(processInfo_.hProcess, INFINITE);
int errorcode = GetLastError();
return errorcode;
}
#region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
}
#endregion
private string processPath_ = string.Empty;
private string processName_ = string.Empty;
private string arguments_ = string.Empty;
private PROCESS_INFORMATION processInfo_;
/// <summary>
/// Gets or sets the process path.
/// </summary>
/// <value>The process path.</value>
public string ProcessPath
{
get
{
return processPath_;
}
set
{
processPath_ = value;
}
}
/// <summary>
/// Gets or sets the name of the process.
/// </summary>
/// <value>The name of the process.</value>
public string ProcessName
{
get
{
return processName_;
}
set
{
processName_ = value;
}
}
/// <summary>
/// Gets or sets the arguments.
/// </summary>
/// <value>The arguments.</value>
public string Arguments
{
get
{
return arguments_;
}
set
{
arguments_ = value;
}
}
}
Now, my function look like:
private void PrintFormPdfData(byte[] formPdfData)
{
string tempFile;
tempFile = Path.GetTempFileName();
using (FileStream fs = new FileStream(tempFile, FileMode.Create))
{
fs.Write(formPdfData, 0, formPdfData.Length);
fs.Flush();
}
string pdfArguments =string.Format("/t /o {0} \"Printer name\"", tempFile);
string pdfPrinterLocation = #"C:\Program Files (x86)\Adobe\Reader 9.0\Reader\AcroRd32.exe";
try
{
ProcessStarter processStarter = new ProcessStarter("AcroRd32", pdfPrinterLocation, pdfArguments);
processStarter.Run();
processStarter.WaitForExit();
processStarter.Stop();
}
finally
{
File.Delete(tempFile);
}
}
Also, ServiceProcessInstaller must have `Account set to "LocalSystem". When I created service I set to "Local service" with this user it does not work. I did not try with "Network service" or "User".
I solved this problem with the registry edits found in this article: https://support.microsoft.com/en-us/kb/184291.
Printing from Windows services (and also IIS) uses the SYSTEM account as I understand it. This account does not have access to the printer but these registry edits gives that SYSTEM account printing privileges.
Here is a summary:
export 3 keys ("folders within regedit.exe") to .reg files:
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\Current Version\Devices
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\Current Version\PrinterPorts
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\Current Version\Windows
edit your 3 newly created reg files and change "HKEY_CURRENT_USER" to "HKEY_USERS\.DEFAULT"
run your 3 reg files by double clicking them in explorer (this adds them to the registry)
That's all it takes. This helped me to print pdf files to a printer from PHP in IIS 7. My php script would work just fine so long as the server happened to have the Administrator logged in when the script was run. Now no one needs to be logged in and the script still works :)
I hope this saves someone as much searching and tinkering as I put in to find it.
You didn't mention the OS, but I suspect you are running into the Session 0 isolation feature that was added in Windows Vista, Server 2008 and subsequent OS releases
Application classes affected by this feature include:
Services that create UI.
A service that tries to use window-message functions such as SendMessage and PostMessage to communicate with an application.
Applications creating globally named objects.